summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/leaf/example
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/boost/libs/leaf/example
parentInitial commit. (diff)
downloadceph-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.cpp565
-rw-r--r--src/boost/libs/leaf/example/capture_in_exception.cpp97
-rw-r--r--src/boost/libs/leaf/example/capture_in_result.cpp119
-rw-r--r--src/boost/libs/leaf/example/error_log.cpp149
-rw-r--r--src/boost/libs/leaf/example/error_trace.cpp149
-rw-r--r--src/boost/libs/leaf/example/exception_to_result.cpp110
-rw-r--r--src/boost/libs/leaf/example/lua_callback_eh.cpp183
-rw-r--r--src/boost/libs/leaf/example/lua_callback_result.cpp174
-rw-r--r--src/boost/libs/leaf/example/print_file/print_file_eh.cpp198
-rw-r--r--src/boost/libs/leaf/example/print_file/print_file_outcome_result.cpp233
-rw-r--r--src/boost/libs/leaf/example/print_file/print_file_result.cpp225
-rw-r--r--src/boost/libs/leaf/example/print_file/readme.md16
-rw-r--r--src/boost/libs/leaf/example/print_half.cpp120
-rw-r--r--src/boost/libs/leaf/example/readme.md13
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.