Skip to content

Commit

Permalink
dynamic_capture -> result<T>, a new way to hold and transport errors …
Browse files Browse the repository at this point in the history
…across threads. Legacy capture functionality removed.
  • Loading branch information
zajo committed Dec 25, 2023
1 parent d8aae79 commit 971ecac
Show file tree
Hide file tree
Showing 62 changed files with 2,898 additions and 2,371 deletions.
1,154 changes: 493 additions & 661 deletions doc/leaf.adoc

Large diffs are not rendered by default.

48 changes: 24 additions & 24 deletions example/capture_in_exception.cpp → example/dynamic_capture_eh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,38 +44,28 @@ int main()
{
int const task_count = 42;

// The error_handlers are used in this thread (see leaf::try_catch below).
// The arguments passed to individual lambdas are transported from the
// worker thread to the main thread automatically.
auto error_handlers = std::make_tuple(
[]( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid )
{
std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl;
},
[]( leaf::diagnostic_info const & unmatched )
{
std::cerr <<
"Unknown failure detected" << std::endl <<
"Cryptic diagnostic information follows" << std::endl <<
unmatched;
} );

// Container to collect the generated std::future objects.
std::vector<std::future<task_result>> fut;
std::vector<std::future<leaf::result<task_result>>> fut;

// Launch the tasks, but rather than launching the task function directly,
// we launch a wrapper function which calls leaf::capture, passing a context
// object that will hold the error objects reported from the task in case it
// throws. The error types the context is able to hold statically are
// automatically deduced from the type of the error_handlers tuple.
// we use leaf::try_catch in compbination with leaf::dynamic_capture:
// in case of a failure, the returned leaf::result<> will capture all error objects.
std::generate_n( std::back_inserter(fut), task_count,
[&]
{
return std::async(
std::launch::async,
[&]
{
return leaf::capture(leaf::make_shared_context(error_handlers), &task);
return leaf::try_catch(
[&]() -> leaf::result<task_result>
{
return task();
},
[]( leaf::dynamic_capture const & cap ) -> leaf::result<task_result>
{
return cap;
} );
} );
} );

Expand All @@ -87,12 +77,22 @@ int main()
leaf::try_catch(
[&]
{
task_result r = f.get();
task_result r = f.get().value();

// Success! Use r to access task_result.
std::cout << "Success!" << std::endl;
(void) r; // Presumably we'll somehow use the task_result.
},
error_handlers );
[]( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid )
{
std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl;
},
[]( leaf::diagnostic_info const & unmatched )
{
std::cerr <<
"Unknown failure detected" << std::endl <<
"Cryptic diagnostic information follows" << std::endl <<
unmatched;
} );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,38 +44,28 @@ int main()
{
int const task_count = 42;

// The error_handlers are used in this thread (see leaf::try_handle_all
// below). The arguments passed to individual lambdas are transported from
// the worker thread to the main thread automatically.
auto error_handlers = std::make_tuple(
[]( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid )
{
std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl;
},
[]( leaf::diagnostic_info const & unmatched )
{
std::cerr <<
"Unknown failure detected" << std::endl <<
"Cryptic diagnostic information follows" << std::endl <<
unmatched;
} );

// Container to collect the generated std::future objects.
std::vector<std::future<leaf::result<task_result>>> fut;

// Launch the tasks, but rather than launching the task function directly,
// we launch a wrapper function which calls leaf::capture, passing a context
// object that will hold the error objects reported from the task in case of
// an error. The error types the context is able to hold statically are
// automatically deduced from the type of the error_handlers tuple.
// we use leaf::try_handle_some in compbination with leaf::dynamic_capture:
// in case of a failure, the returned leaf::result<> will capture all error objects.
std::generate_n( std::back_inserter(fut), task_count,
[&]
{
return std::async(
std::launch::async,
[&]
{
return leaf::capture(leaf::make_shared_context(error_handlers), &task);
return leaf::try_handle_some(
[&]() -> leaf::result<task_result>
{
return task();
},
[]( leaf::dynamic_capture const & cap ) -> leaf::result<task_result>
{
return cap;
} );
} );
} );

Expand All @@ -87,14 +77,24 @@ int main()
leaf::try_handle_all(
[&]() -> leaf::result<void>
{
BOOST_LEAF_AUTO(r,f.get());
BOOST_LEAF_AUTO(r, f.get());

// Success! Use r to access task_result.
std::cout << "Success!" << std::endl;
(void) r; // Presumably we'll somehow use the task_result.
return { };
},
error_handlers );
[]( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid )
{
std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl;
},
[]( leaf::diagnostic_info const & unmatched )
{
std::cerr <<
"Unknown failure detected" << std::endl <<
"Cryptic diagnostic information follows" << std::endl <<
unmatched;
} );
}
}

Expand Down
16 changes: 8 additions & 8 deletions example/print_file/print_file_outcome_result.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ int main( int argc, char const * argv[] )
std::cout << buffer;
std::cout.flush();
if( std::cout.fail() )
return leaf::new_error(output_error, leaf::e_errno{errno}).to_error_code();
return leaf::new_error(output_error, leaf::e_errno{errno});

return 0;
},
Expand Down Expand Up @@ -164,7 +164,7 @@ result<char const *> parse_command_line( int argc, char const * argv[] )
if( argc==2 )
return argv[1];
else
return leaf::new_error(bad_command_line).to_error_code();
return leaf::new_error(bad_command_line);
}


Expand All @@ -174,7 +174,7 @@ result<std::shared_ptr<FILE>> file_open( char const * file_name )
if( FILE * f = fopen(file_name, "rb") )
return std::shared_ptr<FILE>(f, &fclose);
else
return leaf::new_error(open_error, leaf::e_errno{errno}).to_error_code();
return leaf::new_error(open_error, leaf::e_errno{errno});
}


Expand All @@ -184,14 +184,14 @@ result<std::size_t> file_size( FILE & f )
auto load = leaf::on_error([] { return leaf::e_errno{errno}; });

if( fseek(&f, 0, SEEK_END) )
return leaf::new_error(size_error).to_error_code();
return leaf::new_error(size_error);

long s = ftell(&f);
if( s==-1L )
return leaf::new_error(size_error).to_error_code();
return leaf::new_error(size_error);

if( fseek(&f,0,SEEK_SET) )
return leaf::new_error(size_error).to_error_code();
return leaf::new_error(size_error);

return std::size_t(s);
}
Expand All @@ -203,10 +203,10 @@ result<void> file_read( FILE & f, void * buf, std::size_t size )
std::size_t n = fread(buf, 1, size, &f);

if( ferror(&f) )
return leaf::new_error(read_error, leaf::e_errno{errno}).to_error_code();
return leaf::new_error(read_error, leaf::e_errno{errno});

if( n!=size )
return leaf::new_error(eof_error).to_error_code();
return leaf::new_error(eof_error);

return outcome::success();
}
Expand Down
4 changes: 2 additions & 2 deletions example/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

* [print_file](./print_file): The complete example from the [Five Minute Introduction](https://boostorg.github.io/leaf/#introduction). This directory contains several versions of the same program, each using a different error handling style.

* [capture_in_result.cpp](https://github.com/boostorg/leaf/blob/master/example/capture_in_result.cpp?ts=4): Shows how to transport error objects between threads in a `leaf::result<T>` object.
* [capture_in_exception.cpp](https://github.com/boostorg/leaf/blob/master/example/capture_in_exception.cpp?ts=4): Shows how to transport error objects between threads in an exception object.
* [dynamic_capture_result.cpp](https://github.com/boostorg/leaf/blob/master/example/dynamic_capture_result.cpp?ts=4): Shows how to transport error objects between threads in a `leaf::result<T>` object without using exception handling.
* [dynamic_capture_eh.cpp](https://github.com/boostorg/leaf/blob/master/example/dynamic_capture_eh.cpp?ts=4): Shows how to transport error objects between threads in an a `leaf::result<T>` object using exception handling.
* [lua_callback_result.cpp](https://github.com/boostorg/leaf/blob/master/example/lua_callback_result.cpp?ts=4): Transporting arbitrary error objects through an uncooperative C API.
* [lua_callback_eh.cpp](https://github.com/boostorg/leaf/blob/master/example/lua_callback_eh.cpp?ts=4): Transporting arbitrary error objects through an uncooperative exception-safe API.
* [exception_to_result.cpp](https://github.com/boostorg/leaf/blob/master/example/exception_to_result.cpp?ts=4): Demonstrates how to transport exceptions through a `noexcept` layer in the program.
Expand Down
Loading

0 comments on commit 971ecac

Please sign in to comment.