1 // Copyright (c) 2018-2020 Emil Dotchevski and Reverge Studios, Inc.
2 
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 // This is a simple program that demonstrates the use of LEAF to transport error
7 // objects between threads, using exception handling. See capture_in_result.cpp
8 // for the version that does not use exception handling.
9 
10 #include <boost/leaf/capture.hpp>
11 #include <boost/leaf/handle_errors.hpp>
12 #include <boost/leaf/exception.hpp>
13 #include <vector>
14 #include <string>
15 #include <future>
16 #include <iterator>
17 #include <iostream>
18 #include <algorithm>
19 
20 namespace leaf = boost::leaf;
21 
22 // Define several error types.
23 struct e_thread_id { std::thread::id value; };
24 struct e_failure_info1 { std::string value; };
25 struct e_failure_info2 { int value; };
26 
27 // A type that represents a successfully returned result from a task.
28 struct task_result { };
29 
30  // This is our task function. It produces objects of type task_result, but it may fail...
task()31 task_result task()
32 {
33     bool succeed = (rand()%4) != 0; //...at random.
34     if( succeed )
35         return { };
36     else
37         throw leaf::exception(
38             e_thread_id{std::this_thread::get_id()},
39             e_failure_info1{"info"},
40             e_failure_info2{42} );
41 };
42 
main()43 int main()
44 {
45     int const task_count = 42;
46 
47     // The error_handlers are used in this thread (see leaf::try_catch below). The
48     // arguments passed to individual lambdas are transported from the worker thread
49     // to the main thread automatically.
50     auto error_handlers = std::make_tuple(
51         []( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid )
52         {
53             std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl;
54         },
55         []( leaf::diagnostic_info const & unmatched )
56         {
57             std::cerr <<
58                 "Unknown failure detected" << std::endl <<
59                 "Cryptic diagnostic information follows" << std::endl <<
60                 unmatched;
61         } );
62 
63     // Container to collect the generated std::future objects.
64     std::vector<std::future<task_result>> fut;
65 
66     // Launch the tasks, but rather than launching the task function directly, we launch a
67     // wrapper function which calls leaf::capture, passing a context object that will hold
68     // the error objects reported from the task in case it throws. The error types the
69     // context is able to hold statically are automatically deduced from the type of the
70     // error_handlers tuple.
71     std::generate_n( std::back_inserter(fut), task_count,
72         [&]
73         {
74             return std::async(
75                 std::launch::async,
76                 [&]
77                 {
78                     return leaf::capture(leaf::make_shared_context(error_handlers), &task);
79                 } );
80         } );
81 
82     // Wait on the futures, get the task results, handle errors.
83     for( auto & f : fut )
84     {
85         f.wait();
86 
87         leaf::try_catch(
88             [&]
89             {
90                 task_result r = f.get();
91 
92                 // Success! Use r to access task_result.
93                 std::cout << "Success!" << std::endl;
94                 (void) r; // Presumably we'll somehow use the task_result.
95             },
96             error_handlers );
97     }
98 }
99