From 8a3b0244863d4c2f42acc776bcf66f7f452c03a9 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Fri, 27 Sep 2024 22:08:30 -0700 Subject: [PATCH] Renamed examples, fixed minor issues in build scripts --- README.md | 2 +- doc/leaf.adoc | 4 +- example/asio_beast_leaf_rpc.cpp | 564 ------------------ ...ack_eh.cpp => lua_callback_exceptions.cpp} | 0 ..._file_eh.cpp => print_file_exceptions.cpp} | 2 +- ..._result.cpp => print_file_leaf_result.cpp} | 2 +- ...esult.cpp => print_file_system_result.cpp} | 22 +- example/print_file/readme.md | 16 +- example/readme.md | 9 +- ..._eh.cpp => try_capture_all_exceptions.cpp} | 4 +- example/try_capture_all_result.cpp | 6 +- meson.build | 70 +-- test/Jamfile.v2 | 13 +- 13 files changed, 60 insertions(+), 654 deletions(-) delete mode 100644 example/asio_beast_leaf_rpc.cpp rename example/{lua_callback_eh.cpp => lua_callback_exceptions.cpp} (100%) rename example/print_file/{print_file_eh.cpp => print_file_exceptions.cpp} (98%) rename example/print_file/{print_file_result.cpp => print_file_leaf_result.cpp} (99%) rename example/print_file/{print_file_outcome_result.cpp => print_file_system_result.cpp} (90%) rename example/{try_capture_all_eh.cpp => try_capture_all_exceptions.cpp} (94%) diff --git a/README.md b/README.md index b1b1f56..3ffdbcf 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ https://boostorg.github.io/leaf/ ## Features * Portable single-header format, no dependencies. -* Tiny code size when configured for embedded development. +* Tiny code size, configurable for embedded development. * No dynamic memory allocations, even with very large payloads. * Deterministic unbiased efficiency on the "happy" path and the "sad" path. * Error objects are handled in constant time, independent of call stack depth. diff --git a/doc/leaf.adoc b/doc/leaf.adoc index e11c9bd..9710dc0 100644 --- a/doc/leaf.adoc +++ b/doc/leaf.adoc @@ -1365,7 +1365,7 @@ return leaf::try_catch( [.text-right] <> | <> -NOTE: Follow this link to see a complete example program: https://github.com/boostorg/leaf/blob/master/example/try_capture_all_eh.cpp?ts=4[try_capture_all_eh.cpp]. +NOTE: Follow this link to see a complete example program: https://github.com/boostorg/leaf/blob/master/example/try_capture_all_exceptions.cpp?ts=4[try_capture_all_exceptions.cpp]. ''' @@ -1776,7 +1776,7 @@ int main() noexcept NOTE: Follow this link to see the complete program: https://github.com/boostorg/leaf/blob/master/example/lua_callback_result.cpp?ts=4[lua_callback_result.cpp]. -TIP: When using Lua with {CPP}, we need to protect the Lua interpreter from exceptions that may be thrown from {CPP} functions installed as `lua_CFunction` callbacks. Here is the program from this section rewritten to use a {CPP} exception (instead of `leaf::result`) to safely communicate errors out of the `do_work` function: https://github.com/boostorg/leaf/blob/master/example/lua_callback_eh.cpp?ts=4[lua_callback_eh.cpp]. +TIP: When using Lua with {CPP}, we need to protect the Lua interpreter from exceptions that may be thrown from {CPP} functions installed as `lua_CFunction` callbacks. Here is the program from this section rewritten to use a {CPP} exception (instead of `leaf::result`) to safely communicate errors out of the `do_work` function: https://github.com/boostorg/leaf/blob/master/example/lua_callback_exceptions.cpp?ts=4[lua_callback_exceptions.cpp]. '''' diff --git a/example/asio_beast_leaf_rpc.cpp b/example/asio_beast_leaf_rpc.cpp deleted file mode 100644 index bc94ae7..0000000 --- a/example/asio_beast_leaf_rpc.cpp +++ /dev/null @@ -1,564 +0,0 @@ -// Copyright (c) 2019 Sorin Fetche - -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -// PLEASE NOTE: This example requires the Boost 1.70 version of Asio and Beast, -// which at the time of this writing is in beta. - -// Example of a composed asynchronous operation which uses the LEAF library for -// error handling and reporting. -// -// Examples of running: -// - in one terminal (re)run: ./asio_beast_leaf_rpc_v3 0.0.0.0 8080 -// - in another run: -// curl localhost:8080 -v -d "sum 0 1 2 3" -// generating errors returned to the client: -// curl localhost:8080 -v -X DELETE -d "" -// curl localhost:8080 -v -d "mul 1 2x3" -// curl localhost:8080 -v -d "div 1 0" -// curl localhost:8080 -v -d "mod 1" -// -// Runs that showcase the error handling on the server side: -// - error starting the server: -// ./asio_beast_leaf_rpc_v3 0.0.0.0 80 -// - error while running the server logic: -// ./asio_beast_leaf_rpc_v3 0.0.0.0 8080 -// curl localhost:8080 -v -d "error-quit" -// -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace beast = boost::beast; -namespace http = beast::http; -namespace leaf = boost::leaf; -namespace net = boost::asio; - -namespace { -using error_code = boost::system::error_code; -} // namespace - -// The operation being performed when an error occurs. -struct e_last_operation { - std::string_view value; -}; - -// The HTTP request type. -using request_t = http::request; -// The HTTP response type. -using response_t = http::response; - -response_t handle_request(request_t &&request); - -// A composed asynchronous operation that implements a basic remote calculator -// over HTTP. It receives from the remote side commands such as: -// sum 1 2 3 -// div 3 2 -// mod 1 0 -// in the body of POST requests and sends back the result. -// -// Besides the calculator related commands, it also offer a special command: -// - `error_quit` that asks the server to simulate a server side error that -// leads to the connection being dropped. -// -// From the error handling perspective there are three parts of the implementation: -// - the handling of an HTTP request and creating the response to send back -// (see handle_request) -// - the parsing and execution of the remote command we received as the body of -// an an HTTP POST request -// (see execute_command()) -// - this composed asynchronous operation which calls them, -// -// This example operation is based on: -// - https://github.com/boostorg/beast/blob/b02f59ff9126c5a17f816852efbbd0ed20305930/example/echo-op/echo_op.cpp -// - part of -// https://github.com/boostorg/beast/blob/b02f59ff9126c5a17f816852efbbd0ed20305930/example/advanced/server/advanced_server.cpp -// -template -auto async_demo_rpc(AsyncStream &stream, ErrorContext &error_context, CompletionToken &&token) -> - typename net::async_result::type, void(leaf::result)>::return_type { - - static_assert(beast::is_async_stream::value, "AsyncStream requirements not met"); - - using handler_type = - typename net::async_completion)>::completion_handler_type; - using base_type = beast::stable_async_base>; - struct internal_op : base_type { - // This object must have a stable address - struct temporary_data { - beast::flat_buffer buffer; - std::optional> parser; - std::optional response; - }; - - AsyncStream &m_stream; - ErrorContext &m_error_context; - temporary_data &m_data; - bool m_write_and_quit; - - internal_op(AsyncStream &stream, ErrorContext &error_context, handler_type &&handler) - : base_type{std::move(handler), stream.get_executor()}, m_stream{stream}, m_error_context{error_context}, - m_data{beast::allocate_stable(*this)}, m_write_and_quit{false} { - start_read_request(); - } - - void operator()(error_code ec, std::size_t /*bytes_transferred*/ = 0) { - leaf::result result_continue_execution; - { - auto active_context = activate_context(m_error_context); - auto load = leaf::on_error(e_last_operation{m_data.response ? "async_demo_rpc::continuation-write" - : "async_demo_rpc::continuation-read"}); - if (ec == http::error::end_of_stream) { - // The remote side closed the connection. - result_continue_execution = false; - } else if (ec) { - result_continue_execution = leaf::new_error(ec); - } else { - result_continue_execution = leaf::exception_to_result([&]() -> leaf::result { - if (!m_data.response) { - // Process the request we received. - m_data.response = handle_request(std::move(m_data.parser->release())); - m_write_and_quit = m_data.response->need_eof(); - http::async_write(m_stream, *m_data.response, std::move(*this)); - return true; - } - - // If getting here, we completed a write operation. - m_data.response.reset(); - // And start reading a new message if not quitting (i.e. - // the message semantics of the last response we sent - // required an end of file) - if (!m_write_and_quit) { - start_read_request(); - return true; - } - - // We didn't initiate any new async operation above, so - // we will not continue the execution. - return false; - }); - } - // The activation object and load_last_operation need to be - // reset before calling the completion handler This is because, - // in general, the completion handler may be called directly or - // posted and if posted, it could execute in another thread. - // This means that regardless of how the handler gets to be - // actually called we must ensure that it is not called with the - // error context active. Note: An error context cannot be - // activated twice - } - if (!result_continue_execution) { - // We don't continue the execution due to an error, calling the - // completion handler - this->complete_now(result_continue_execution.error()); - } else if( !*result_continue_execution ) { - // We don't continue the execution due to the flag not being - // set, calling the completion handler - this->complete_now(leaf::result{}); - } - } - - void start_read_request() { - m_data.parser.emplace(); - m_data.parser->body_limit(1024); - http::async_read(m_stream, m_data.buffer, *m_data.parser, std::move(*this)); - } - }; - - auto initiation = [](auto &&completion_handler, AsyncStream *stream, ErrorContext *error_context) { - internal_op op{*stream, *error_context, std::forward(completion_handler)}; - }; - - // We are in the "initiation" part of the async operation. - [[maybe_unused]] auto load = leaf::on_error(e_last_operation{"async_demo_rpc::initiation"}); - return net::async_initiate)>(initiation, token, &stream, &error_context); -} - -// The location of a int64 parse error. It refers the range of characters from -// which the parsing was done. -struct e_parse_int64_error { - using location_base = std::pair; - struct location : public location_base { - using location_base::location_base; - - friend std::ostream &operator<<(std::ostream &os, location const &value) { - auto const &sv = value.first; - std::size_t pos = std::distance(sv.begin(), value.second); - if (pos == 0) { - os << "->\"" << sv << "\""; - } else if (pos < sv.size()) { - os << "\"" << sv.substr(0, pos) << "\"->\"" << sv.substr(pos) << "\""; - } else { - os << "\"" << sv << "\"<-"; - } - return os; - } - }; - - location value; -}; - -// Parses an integer from a string_view. -leaf::result parse_int64(std::string_view word) { - auto const begin = word.begin(); - auto const end = word.end(); - std::int64_t value = 0; - auto i = begin; - bool result = boost::spirit::qi::parse(i, end, boost::spirit::long_long, value); - if (!result || i != end) { - return leaf::new_error(e_parse_int64_error{std::make_pair(word, i)}); - } - return value; -} - -// The command being executed while we get an error. It refers the range of -// characters from which the command was extracted. -struct e_command { - std::string_view value; -}; - -// The details about an incorrect number of arguments error Some commands may -// accept a variable number of arguments (e.g. greater than 1 would mean [2, -// SIZE_MAX]). -struct e_unexpected_arg_count { - struct arg_info { - std::size_t count; - std::size_t min; - std::size_t max; - - friend std::ostream &operator<<(std::ostream &os, arg_info const &value) { - os << value.count << " (required: "; - if (value.min == value.max) { - os << value.min; - } else if (value.max < SIZE_MAX) { - os << "[" << value.min << ", " << value.max << "]"; - } else { - os << "[" << value.min << ", MAX]"; - } - os << ")"; - return os; - } - }; - - arg_info value; -}; - -// The HTTP status that should be returned in case we get into an error. -struct e_http_status { - http::status value; -}; - -// Unexpected HTTP method. -struct e_unexpected_http_method { - http::verb value; -}; - -// The E-type that describes the `error_quit` command as an error condition. -struct e_error_quit { - struct none_t {}; - none_t value; -}; - -// Processes a remote command. -leaf::result execute_command(std::string_view line) { - // Split the command in words. - std::list words; // or std::deque words; - - char const *const ws = "\t \r\n"; - auto skip_ws = [&](std::string_view &line) { - if (auto pos = line.find_first_not_of(ws); pos != std::string_view::npos) { - line = line.substr(pos); - } else { - line = std::string_view{}; - } - }; - - skip_ws(line); - while (!line.empty()) { - std::string_view word; - if (auto pos = line.find_first_of(ws); pos != std::string_view::npos) { - word = line.substr(0, pos); - line = line.substr(pos + 1); - } else { - word = line; - line = std::string_view{}; - } - - if (!word.empty()) { - words.push_back(word); - } - skip_ws(line); - } - - static char const *const help = "Help:\n" - " error-quit Simulated error to end the session\n" - " sum * Addition\n" - " sub + Substraction\n" - " mul * Multiplication\n" - " div + Division\n" - " mod Remainder\n" - " This message"; - - if (words.empty()) { - return std::string(help); - } - - auto command = words.front(); - words.pop_front(); - - auto load_cmd = leaf::on_error(e_command{command}, e_http_status{http::status::bad_request}); - std::string response; - - if (command == "error-quit") { - return leaf::new_error(e_error_quit{}); - } else if (command == "sum") { - std::int64_t sum = 0; - for (auto const &w : words) { - BOOST_LEAF_AUTO(i, parse_int64(w)); - sum += i; - } - response = std::to_string(sum); - } else if (command == "sub") { - if (words.size() < 2) { - return leaf::new_error(e_unexpected_arg_count{words.size(), 2, SIZE_MAX}); - } - BOOST_LEAF_AUTO(sub, parse_int64(words.front())); - words.pop_front(); - for (auto const &w : words) { - BOOST_LEAF_AUTO(i, parse_int64(w)); - sub -= i; - } - response = std::to_string(sub); - } else if (command == "mul") { - std::int64_t mul = 1; - for (auto const &w : words) { - BOOST_LEAF_AUTO(i, parse_int64(w)); - mul *= i; - } - response = std::to_string(mul); - } else if (command == "div") { - if (words.size() < 2) { - return leaf::new_error(e_unexpected_arg_count{words.size(), 2, SIZE_MAX}); - } - BOOST_LEAF_AUTO(div, parse_int64(words.front())); - words.pop_front(); - for (auto const &w : words) { - BOOST_LEAF_AUTO(i, parse_int64(w)); - if (i == 0) { - // In some cases this command execution function might throw, - // not just return an error. - throw std::runtime_error{"division by zero"}; - } - div /= i; - } - response = std::to_string(div); - } else if (command == "mod") { - if (words.size() != 2) { - return leaf::new_error(e_unexpected_arg_count{words.size(), 2, 2}); - } - BOOST_LEAF_AUTO(i1, parse_int64(words.front())); - words.pop_front(); - BOOST_LEAF_AUTO(i2, parse_int64(words.front())); - words.pop_front(); - if (i2 == 0) { - // In some cases this command execution function might throw, not - // just return an error. - leaf::throw_exception(std::runtime_error{"division by zero"}); - } - response = std::to_string(i1 % i2); - } else { - response = help; - } - - return response; -} - -std::string diagnostic_to_str(leaf::diagnostic_details const &diag) { - auto str = boost::str(boost::format("%1%") % diag); - boost::algorithm::replace_all(str, "\n", "\n "); - return "\nDetailed error diagnostic:\n----\n" + str + "\n----"; -}; - -// Handles an HTTP request and returns the response to send back. -response_t handle_request(request_t &&request) { - - auto msg_prefix = [](e_command const *cmd) { - if (cmd != nullptr) { - return boost::str(boost::format("Error (%1%):") % cmd->value); - } - return std::string("Error:"); - }; - - auto make_sr = [](e_http_status const *status, std::string &&response) { - return std::make_pair(status != nullptr ? status->value : http::status::internal_server_error, - std::move(response)); - }; - - // In this variant of the RPC example we execute the remote command and - // handle any errors coming from it in one place (using - // `leaf::try_handle_all`). - auto pair_status_response = leaf::try_handle_all( - [&]() -> leaf::result> { - if (request.method() != http::verb::post) { - return leaf::new_error(e_unexpected_http_method{http::verb::post}, - e_http_status{http::status::bad_request}); - } - BOOST_LEAF_AUTO(response, execute_command(request.body())); - return std::make_pair(http::status::ok, std::move(response)); - }, - // For the `error_quit` command and associated error condition we have - // the error handler itself fail (by throwing). This means that the - // server will not send any response to the client, it will just - // shutdown the connection. This implementation showcases two aspects: - // - that the implementation of error handling can fail, too - // - how the asynchronous operation calling this error handling function - // reacts to this failure. - [](e_error_quit const &) -> std::pair { throw std::runtime_error("error_quit"); }, - // For the rest of error conditions we just build a message to be sent - // to the remote client. - [&](e_parse_int64_error const &e, e_http_status const *status, e_command const *cmd, - leaf::diagnostic_details const &diag) { - return make_sr(status, boost::str(boost::format("%1% int64 parse error: %2%") % msg_prefix(cmd) % e.value) + - diagnostic_to_str(diag)); - }, - [&](e_unexpected_arg_count const &e, e_http_status const *status, e_command const *cmd, - leaf::diagnostic_details const &diag) { - return make_sr(status, - boost::str(boost::format("%1% wrong argument count: %2%") % msg_prefix(cmd) % e.value) + - diagnostic_to_str(diag)); - }, - [&](e_unexpected_http_method const &e, e_http_status const *status, e_command const *cmd, - leaf::diagnostic_details const &diag) { - return make_sr(status, boost::str(boost::format("%1% unexpected HTTP method. Expected: %2%") % - msg_prefix(cmd) % e.value) + - diagnostic_to_str(diag)); - }, - [&](std::exception const & e, e_http_status const *status, e_command const *cmd, - leaf::diagnostic_details const &diag) { - return make_sr(status, boost::str(boost::format("%1% %2%") % msg_prefix(cmd) % e.what()) + - diagnostic_to_str(diag)); - }, - [&](e_http_status const *status, e_command const *cmd, leaf::diagnostic_details const &diag) { - return make_sr(status, boost::str(boost::format("%1% unknown failure") % msg_prefix(cmd)) + - diagnostic_to_str(diag)); - }); - response_t response{pair_status_response.first, request.version()}; - response.set(http::field::server, "Example-with-" BOOST_BEAST_VERSION_STRING); - response.set(http::field::content_type, "text/plain"); - response.keep_alive(request.keep_alive()); - pair_status_response.second += "\n"; - response.body() = std::move(pair_status_response.second); - response.prepare_payload(); - return response; -} - -int main(int argc, char **argv) { - auto msg_prefix = [](e_last_operation const *op) { - if (op != nullptr) { - return boost::str(boost::format("Error (%1%): ") % op->value); - } - return std::string("Error: "); - }; - - // Error handler for internal server internal errors (not communicated to - // the remote client). - auto error_handlers = std::make_tuple( - [&](std::exception_ptr const &ep, e_last_operation const *op) { - return leaf::try_handle_all( - [&]() -> leaf::result { std::rethrow_exception(ep); }, - [&](std::exception const & e, leaf::diagnostic_details const &diag) { - std::cerr << msg_prefix(op) << e.what() << " (captured)" << diagnostic_to_str(diag) - << std::endl; - return -11; - }, - [&](leaf::diagnostic_details const &diag) { - std::cerr << msg_prefix(op) << "unknown (captured)" << diagnostic_to_str(diag) << std::endl; - return -12; - }); - }, - [&](std::exception const & e, e_last_operation const *op, leaf::diagnostic_details const &diag) { - std::cerr << msg_prefix(op) << e.what() << diagnostic_to_str(diag) << std::endl; - return -21; - }, - [&](error_code ec, leaf::diagnostic_details const &diag, e_last_operation const *op) { - std::cerr << msg_prefix(op) << ec << ":" << ec.message() << diagnostic_to_str(diag) << std::endl; - return -22; - }, - [&](leaf::diagnostic_details const &diag, e_last_operation const *op) { - std::cerr << msg_prefix(op) << "unknown" << diagnostic_to_str(diag) << std::endl; - return -23; - }); - - // Top level try block and error handler. It will handle errors from - // starting the server for example failure to bind to a given port (e.g. - // ports less than 1024 if not running as root) - return leaf::try_handle_all( - [&]() -> leaf::result { - auto load = leaf::on_error(e_last_operation{"main"}); - if (argc != 3) { - std::cerr << "Usage: " << argv[0] << "
" << std::endl; - std::cerr << "Example:\n " << argv[0] << " 0.0.0.0 8080" << std::endl; - return -1; - } - - auto const address{net::ip::make_address(argv[1])}; - auto const port{static_cast(std::atoi(argv[2]))}; - net::ip::tcp::endpoint const endpoint{address, port}; - - net::io_context io_context; - - // Start the server acceptor and wait for a client. - net::ip::tcp::acceptor acceptor{io_context, endpoint}; - - auto local_endpoint = acceptor.local_endpoint(); - auto address_try_msg = acceptor.local_endpoint().address().to_string(); - if (address_try_msg == "0.0.0.0") { - address_try_msg = "localhost"; - } - std::cout << "Server: Started on: " << local_endpoint << std::endl; - std::cout << "Try in a different terminal:\n" - << " curl " << address_try_msg << ":" << local_endpoint.port() << " -d \"\"\nor\n" - << " curl " << address_try_msg << ":" << local_endpoint.port() << " -d \"sum 1 2 3\"" - << std::endl; - - auto socket = acceptor.accept(); - std::cout << "Server: Client connected: " << socket.remote_endpoint() << std::endl; - - // The error context for the async operation. - auto error_context = leaf::make_context(error_handlers); - int rv = 0; - async_demo_rpc(socket, error_context, [&](leaf::result result) { - // Note: In case we wanted to add some additional information to - // the error associated with the result we would need to - // activate the error context - auto active_context = activate_context(error_context); - if (result) { - std::cout << "Server: Client work completed successfully" << std::endl; - rv = 0; - } else { - // Handle errors from running the server logic - rv = error_context.handle_error(result.error(), error_handlers); - } - }); - io_context.run(); - - // Let the remote side know we are shutting down. - error_code ignored; - socket.shutdown(net::ip::tcp::socket::shutdown_both, ignored); - return rv; - }, - error_handlers); -} diff --git a/example/lua_callback_eh.cpp b/example/lua_callback_exceptions.cpp similarity index 100% rename from example/lua_callback_eh.cpp rename to example/lua_callback_exceptions.cpp diff --git a/example/print_file/print_file_eh.cpp b/example/print_file/print_file_exceptions.cpp similarity index 98% rename from example/print_file/print_file_eh.cpp rename to example/print_file/print_file_exceptions.cpp index 7e1dd79..adf54f7 100644 --- a/example/print_file/print_file_eh.cpp +++ b/example/print_file/print_file_exceptions.cpp @@ -7,7 +7,7 @@ // It reads a text file in a buffer and prints it to std::cout, using LEAF to // handle errors. This version uses exception handling. The version that does -// not use exception handling is in print_file_result.cpp. +// not use exception handling is in print_file_leaf_result.cpp. #include #include diff --git a/example/print_file/print_file_result.cpp b/example/print_file/print_file_leaf_result.cpp similarity index 99% rename from example/print_file/print_file_result.cpp rename to example/print_file/print_file_leaf_result.cpp index 36b67ff..a32ec54 100644 --- a/example/print_file/print_file_result.cpp +++ b/example/print_file/print_file_leaf_result.cpp @@ -7,7 +7,7 @@ // It reads a text file in a buffer and prints it to std::cout, using LEAF to // handle errors. This version does not use exception handling. The version that -// does use exception handling is in print_file_eh.cpp. +// does use exception handling is in print_file_exceptions.cpp. #include #include diff --git a/example/print_file/print_file_outcome_result.cpp b/example/print_file/print_file_system_result.cpp similarity index 90% rename from example/print_file/print_file_outcome_result.cpp rename to example/print_file/print_file_system_result.cpp index 8a7b32a..b5addfb 100644 --- a/example/print_file/print_file_outcome_result.cpp +++ b/example/print_file/print_file_system_result.cpp @@ -3,19 +3,19 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // This is the program presented in -// https://boostorg.github.io/leaf/#introduction-result, converted to use -// outcome::result instead of leaf::result. +// https://boostorg.github.io/leaf/#introduction-result. // It reads a text file in a buffer and prints it to std::cout, using LEAF to -// handle errors. This version does not use exception handling. +// handle errors. This version does not use exception handling. The version that +// does use exception handling is in print_file_exceptions.cpp. + -#include #include +#include #include #include #include -namespace outcome = boost::outcome_v2; namespace leaf = boost::leaf; @@ -32,12 +32,12 @@ enum error_code template -using result = outcome::std_result; +using result = boost::system::result; -// To enable LEAF to work with outcome::result, we need to specialize the +// To enable LEAF to work with boost::system::result, we need to specialize the // is_result_type template: namespace boost { namespace leaf { - template struct is_result_type>: std::true_type { }; + template struct is_result_type>: std::true_type { }; } } @@ -208,7 +208,7 @@ result file_read( FILE & f, void * buf, std::size_t size ) if( n != size ) return leaf::new_error(eof_error); - return outcome::success(); + return { }; } //////////////////////////////////////// @@ -217,14 +217,14 @@ result file_read( FILE & f, void * buf, std::size_t size ) namespace boost { - [[noreturn]] void throw_exception( std::exception const & e ) + BOOST_NORETURN void throw_exception( std::exception const & e ) { std::cerr << "Terminating due to a C++ exception under BOOST_LEAF_NO_EXCEPTIONS: " << e.what(); std::terminate(); } struct source_location; - [[noreturn]] void throw_exception( std::exception const & e, boost::source_location const & ) + BOOST_NORETURN void throw_exception( std::exception const & e, boost::source_location const & ) { throw_exception(e); } diff --git a/example/print_file/readme.md b/example/print_file/readme.md index 3c5a51c..050e1f6 100644 --- a/example/print_file/readme.md +++ b/example/print_file/readme.md @@ -1,16 +1,14 @@ # Print File Example -This directory has three versions of the same simple program, which reads a -file, prints it to standard out and handles errors using LEAF, each using a -different variation on error handling: +This directory contains several versions of a trivial program which takes a file name on the command line and prints it. Each version uses a different error handling implementaiton. -* [print_file_result.cpp](./print_file_result.cpp) reports errors with +* [print_file_leaf_result.cpp](./print_file_leaf_result.cpp) reports errors with `leaf::result`, using an error code `enum` for classification of failures. -* [print_file_outcome_result.cpp](./print_file_outcome_result.cpp) is the same - as the above, but using `outcome::result`. This demonstrates the ability - to transport arbitrary error objects through APIs that do not use - `leaf::result`. +* [print_file_system_result.cpp](./print_file_system_result.cpp) is the same as + above, but using `boost::system::result` instead of `leaf::result`. + This demonstrates the ability of LEAF to transport arbitrary error objects using an + external result type, rather than `boost::leaf::result`. -* [print_file_eh.cpp](./print_file_eh.cpp) throws on error, using an error code +* [print_file_exceptions.cpp](./print_file_exceptions.cpp) throws on error, using an error code `enum` for classification of failures. diff --git a/example/readme.md b/example/readme.md index 323d9e4..8b5f408 100644 --- a/example/readme.md +++ b/example/readme.md @@ -1,13 +1,12 @@ # Example Programs Using LEAF to Handle Errors -* [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. +* [print_file](./print_file): This directory contains several versions of a trivial program which takes a file name on the command line and prints it. Each version uses a different error handling implementaiton. -* [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` 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` object using exception handling. +* [try_capture_all_result.cpp](https://github.com/boostorg/leaf/blob/master/example/try_capture_all_result.cpp?ts=4): Shows how to transport error objects between threads in a `leaf::result` object without using exception handling. +* [try_capture_all_exceptions.cpp](https://github.com/boostorg/leaf/blob/master/example/try_capture_all_exceptions.cpp?ts=4): Shows how to transport error objects between threads in a `leaf::result` 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. +* [lua_callback_exceptions.cpp](https://github.com/boostorg/leaf/blob/master/example/lua_callback_exceptions.cpp?ts=4): Transporting arbitrary error objects through an uncooperative API using exceptions. * [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. * [exception_error_log.cpp](https://github.com/boostorg/leaf/blob/master/example/error_log.cpp?ts=4): Using `accumulate` to produce an error log. * [exception_error_trace.cpp](https://github.com/boostorg/leaf/blob/master/example/error_trace.cpp?ts=4): Same as above, but the log is recorded in a `std::deque` rather than just printed. * [print_half.cpp](https://github.com/boostorg/leaf/blob/master/example/print_half.cpp?ts=4): This is a Boost Outcome example adapted to LEAF, demonstrating the use of `try_handle_some` to handle some errors, forwarding any other error to the caller. -* [asio_beast_leaf_rpc.cpp](https://github.com/boostorg/leaf/blob/master/example/asio_beast_leaf_rpc.cpp?ts=4): A simple RPC calculator implemented with Beast+ASIO+LEAF. diff --git a/example/try_capture_all_eh.cpp b/example/try_capture_all_exceptions.cpp similarity index 94% rename from example/try_capture_all_eh.cpp rename to example/try_capture_all_exceptions.cpp index 588cac4..4372482 100644 --- a/example/try_capture_all_eh.cpp +++ b/example/try_capture_all_exceptions.cpp @@ -61,8 +61,8 @@ int main() std::vector>> fut; // Launch the tasks, but rather than launching the task function directly, - // 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. + // we use try_capture_all: in case of a failure, the returned leaf::result<> + // will capture all error objects. std::generate_n( std::back_inserter(fut), task_count, [&] { diff --git a/example/try_capture_all_result.cpp b/example/try_capture_all_result.cpp index cabe589..617f77d 100644 --- a/example/try_capture_all_result.cpp +++ b/example/try_capture_all_result.cpp @@ -3,7 +3,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // This is a simple program that demonstrates the use of LEAF to transport error -// objects between threads, without using exception handling. See capture_eh.cpp +// objects between threads, without using exception handling. See try_capture_all_exceptions.cpp // for the version that uses exception handling. #include @@ -61,8 +61,8 @@ int main() std::vector>> fut; // Launch the tasks, but rather than launching the task function directly, - // 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. + // we use leaf::try_capture_all: in case of a failure, the returned leaf::result<> + // will capture all error objects. std::generate_n( std::back_inserter(fut), task_count, [&] { diff --git a/meson.build b/meson.build index f95254f..dc57f14 100644 --- a/meson.build +++ b/meson.build @@ -37,7 +37,11 @@ if not meson.is_subproject() '-DBOOST_LEAF_BOOST_AVAILABLE', language:'cpp' ) endif - if compiler_id=='clang' + if compiler_id=='msvc' + add_global_arguments( + '/FC', + language:'cpp' ) + elif compiler_id=='clang' if get_option('buildtype')!='debug' add_global_arguments( '-Wno-unused-variable', @@ -234,61 +238,29 @@ endif if option_enable_examples - print_file_examples = [ - 'print_file_result' - ] + executable('print_file_leaf_result', 'example/print_file/print_file_leaf_result.cpp', dependencies: [leaf] ) if option_exceptions - print_file_examples += [ - 'print_file_eh' - ] + executable('print_file_exceptions', 'example/print_file/print_file_exceptions.cpp', dependencies: [leaf] ) endif if option_boost - print_file_examples += [ - 'print_file_outcome_result' - ] + executable('print_file_system_result', 'example/print_file/print_file_system_result.cpp', dependencies: [leaf, dep_boost] ) endif - foreach e : print_file_examples - executable(e, 'example/print_file/'+e+'.cpp', dependencies: [leaf, dep_thread, dep_boost] ) - endforeach - -endif - -################################# - -if option_enable_examples - - examples = [ - 'error_log', - 'error_trace', - 'print_half', - 'try_capture_all_result' - ] + executable('error_log', 'example/error_log.cpp', dependencies: [leaf] ) + executable('error_trace', 'example/error_trace.cpp', dependencies: [leaf] ) + executable('print_half', 'example/print_half.cpp', dependencies: [leaf] ) + executable('try_capture_all_result', 'example/try_capture_all_result.cpp', dependencies: [leaf] ) if option_exceptions - examples += [ - 'exception_to_result', - 'try_capture_all_eh' - ] - if option_lua - examples += [ - 'lua_callback_eh' - ] - endif - if option_boost - examples += [ - 'asio_beast_leaf_rpc' - ] - endif - endif - if option_lua - examples += [ - 'lua_callback_result' - ] + executable('try_capture_all_exceptions', 'example/try_capture_all_exceptions.cpp', dependencies: [leaf] ) + executable('exception_to_result', 'example/exception_to_result.cpp', dependencies: [leaf] ) endif - foreach e : examples - executable(e, 'example/'+e+'.cpp', dependencies: [leaf, dep_thread, dep_boost, dep_lua] ) - endforeach + if option_lua + if option_exceptions + executable('lua_callback_exceptions', 'example/lua_callback_exceptions.cpp', dependencies: [leaf, dep_lua] ) + endif + executable('lua_callback_result', 'example/lua_callback_result.cpp', dependencies: [leaf, dep_lua] ) + endif endif @@ -301,7 +273,7 @@ if option_enable_benchmarks endif if option_exceptions - error('The option leaf_enable_benchmarks requires the built-in option cpp_eh set to none. Aborting.') + error('The option leaf_enable_benchmarks requires the built-in option cpp_exceptions set to none. Aborting.') endif dep_tl_expected = subproject('tl_expected').get_variable('headers') diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e01599c..85371b5 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -152,11 +152,12 @@ compile-fail _compile-fail-result_2.cpp ; compile-fail _compile-fail-result_3.cpp ; compile-fail _compile-fail-result_4.cpp ; -exe try_capture_all_eh : ../example/try_capture_all_eh.cpp : single:no off:no leaf_debug_capture0:no leaf_release_capture0:no ; +exe try_capture_all_exceptions : ../example/try_capture_all_exceptions.cpp : single:no off:no leaf_debug_capture0:no leaf_release_capture0:no ; exe try_capture_all_result : ../example/try_capture_all_result.cpp : single:no leaf_debug_capture0:no leaf_release_capture0:no leaf_debug_embedded:no leaf_release_embedded:no ; -exe error_log : ../example/error_log.cpp : off:no ; -exe error_trace : ../example/error_trace.cpp : off:no ; +exe error_log : ../example/error_log.cpp ; +exe error_trace : ../example/error_trace.cpp ; exe exception_to_result : ../example/exception_to_result.cpp : off:no ; -exe print_file_eh : ../example/print_file/print_file_eh.cpp : off:no ; -exe print_file_result : ../example/print_file/print_file_result.cpp : off:no ; -exe print_half : ../example/print_half.cpp : off:no ; +exe print_file_exceptions : ../example/print_file/print_file_exceptions.cpp : off:no ; +exe print_file_leaf_result : ../example/print_file/print_file_leaf_result.cpp ; +exe print_file_system_result : ../example/print_file/print_file_system_result.cpp : leaf_debug_embedded:no leaf_release_embedded:no ; +exe print_half : ../example/print_half.cpp ;