diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/boost/libs/leaf/example | |
parent | Initial commit. (diff) | |
download | ceph-upstream/18.2.2.tar.xz ceph-upstream/18.2.2.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/boost/libs/leaf/example')
-rw-r--r-- | src/boost/libs/leaf/example/asio_beast_leaf_rpc.cpp | 565 | ||||
-rw-r--r-- | src/boost/libs/leaf/example/capture_in_exception.cpp | 97 | ||||
-rw-r--r-- | src/boost/libs/leaf/example/capture_in_result.cpp | 119 | ||||
-rw-r--r-- | src/boost/libs/leaf/example/error_log.cpp | 149 | ||||
-rw-r--r-- | src/boost/libs/leaf/example/error_trace.cpp | 149 | ||||
-rw-r--r-- | src/boost/libs/leaf/example/exception_to_result.cpp | 110 | ||||
-rw-r--r-- | src/boost/libs/leaf/example/lua_callback_eh.cpp | 183 | ||||
-rw-r--r-- | src/boost/libs/leaf/example/lua_callback_result.cpp | 174 | ||||
-rw-r--r-- | src/boost/libs/leaf/example/print_file/print_file_eh.cpp | 198 | ||||
-rw-r--r-- | src/boost/libs/leaf/example/print_file/print_file_outcome_result.cpp | 233 | ||||
-rw-r--r-- | src/boost/libs/leaf/example/print_file/print_file_result.cpp | 225 | ||||
-rw-r--r-- | src/boost/libs/leaf/example/print_file/readme.md | 16 | ||||
-rw-r--r-- | src/boost/libs/leaf/example/print_half.cpp | 120 | ||||
-rw-r--r-- | src/boost/libs/leaf/example/readme.md | 13 |
14 files changed, 2351 insertions, 0 deletions
diff --git a/src/boost/libs/leaf/example/asio_beast_leaf_rpc.cpp b/src/boost/libs/leaf/example/asio_beast_leaf_rpc.cpp new file mode 100644 index 000000000..8201a2ace --- /dev/null +++ b/src/boost/libs/leaf/example/asio_beast_leaf_rpc.cpp @@ -0,0 +1,565 @@ +// 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 <boost/algorithm/string/replace.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/version.hpp> +#include <boost/format.hpp> +#include <boost/leaf.hpp> +#include <boost/spirit/include/qi_numeric.hpp> +#include <boost/spirit/include/qi_parse.hpp> +#include <deque> +#include <iostream> +#include <list> +#include <optional> +#include <string> + +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<http::string_body>; +// The HTTP response type. +using response_t = http::response<http::string_body>; + +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 <class AsyncStream, typename ErrorContext, typename CompletionToken> +auto async_demo_rpc(AsyncStream &stream, ErrorContext &error_context, CompletionToken &&token) -> + typename net::async_result<typename std::decay<CompletionToken>::type, void(leaf::result<void>)>::return_type { + + static_assert(beast::is_async_stream<AsyncStream>::value, "AsyncStream requirements not met"); + + using handler_type = + typename net::async_completion<CompletionToken, void(leaf::result<void>)>::completion_handler_type; + using base_type = beast::stable_async_base<handler_type, beast::executor_type<AsyncStream>>; + struct internal_op : base_type { + // This object must have a stable address + struct temporary_data { + beast::flat_buffer buffer; + std::optional<http::request_parser<request_t::body_type>> parser; + std::optional<response_t> 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<temporary_data>(*this)}, m_write_and_quit{false} { + start_read_request(); + } + + void operator()(error_code ec, std::size_t /*bytes_transferred*/ = 0) { + leaf::result<bool> 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<bool> { + 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>{}); + } + } + + 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<decltype(completion_handler)>(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<CompletionToken, void(leaf::result<void>)>(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<std::string_view const, std::string_view::const_iterator>; + 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<std::int64_t> 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<std::string> execute_command(std::string_view line) { + // Split the command in words. + std::list<std::string_view> words; // or std::deque<std::string_view> 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 <int64>* Addition\n" + " sub <int64>+ Substraction\n" + " mul <int64>* Multiplication\n" + " div <int64>+ Division\n" + " mod <int64> <int64> Remainder\n" + " <anything else> 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. + throw leaf::exception(std::runtime_error{"division by zero"}); + } + response = std::to_string(i1 % i2); + } else { + response = help; + } + + return response; +} + +std::string diagnostic_to_str(leaf::verbose_diagnostic_info 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<std::pair<http::status, std::string>> { + 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<http::status, std::string> { 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::verbose_diagnostic_info 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::verbose_diagnostic_info 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::verbose_diagnostic_info 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::verbose_diagnostic_info 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::verbose_diagnostic_info 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<int> { std::rethrow_exception(ep); }, + [&](std::exception const & e, leaf::verbose_diagnostic_info const &diag) { + std::cerr << msg_prefix(op) << e.what() << " (captured)" << diagnostic_to_str(diag) + << std::endl; + return -11; + }, + [&](leaf::verbose_diagnostic_info 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::verbose_diagnostic_info const &diag) { + std::cerr << msg_prefix(op) << e.what() << diagnostic_to_str(diag) << std::endl; + return -21; + }, + [&](error_code ec, leaf::verbose_diagnostic_info const &diag, e_last_operation const *op) { + std::cerr << msg_prefix(op) << ec << ":" << ec.message() << diagnostic_to_str(diag) << std::endl; + return -22; + }, + [&](leaf::verbose_diagnostic_info 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<int> { + auto load = leaf::on_error(e_last_operation{"main"}); + if (argc != 3) { + std::cerr << "Usage: " << argv[0] << " <address> <port>" << 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::uint16_t>(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<void> 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 + leaf::result<int> result_int{result.error()}; + rv = error_context.handle_error<int>(result_int.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/src/boost/libs/leaf/example/capture_in_exception.cpp b/src/boost/libs/leaf/example/capture_in_exception.cpp new file mode 100644 index 000000000..c203affa8 --- /dev/null +++ b/src/boost/libs/leaf/example/capture_in_exception.cpp @@ -0,0 +1,97 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + +// 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) + +// This is a simple program that demonstrates the use of LEAF to transport error +// objects between threads, using exception handling. See capture_in_result.cpp +// for the version that does not use exception handling. + +#include <boost/leaf.hpp> +#include <vector> +#include <string> +#include <future> +#include <iterator> +#include <iostream> +#include <algorithm> + +namespace leaf = boost::leaf; + +// Define several error types. +struct e_thread_id { std::thread::id value; }; +struct e_failure_info1 { std::string value; }; +struct e_failure_info2 { int value; }; + +// A type that represents a successfully returned result from a task. +struct task_result { }; + + // This is our task function. It produces objects of type task_result, but it + // may fail. +task_result task() +{ + bool succeed = (rand()%4) != 0; //...at random. + if( succeed ) + return { }; + else + throw leaf::exception( + e_thread_id{std::this_thread::get_id()}, + e_failure_info1{"info"}, + e_failure_info2{42} ); +}; + +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; + + // 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. + 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); + } ); + } ); + + // Wait on the futures, get the task results, handle errors. + for( auto & f : fut ) + { + f.wait(); + + leaf::try_catch( + [&] + { + task_result 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. + }, + error_handlers ); + } +} diff --git a/src/boost/libs/leaf/example/capture_in_result.cpp b/src/boost/libs/leaf/example/capture_in_result.cpp new file mode 100644 index 000000000..f2aec02f0 --- /dev/null +++ b/src/boost/libs/leaf/example/capture_in_result.cpp @@ -0,0 +1,119 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + +// 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) + +// 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 +// for the version that uses exception handling. + +#include <boost/leaf.hpp> +#include <vector> +#include <string> +#include <future> +#include <iterator> +#include <iostream> +#include <algorithm> + +namespace leaf = boost::leaf; + +// Define several error types. +struct e_thread_id { std::thread::id value; }; +struct e_failure_info1 { std::string value; }; +struct e_failure_info2 { int value; }; + +// A type that represents a successfully returned result from a task. +struct task_result { }; + + // This is our task function. It produces objects of type task_result, but it + // may fail. +leaf::result<task_result> task() +{ + bool succeed = (rand()%4) != 0; //...at random. + if( succeed ) + return { }; + else + return leaf::new_error( + e_thread_id{std::this_thread::get_id()}, + e_failure_info1{"info"}, + e_failure_info2{42} ); +}; + +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. + 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); + } ); + } ); + + // Wait on the futures, get the task results, handle errors. + for( auto & f : fut ) + { + f.wait(); + + leaf::try_handle_all( + [&]() -> leaf::result<void> + { + 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 ); + } +} + +//////////////////////////////////////// + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +namespace boost +{ + BOOST_LEAF_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; + BOOST_LEAF_NORETURN void throw_exception( std::exception const & e, boost::source_location const & ) + { + throw_exception(e); + } +} + +#endif diff --git a/src/boost/libs/leaf/example/error_log.cpp b/src/boost/libs/leaf/example/error_log.cpp new file mode 100644 index 000000000..c8ccc81a9 --- /dev/null +++ b/src/boost/libs/leaf/example/error_log.cpp @@ -0,0 +1,149 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + +// 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) + +// This program demonstrates the use of leaf::on_error to print the path an +// error takes as it bubbles up the call stack. The printing code only runs if: +// - An error occurs, and +// - A handler that takes e_error_log argument is present. Otherwise none of the +// error log machinery will be invoked by LEAF. + +// This example is similar to error_trace, except the path the error takes is +// not captured, only printed. + +#include <boost/leaf.hpp> +#include <iostream> +#include <cstdlib> + +#define ENABLE_ERROR_LOG 1 + +namespace leaf = boost::leaf; + +// The error log is activated only if an error handling scope provides a handler +// for e_error_log. +struct e_error_log +{ + struct rec + { + char const * file; + int line; + friend std::ostream & operator<<( std::ostream & os, rec const & x ) + { + return os << x.file << '(' << x.line << ')'; + } + }; + + e_error_log() + { + std::cerr << "Error! Log:" << std::endl; + } + + // Our e_error_log instance is stateless, used only as a target to + // operator<<. + template <class T> + friend std::ostream & operator<<( e_error_log const &, T const & x ) + { + return std::cerr << x << std::endl; + } +}; + +// The ERROR_LOG macro is designed for use in functions that detect or forward +// errors up the call stack. If an error occurs, and if an error handling scope +// provides a handler for e_error_log, the supplied lambda is executed as the +// error bubbles up. +#define ERROR_LOG auto _log = leaf::on_error( []( e_error_log & log ) { log << e_error_log::rec{__FILE__, __LINE__}; } ) + +// Each function in the sequence below calls the previous function, and each +// function has failure_percent chance of failing. If a failure occurs, the +// ERROR_LOG macro will cause the path the error takes to be printed. +int const failure_percent = 25; + +leaf::result<void> f1() +{ + ERROR_LOG; + if( (std::rand()%100) > failure_percent ) + return { }; + else + return leaf::new_error(); +} + +leaf::result<void> f2() +{ + ERROR_LOG; + if( (std::rand()%100) > failure_percent ) + return f1(); + else + return leaf::new_error(); +} + +leaf::result<void> f3() +{ + ERROR_LOG; + if( (std::rand()%100) > failure_percent ) + return f2(); + else + return leaf::new_error(); +} + +leaf::result<void> f4() +{ + ERROR_LOG; + if( (std::rand()%100) > failure_percent ) + return f3(); + else + return leaf::new_error(); +} + +leaf::result<void> f5() +{ + ERROR_LOG; + if( (std::rand()%100) > failure_percent ) + return f4(); + else + return leaf::new_error(); +} + +int main() +{ + for( int i=0; i!=10; ++i ) + leaf::try_handle_all( + [&]() -> leaf::result<void> + { + std::cout << "Run # " << i << ": "; + BOOST_LEAF_CHECK(f5()); + std::cout << "Success!" << std::endl; + return { }; + }, +#if ENABLE_ERROR_LOG // This single #if enables or disables the printing of the error log. + []( e_error_log const & ) + { + }, +#endif + [] + { + std::cerr << "Error!" << std::endl; + } ); + return 0; +} + +//////////////////////////////////////// + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +namespace boost +{ + BOOST_LEAF_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; + BOOST_LEAF_NORETURN void throw_exception( std::exception const & e, boost::source_location const & ) + { + throw_exception(e); + } +} + +#endif diff --git a/src/boost/libs/leaf/example/error_trace.cpp b/src/boost/libs/leaf/example/error_trace.cpp new file mode 100644 index 000000000..706f577ec --- /dev/null +++ b/src/boost/libs/leaf/example/error_trace.cpp @@ -0,0 +1,149 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + +// 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) + +// This program demonstrates the use of leaf::on_error to capture the path an +// error takes as is bubbles up the call stack. The error path-capturing code +// only runs if: +// - An error occurs, and +// - A handler that takes e_error_trace argument is present. Otherwise none of +// the error trace machinery will be invoked by LEAF. + +// This example is similar to error_log, except the path the error takes is +// recorded in a std::deque, rather than just printed in-place. + +#include <boost/leaf.hpp> +#include <iostream> +#include <deque> +#include <cstdlib> + +#define ENABLE_ERROR_TRACE 1 + +namespace leaf = boost::leaf; + +// The error trace is activated only if an error handling scope provides a +// handler for e_error_trace. +struct e_error_trace +{ + struct rec + { + char const * file; + int line; + friend std::ostream & operator<<( std::ostream & os, rec const & x ) + { + return os << x.file << '(' << x.line << ')' << std::endl; + } + }; + + std::deque<rec> value; + + friend std::ostream & operator<<( std::ostream & os, e_error_trace const & tr ) + { + for( auto & i : tr.value ) + os << i; + return os; + } +}; + +// The ERROR_TRACE macro is designed for use in functions that detect or forward +// errors up the call stack. If an error occurs, and if an error handling scope +// provides a handler for e_error_trace, the supplied lambda is executed as the +// error bubbles up. +#define ERROR_TRACE auto _trace = leaf::on_error( []( e_error_trace & tr ) { tr.value.emplace_front(e_error_trace::rec{__FILE__, __LINE__}); } ) + +// Each function in the sequence below calls the previous function, and each +// function has failure_percent chance of failing. If a failure occurs, the +// ERROR_TRACE macro will cause the path the error takes to be captured in an +// e_error_trace. +int const failure_percent = 25; + +leaf::result<void> f1() +{ + ERROR_TRACE; + if( (std::rand()%100) > failure_percent ) + return { }; + else + return leaf::new_error(); +} + +leaf::result<void> f2() +{ + ERROR_TRACE; + if( (std::rand()%100) > failure_percent ) + return f1(); + else + return leaf::new_error(); +} + +leaf::result<void> f3() +{ + ERROR_TRACE; + if( (std::rand()%100) > failure_percent ) + return f2(); + else + return leaf::new_error(); +} + +leaf::result<void> f4() +{ + ERROR_TRACE; + if( (std::rand()%100) > failure_percent ) + return f3(); + else + return leaf::new_error(); +} + +leaf::result<void> f5() +{ + ERROR_TRACE; + if( (std::rand()%100) > failure_percent ) + return f4(); + else + return leaf::new_error(); +} + +int main() +{ + for( int i=0; i!=10; ++i ) + leaf::try_handle_all( + [&]() -> leaf::result<void> + { + std::cout << "Run # " << i << ": "; + BOOST_LEAF_CHECK(f5()); + std::cout << "Success!" << std::endl; + return { }; + }, +#if ENABLE_ERROR_TRACE // This single #if enables or disables the capturing of the error trace. + []( e_error_trace const & tr ) + { + std::cerr << "Error! Trace:" << std::endl << tr; + }, +#endif + [] + { + std::cerr << "Error!" << std::endl; + } ); + return 0; +} + +//////////////////////////////////////// + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +namespace boost +{ + BOOST_LEAF_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; + BOOST_LEAF_NORETURN void throw_exception( std::exception const & e, boost::source_location const & ) + { + throw_exception(e); + } +} + +#endif diff --git a/src/boost/libs/leaf/example/exception_to_result.cpp b/src/boost/libs/leaf/example/exception_to_result.cpp new file mode 100644 index 000000000..793f6777a --- /dev/null +++ b/src/boost/libs/leaf/example/exception_to_result.cpp @@ -0,0 +1,110 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + +// 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) + +// This example demonstrates how to transport exceptions thrown by a low level +// function through an intermediate scopes that are not exception-safe, to be +// handled in a high level function which may or may not be exception-safe. + +#include <boost/leaf.hpp> +#include <iostream> + +namespace leaf = boost::leaf; + +class error_base: public virtual std::exception { }; +class error_a: public virtual error_base { }; +class error_b: public virtual error_base { }; +class error_c: public virtual error_base { }; + + +// Lower-level library function which throws exceptions. +int compute_answer_throws() +{ + switch( rand()%4 ) + { + default: return 42; + case 1: throw error_a(); + case 2: throw error_b(); + case 3: throw error_c(); + } +} + + +// Call compute_answer_throws, switch to result<int> for error handling. +leaf::result<int> compute_answer() noexcept +{ + // Convert exceptions of types error_a and error_b to be communicated by + // leaf::result. Any other exception will be communicated as a + // std::exception_ptr. + return leaf::exception_to_result<error_a, error_b>( + [] + { + return compute_answer_throws(); + } ); +} + + +// Print the answer if the call to compute_answer is successful. +leaf::result<void> print_answer() noexcept +{ + BOOST_LEAF_AUTO( answer, compute_answer()); + std::cout << "Answer: " << answer << std::endl; + return { }; +} + + +int main() +{ + // Exercise print_answer a few times and handle errors. Note that the + // exception objects that compute_answer_throws throws are not handled as + // exceptions, but as regular LEAF error error objects... + for( int i=0; i!=42; ++i ) + { + leaf::try_handle_all( + []() -> leaf::result<void> + { + BOOST_LEAF_CHECK(print_answer()); + return { }; + }, + + []( error_a const & e ) + { + std::cerr << "Error A!" << std::endl; + }, + + []( error_b const & e ) + { + std::cerr << "Error B!" << std::endl; + }, + + // ...except for error_c errors, which (for demonstration) are + // captured as exceptions into std::exception_ptr as "unknown" + // exceptions. Presumably this should not happen, therefore at this + // point we treat this situation as a logic error: we print + // diagnostic information and bail out. + []( std::exception_ptr const * ep ) + { + std::cerr << "Got unknown error!" << std::endl; + + // Above, why do we take ep as a pointer? Because handle_all + // requires that the last handler matches any error and, taken + // as a pointer, if there isn't a std::exception_ptr associated + // with the error, the handler will still be matched (with 0 + // passed for ep). Had we taken it by value or by const &, the + // program would not have compiled. + if( ep ) + leaf::try_catch( + [&] + { + std::rethrow_exception(*ep); + }, + []( leaf::error_info const & unmatched ) + { + std::cerr << unmatched; + } ); + } ); + } + + return 0; +} diff --git a/src/boost/libs/leaf/example/lua_callback_eh.cpp b/src/boost/libs/leaf/example/lua_callback_eh.cpp new file mode 100644 index 000000000..4e87f267b --- /dev/null +++ b/src/boost/libs/leaf/example/lua_callback_eh.cpp @@ -0,0 +1,183 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + +// 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) + +// This is a simple program that shows how to report error objects out of a +// C-callback (which may not throw exceptions). + +extern "C" { + #include "lua.h" + #include "lauxlib.h" +} +#include <boost/leaf.hpp> +#include <iostream> +#include <stdlib.h> + +namespace leaf = boost::leaf; + +enum do_work_error_code +{ + ec1=1, + ec2 +}; + +struct e_lua_pcall_error +{ + int value; + + friend std::ostream & operator<<( std::ostream & os, e_lua_pcall_error const & x ) + { + os << "Lua error code = " << x.value; + switch( x.value ) + { + case LUA_ERRRUN: return os << " (LUA_ERRRUN)"; + case LUA_ERRMEM: return os << " (LUA_ERRMEM)"; + case LUA_ERRERR: return os << " (LUA_ERRERR)"; + default: return os << " (unknown)"; + } + } +}; + +struct e_lua_error_message { std::string value; }; + +struct e_lua_exception { std::exception_ptr value; }; + +// A noexcept wrapper for a lua_CFunction pointer. We capture the +// std::current_exception and wrap it in a e_lua_exception object. +template <int (*F)( lua_State * L )> +int wrap_lua_CFunction( lua_State * L ) noexcept +{ + return leaf::try_catch( + [&] + { + return F(L); + }, + [&]( leaf::error_info const & ei ) + { + ei.error().load( e_lua_exception{std::current_exception()} ); + return luaL_error(L, "C++ Exception"); // luaL_error does not return (longjmp). + } ); +} + + +// This is a C callback with a specific signature, callable from programs +// written in Lua. If it succeeds, it returns an int answer, by pushing it onto +// the Lua stack. But "sometimes" it fails, in which case it throws an +// exception, which will be processed by wrap_lua_CFunction (above). +int do_work( lua_State * L ) +{ + bool success = rand() % 2; // "Sometimes" do_work fails. + if( success ) + { + lua_pushnumber(L, 42); // Success, push the result on the Lua stack, return to Lua. + return 1; + } + else + { + throw leaf::exception(ec1); + } +} + + +std::shared_ptr<lua_State> init_lua_state() +{ + // Create a new lua_State, we'll use std::shared_ptr for automatic cleanup. + std::shared_ptr<lua_State> L(lua_open(), &lua_close); + + // Register the do_work function (above) as a C callback, under the global + // Lua name "do_work". With this, calls from Lua programs to do_work will + // land in the do_work C function we've registered. + lua_register( &*L, "do_work", &wrap_lua_CFunction<&do_work> ); + + // Pass some Lua code as a C string literal to Lua. This creates a global + // Lua function called "call_do_work", which we will later ask Lua to + // execute. + luaL_dostring( &*L, "\ +\n function call_do_work()\ +\n return do_work()\ +\n end" ); + + return L; +} + + +// Here we will ask Lua to execute the function call_do_work, which is written +// in Lua, and returns the value from do_work, which is written in C++ and +// registered with the Lua interpreter as a C callback. + +// If do_work succeeds, we return the resulting int answer. If it fails, we'll +// communicate that failure to our caller. +int call_lua( lua_State * L ) +{ + return leaf::try_catch( + [&] + { + leaf::error_monitor cur_err; + + // Ask the Lua interpreter to call the global Lua function call_do_work. + lua_getfield( L, LUA_GLOBALSINDEX, "call_do_work" ); + if( int err = lua_pcall(L, 0, 1, 0) ) + { + std::string msg = lua_tostring(L, 1); + lua_pop(L,1); + + // We got a Lua error which may be the error we're reporting + // from do_work, or some other error. If it is another error, + // cur_err.assigned_error_id() will return a new leaf::error_id, + // otherwise we'll be working with the original error reported + // by a C++ exception out of do_work. + throw leaf::exception( cur_err.assigned_error_id().load( e_lua_pcall_error{err}, e_lua_error_message{std::move(msg)} ) ); + } + else + { + // Success! Just return the int answer. + int answer = lua_tonumber(L, -1); + lua_pop(L, 1); + return answer; + } + }, + + []( e_lua_exception e ) -> int + { + // This is the exception communicated out of wrap_lua_CFunction. + std::rethrow_exception( e.value ); + } ); +} + +int main() +{ + std::shared_ptr<lua_State> L=init_lua_state(); + + for( int i=0; i!=10; ++i ) + { + leaf::try_catch( + + [&] + { + int answer = call_lua(&*L); + std::cout << "do_work succeeded, answer=" << answer << '\n'; + + }, + + []( do_work_error_code e, e_lua_error_message const & msg ) + { + std::cout << "Got do_work_error_code = " << e << ", " << msg.value << "\n"; + }, + + []( e_lua_pcall_error const & err, e_lua_error_message const & msg ) + { + std::cout << "Got e_lua_pcall_error, " << err << ", " << msg.value << "\n"; + }, + + []( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + } ); + } + + return 0; +} diff --git a/src/boost/libs/leaf/example/lua_callback_result.cpp b/src/boost/libs/leaf/example/lua_callback_result.cpp new file mode 100644 index 000000000..47ac6839f --- /dev/null +++ b/src/boost/libs/leaf/example/lua_callback_result.cpp @@ -0,0 +1,174 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + +// 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) + +// This is a simple program that shows how to report error objects out of a +// C-callback, converting them to leaf::result<T> as soon as controlreaches C++. + +extern "C" { + #include "lua.h" + #include "lauxlib.h" +} +#include <boost/leaf.hpp> +#include <iostream> +#include <stdlib.h> + +namespace leaf = boost::leaf; + +enum do_work_error_code +{ + ec1=1, + ec2 +}; + +struct e_lua_pcall_error +{ + int value; + + friend std::ostream & operator<<( std::ostream & os, e_lua_pcall_error const & x ) + { + os << "Lua error code = " << x.value; + switch( x.value ) + { + case LUA_ERRRUN: return os << " (LUA_ERRRUN)"; + case LUA_ERRMEM: return os << " (LUA_ERRMEM)"; + case LUA_ERRERR: return os << " (LUA_ERRERR)"; + default: return os << " (unknown)"; + } + } +}; + +struct e_lua_error_message { std::string value; }; + + +// This is a C callback with a specific signature, callable from programs +// written in Lua. If it succeeds, it returns an int answer, by pushing it onto +// the Lua stack. But "sometimes" it fails, in which case it calls luaL_error. +// This causes the Lua interpreter to abort and pop back into the C++ code which +// called it (see call_lua below). +int do_work( lua_State * L ) +{ + bool success = rand() % 2; // "Sometimes" do_work fails. + if( success ) + { + lua_pushnumber(L, 42); // Success, push the result on the Lua stack, return to Lua. + return 1; + } + else + { + return leaf::new_error(ec1), luaL_error(L,"do_work_error"); // luaL_error does not return (longjmp). + } +} + + +std::shared_ptr<lua_State> init_lua_state() +{ + // Create a new lua_State, we'll use std::shared_ptr for automatic cleanup. + std::shared_ptr<lua_State> L(lua_open(), &lua_close); + + // Register the do_work function (above) as a C callback, under the global + // Lua name "do_work". With this, calls from Lua programs to do_work will + // land in the do_work C function we've registered. + lua_register( &*L, "do_work", &do_work ); + + // Pass some Lua code as a C string literal to Lua. This creates a global + // Lua function called "call_do_work", which we will later ask Lua to + // execute. + luaL_dostring( &*L, "\ +\n function call_do_work()\ +\n return do_work()\ +\n end" ); + + return L; +} + + +// Here we will ask Lua to execute the function call_do_work, which is written +// in Lua, and returns the value from do_work, which is written in C++ and +// registered with the Lua interpreter as a C callback. + +// If do_work succeeds, we return the resulting int answer. If it fails, we'll +// communicate that failure to our caller. +leaf::result<int> call_lua( lua_State * L ) +{ + leaf::error_monitor cur_err; + + // Ask the Lua interpreter to call the global Lua function call_do_work. + lua_getfield( L, LUA_GLOBALSINDEX, "call_do_work" ); + if( int err = lua_pcall(L, 0, 1, 0) ) // Ask Lua to call the global function call_do_work. + { + std::string msg = lua_tostring(L, 1); + lua_pop(L, 1); + + // We got a Lua error which may be the error we're reporting from + // do_work, or some other error. If it is another error, + // cur_err.assigned_error_id() will return a new leaf::error_id, + // otherwise we'll be working with the original value returned by + // leaf::new_error in do_work. + return cur_err.assigned_error_id().load( e_lua_pcall_error{err}, e_lua_error_message{std::move(msg)} ); + } + else + { + // Success! Just return the int answer. + int answer = lua_tonumber(L, -1); + lua_pop(L, 1); + return answer; + } +} + +int main() +{ + std::shared_ptr<lua_State> L=init_lua_state(); + + for( int i=0; i!=10; ++i ) + { + leaf::try_handle_all( + + [&]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, call_lua(&*L)); + std::cout << "do_work succeeded, answer=" << answer << '\n'; + return { }; + }, + + []( do_work_error_code e, e_lua_error_message const & msg ) + { + std::cout << "Got do_work_error_code = " << e << ", " << msg.value << "\n"; + }, + + []( e_lua_pcall_error const & err, e_lua_error_message const & msg ) + { + std::cout << "Got e_lua_pcall_error, " << err << ", " << msg.value << "\n"; + }, + + []( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + } ); + } + + return 0; +} + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +namespace boost +{ + BOOST_LEAF_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; + BOOST_LEAF_NORETURN void throw_exception( std::exception const & e, boost::source_location const & ) + { + throw_exception(e); + } +} + +#endif diff --git a/src/boost/libs/leaf/example/print_file/print_file_eh.cpp b/src/boost/libs/leaf/example/print_file/print_file_eh.cpp new file mode 100644 index 000000000..69361e40e --- /dev/null +++ b/src/boost/libs/leaf/example/print_file/print_file_eh.cpp @@ -0,0 +1,198 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + +// 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) + +// This is the program presented in +// https://boostorg.github.io/leaf/#introduction-eh. + +// 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. + +#include <boost/leaf.hpp> +#include <iostream> +#include <stdio.h> + +namespace leaf = boost::leaf; + + +// First, we need an enum to define our error codes: +enum error_code +{ + bad_command_line = 1, + open_error, + read_error, + size_error, + eof_error, + output_error +}; + + +// We will handle all failures in our main function, but first, here are the +// declarations of the functions it calls, each communicating failures by +// throwing exceptions + +// Parse the command line, return the file name. +char const * parse_command_line( int argc, char const * argv[] ); + +// Open a file for reading. +std::shared_ptr<FILE> file_open( char const * file_name ); + +// Return the size of the file. +int file_size( FILE & f ); + +// Read size bytes from f into buf. +void file_read( FILE & f, void * buf, int size ); + + +// The main function, which handles all errors. +int main( int argc, char const * argv[] ) +{ + return leaf::try_catch( + + [&] + { + char const * file_name = parse_command_line(argc,argv); + + auto load = leaf::on_error( leaf::e_file_name{file_name} ); + + std::shared_ptr<FILE> f = file_open(file_name); + + int s = file_size(*f); + + std::string buffer(1 + s, '\0'); + file_read(*f, &buffer[0], buffer.size()-1); + + std::cout << buffer; + std::cout.flush(); + if( std::cout.fail() ) + throw leaf::exception(output_error, leaf::e_errno{errno}); + + return 0; + }, + + // Each of the lambdas below is an error handler. LEAF will consider + // them, in order, and call the first one that matches the available + // error objects. + + // This handler will be called if the error includes: + // - an object of type error_code equal to open_error, and + // - an object of type leaf::e_errno that has .value equal to ENOENT, + // and + // - an object of type leaf::e_file_name. + []( leaf::match<error_code, open_error>, leaf::match_value<leaf::e_errno, ENOENT>, leaf::e_file_name const & fn ) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 1; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to open_error, and + // - an object of type leaf::e_errno (regardless of its .value), and + // - an object of type leaf::e_file_name. + []( leaf::match<error_code, open_error>, leaf::e_errno const & errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 2; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to any of size_error, + // read_error, eof_error, and + // - an optional object of type leaf::e_errno (regardless of its + // .value), and + // - an object of type leaf::e_file_name. + []( leaf::match<error_code, size_error, read_error, eof_error>, leaf::e_errno const * errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to access " << fn.value; + if( errn ) + std::cerr << ", errno=" << *errn; + std::cerr << std::endl; + return 3; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to output_error, and + // - an object of type leaf::e_errno (regardless of its .value), + []( leaf::match<error_code, output_error>, leaf::e_errno const & errn ) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 4; + }, + + // This handler will be called if we've got a bad_command_line + []( leaf::match<error_code, bad_command_line> ) + { + std::cout << "Bad command line argument" << std::endl; + return 5; + }, + + // This last handler matches any error: it prints diagnostic information + // to help debug logic errors in the program, since it failed to match + // an appropriate error handler to the error condition it encountered. + // In this program this handler will never be called. + []( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 6; + } ); +} + + +// Implementations of the functions called by main: + + +// Parse the command line, return the file name. +char const * parse_command_line( int argc, char const * argv[] ) +{ + if( argc==2 ) + return argv[1]; + else + throw leaf::exception(bad_command_line); +} + + +// Open a file for reading. +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 + throw leaf::exception(open_error, leaf::e_errno{errno}); +} + + +// Return the size of the file. +int file_size( FILE & f ) +{ + auto load = leaf::on_error([] { return leaf::e_errno{errno}; }); + + if( fseek(&f, 0, SEEK_END) ) + throw leaf::exception(size_error); + + int s = ftell(&f); + if( s==-1L ) + throw leaf::exception(size_error); + + if( fseek(&f,0,SEEK_SET) ) + throw leaf::exception(size_error); + + return s; +} + + +// Read size bytes from f into buf. +void file_read( FILE & f, void * buf, int size ) +{ + int n = fread(buf, 1, size, &f); + + if( ferror(&f) ) + throw leaf::exception(read_error, leaf::e_errno{errno}); + + if( n!=size ) + throw leaf::exception(eof_error); +} diff --git a/src/boost/libs/leaf/example/print_file/print_file_outcome_result.cpp b/src/boost/libs/leaf/example/print_file/print_file_outcome_result.cpp new file mode 100644 index 000000000..6710829df --- /dev/null +++ b/src/boost/libs/leaf/example/print_file/print_file_outcome_result.cpp @@ -0,0 +1,233 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + +// 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) + +// This is the program presented in +// https://boostorg.github.io/leaf/#introduction-result, converted to use +// outcome::result instead of leaf::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. + +#include <boost/outcome/std_result.hpp> +#include <boost/leaf.hpp> +#include <iostream> +#include <stdio.h> + +namespace outcome = boost::outcome_v2; +namespace leaf = boost::leaf; + + +// First, we need an enum to define our error codes: +enum error_code +{ + bad_command_line = 1, + open_error, + read_error, + size_error, + eof_error, + output_error +}; + + +template <class T> +using result = outcome::std_result<T>; + +// To enable LEAF to work with outcome::result, we need to specialize the +// is_result_type template: +namespace boost { namespace leaf { + template <class T> struct is_result_type<outcome::std_result<T>>: std::true_type { }; +} } + + +// We will handle all failures in our main function, but first, here are the +// declarations of the functions it calls, each communicating failures using +// result<T>: + +// Parse the command line, return the file name. +result<char const *> parse_command_line( int argc, char const * argv[] ); + +// Open a file for reading. +result<std::shared_ptr<FILE>> file_open( char const * file_name ); + +// Return the size of the file. +result<int> file_size( FILE & f ); + +// Read size bytes from f into buf. +result<void> file_read( FILE & f, void * buf, int size ); + + +// The main function, which handles all errors. +int main( int argc, char const * argv[] ) +{ + return leaf::try_handle_all( + + [&]() -> result<int> + { + BOOST_LEAF_AUTO(file_name, parse_command_line(argc,argv)); + + auto load = leaf::on_error( leaf::e_file_name{file_name} ); + + BOOST_LEAF_AUTO(f, file_open(file_name)); + + BOOST_LEAF_AUTO(s, file_size(*f)); + + std::string buffer(1 + s, '\0'); + BOOST_LEAF_CHECK(file_read(*f, &buffer[0], buffer.size()-1)); + + std::cout << buffer; + std::cout.flush(); + if( std::cout.fail() ) + return leaf::new_error(output_error, leaf::e_errno{errno}).to_error_code(); + + return 0; + }, + + // Each of the lambdas below is an error handler. LEAF will consider + // them, in order, and call the first one that matches the available + // error objects. + + // This handler will be called if the error includes: + // - an object of type error_code equal to open_error, and + // - an object of type leaf::e_errno that has .value equal to ENOENT, + // and + // - an object of type leaf::e_file_name. + []( leaf::match<error_code, open_error>, leaf::match_value<leaf::e_errno, ENOENT>, leaf::e_file_name const & fn ) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 1; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to open_error, and + // - an object of type leaf::e_errno (regardless of its .value), and + // - an object of type leaf::e_file_name. + []( leaf::match<error_code, open_error>, leaf::e_errno const & errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 2; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to any of size_error, + // read_error, eof_error, and + // - an optional object of type leaf::e_errno (regardless of its + // .value), and + // - an object of type leaf::e_file_name. + []( leaf::match<error_code, size_error, read_error, eof_error>, leaf::e_errno const * errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to access " << fn.value; + if( errn ) + std::cerr << ", errno=" << *errn; + std::cerr << std::endl; + return 3; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to output_error, and + // - an object of type leaf::e_errno (regardless of its .value), + []( leaf::match<error_code, output_error>, leaf::e_errno const & errn ) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 4; + }, + + // This handler will be called if we've got a bad_command_line + []( leaf::match<error_code, bad_command_line> ) + { + std::cout << "Bad command line argument" << std::endl; + return 5; + }, + + // This last handler matches any error: it prints diagnostic information + // to help debug logic errors in the program, since it failed to match + // an appropriate error handler to the error condition it encountered. + // In this program this handler will never be called. + []( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 6; + } ); +} + + +// Implementations of the functions called by main: + + +// Parse the command line, return the file name. +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(); +} + + +// Open a file for reading. +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 the size of the file. +result<int> 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(); + + int s = ftell(&f); + if( s==-1L ) + return leaf::new_error(size_error).to_error_code(); + + if( fseek(&f,0,SEEK_SET) ) + return leaf::new_error(size_error).to_error_code(); + + return s; +} + + +// Read size bytes from f into buf. +result<void> file_read( FILE & f, void * buf, int size ) +{ + int n = fread(buf, 1, size, &f); + + if( ferror(&f) ) + return leaf::new_error(read_error, leaf::e_errno{errno}).to_error_code(); + + if( n!=size ) + return leaf::new_error(eof_error).to_error_code(); + + return outcome::success(); +} + +//////////////////////////////////////// + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +namespace boost +{ + BOOST_LEAF_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; + BOOST_LEAF_NORETURN void throw_exception( std::exception const & e, boost::source_location const & ) + { + throw_exception(e); + } +} + +#endif diff --git a/src/boost/libs/leaf/example/print_file/print_file_result.cpp b/src/boost/libs/leaf/example/print_file/print_file_result.cpp new file mode 100644 index 000000000..46f5af25a --- /dev/null +++ b/src/boost/libs/leaf/example/print_file/print_file_result.cpp @@ -0,0 +1,225 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + +// 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) + +// This is the program presented in +// 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. The version that +// does use exception handling is in print_file_eh.cpp. + +#include <boost/leaf.hpp> +#include <iostream> +#include <stdio.h> + +namespace leaf = boost::leaf; + + +// First, we need an enum to define our error codes: +enum error_code +{ + bad_command_line = 1, + open_error, + read_error, + size_error, + eof_error, + output_error +}; + + +template <class T> +using result = leaf::result<T>; + + +// We will handle all failures in our main function, but first, here are the +// declarations of the functions it calls, each communicating failures using +// result<T>: + +// Parse the command line, return the file name. +result<char const *> parse_command_line( int argc, char const * argv[] ); + +// Open a file for reading. +result<std::shared_ptr<FILE>> file_open( char const * file_name ); + +// Return the size of the file. +result<int> file_size( FILE & f ); + +// Read size bytes from f into buf. +result<void> file_read( FILE & f, void * buf, int size ); + + +// The main function, which handles all errors. +int main( int argc, char const * argv[] ) +{ + return leaf::try_handle_all( + + [&]() -> result<int> + { + BOOST_LEAF_AUTO(file_name, parse_command_line(argc,argv)); + + auto load = leaf::on_error( leaf::e_file_name{file_name} ); + + BOOST_LEAF_AUTO(f, file_open(file_name)); + + BOOST_LEAF_AUTO(s, file_size(*f)); + + std::string buffer(1 + s, '\0'); + BOOST_LEAF_CHECK(file_read(*f, &buffer[0], buffer.size()-1)); + + std::cout << buffer; + std::cout.flush(); + if( std::cout.fail() ) + return leaf::new_error(output_error, leaf::e_errno{errno}); + + return 0; + }, + + // Each of the lambdas below is an error handler. LEAF will consider + // them, in order, and call the first one that matches the available + // error objects. + + // This handler will be called if the error includes: + // - an object of type error_code equal to open_error, and + // - an object of type leaf::e_errno that has .value equal to ENOENT, + // and + // - an object of type leaf::e_file_name. + []( leaf::match<error_code, open_error>, leaf::match_value<leaf::e_errno, ENOENT>, leaf::e_file_name const & fn ) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 1; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to open_error, and + // - an object of type leaf::e_errno (regardless of its .value), and + // - an object of type leaf::e_file_name. + []( leaf::match<error_code, open_error>, leaf::e_errno const & errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 2; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to any of size_error, + // read_error, eof_error, and + // - an optional object of type leaf::e_errno (regardless of its + // .value), and + // - an object of type leaf::e_file_name. + []( leaf::match<error_code, size_error, read_error, eof_error>, leaf::e_errno const * errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to access " << fn.value; + if( errn ) + std::cerr << ", errno=" << *errn; + std::cerr << std::endl; + return 3; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to output_error, and + // - an object of type leaf::e_errno (regardless of its .value), + []( leaf::match<error_code, output_error>, leaf::e_errno const & errn ) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 4; + }, + + // This handler will be called if we've got a bad_command_line + []( leaf::match<error_code, bad_command_line> ) + { + std::cout << "Bad command line argument" << std::endl; + return 5; + }, + + // This last handler matches any error: it prints diagnostic information + // to help debug logic errors in the program, since it failed to match + // an appropriate error handler to the error condition it encountered. + // In this program this handler will never be called. + []( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 6; + } ); +} + + +// Implementations of the functions called by main: + + +// Parse the command line, return the file name. +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); +} + + +// Open a file for reading. +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}); +} + + +// Return the size of the file. +result<int> 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); + + int s = ftell(&f); + if( s==-1L ) + return leaf::new_error(size_error); + + if( fseek(&f,0,SEEK_SET) ) + return leaf::new_error(size_error); + + return s; +} + + +// Read size bytes from f into buf. +result<void> file_read( FILE & f, void * buf, int size ) +{ + int n = fread(buf, 1, size, &f); + + if( ferror(&f) ) + return leaf::new_error(read_error, leaf::e_errno{errno}); + + if( n!=size ) + return leaf::new_error(eof_error); + + return { }; +} + +//////////////////////////////////////// + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +namespace boost +{ + BOOST_LEAF_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; + BOOST_LEAF_NORETURN void throw_exception( std::exception const & e, boost::source_location const & ) + { + throw_exception(e); + } +} + +#endif diff --git a/src/boost/libs/leaf/example/print_file/readme.md b/src/boost/libs/leaf/example/print_file/readme.md new file mode 100644 index 000000000..3c5a51cda --- /dev/null +++ b/src/boost/libs/leaf/example/print_file/readme.md @@ -0,0 +1,16 @@ +# 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: + +* [print_file_result.cpp](./print_file_result.cpp) reports errors with + `leaf::result<T>`, 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<T>`. This demonstrates the ability + to transport arbitrary error objects through APIs that do not use + `leaf::result<T>`. + +* [print_file_eh.cpp](./print_file_eh.cpp) throws on error, using an error code + `enum` for classification of failures. diff --git a/src/boost/libs/leaf/example/print_half.cpp b/src/boost/libs/leaf/example/print_half.cpp new file mode 100644 index 000000000..f6caaa8e7 --- /dev/null +++ b/src/boost/libs/leaf/example/print_half.cpp @@ -0,0 +1,120 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + +// 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) + +// This program is an adaptation of the following Boost Outcome example: +// https://github.com/ned14/outcome/blob/master/doc/src/snippets/using_result.cpp + +#include <boost/leaf.hpp> +#include <algorithm> +#include <ctype.h> +#include <string> +#include <iostream> + +namespace leaf = boost::leaf; + +enum class ConversionErrc +{ + EmptyString = 1, + IllegalChar, + TooLong +}; + +leaf::result<int> convert(const std::string& str) noexcept +{ + if (str.empty()) + return leaf::new_error(ConversionErrc::EmptyString); + + if (!std::all_of(str.begin(), str.end(), ::isdigit)) + return leaf::new_error(ConversionErrc::IllegalChar); + + if (str.length() > 9) + return leaf::new_error(ConversionErrc::TooLong); + + return atoi(str.c_str()); +} + +// Do not static_store BigInt to actually work -- it's a stub. +struct BigInt +{ + static leaf::result<BigInt> fromString(const std::string& s) { return BigInt{s}; } + explicit BigInt(const std::string&) { } + BigInt half() const { return BigInt{""}; } + friend std::ostream& operator<<(std::ostream& o, const BigInt&) { return o << "big int half"; } +}; + +// This function handles ConversionErrc::TooLong errors, forwards any other +// error to the caller. +leaf::result<void> print_half(const std::string& text) +{ + return leaf::try_handle_some( + [&]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(r,convert(text)); + std::cout << r / 2 << std::endl; + return { }; + }, + [&]( leaf::match<ConversionErrc, ConversionErrc::TooLong> ) -> leaf::result<void> + { + BOOST_LEAF_AUTO(i, BigInt::fromString(text)); + std::cout << i.half() << std::endl; + return { }; + } ); +} + +int main( int argc, char const * argv[] ) +{ + return leaf::try_handle_all( + [&]() -> leaf::result<int> + { + BOOST_LEAF_CHECK( print_half(argc<2 ? "" : argv[1]) ); + std::cout << "ok" << std::endl; + return 0; + }, + + []( leaf::match<ConversionErrc, ConversionErrc::EmptyString> ) + { + std::cerr << "Empty string!" << std::endl; + return 1; + }, + + []( leaf::match<ConversionErrc, ConversionErrc::IllegalChar> ) + { + std::cerr << "Illegal char!" << std::endl; + return 2; + }, + + []( leaf::error_info const & unmatched ) + { + // This will never execute in this program, but it would detect + // logic errors where an unknown error reaches main. In this case, + // we print diagnostic information. + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 3; + } ); +} + +//////////////////////////////////////// + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +namespace boost +{ + BOOST_LEAF_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; + BOOST_LEAF_NORETURN void throw_exception( std::exception const & e, boost::source_location const & ) + { + throw_exception(e); + } +} + +#endif diff --git a/src/boost/libs/leaf/example/readme.md b/src/boost/libs/leaf/example/readme.md new file mode 100644 index 000000000..3e98584a0 --- /dev/null +++ b/src/boost/libs/leaf/example/readme.md @@ -0,0 +1,13 @@ +# 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. + +* [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. +* [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. +* [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. |