diff options
Diffstat (limited to 'src/boost/libs/leaf')
152 files changed, 17825 insertions, 0 deletions
diff --git a/src/boost/libs/leaf/CMakeLists.txt b/src/boost/libs/leaf/CMakeLists.txt new file mode 100644 index 000000000..fcdab913a --- /dev/null +++ b/src/boost/libs/leaf/CMakeLists.txt @@ -0,0 +1,20 @@ +# Generated by `boostdep --cmake leaf` +# Copyright 2020 Peter Dimov +# Distributed under the Boost Software License, Version 1.0. +# https://www.boost.org/LICENSE_1_0.txt + +cmake_minimum_required(VERSION 3.5...3.16) + +project(boost_leaf VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) + +add_library(boost_leaf INTERFACE) +add_library(Boost::leaf ALIAS boost_leaf) + +target_include_directories(boost_leaf INTERFACE include) + +if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt") + + add_subdirectory(test) + +endif() + diff --git a/src/boost/libs/leaf/LICENSE_1_0.txt b/src/boost/libs/leaf/LICENSE_1_0.txt new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/src/boost/libs/leaf/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/boost/libs/leaf/README.md b/src/boost/libs/leaf/README.md new file mode 100644 index 000000000..a8241876e --- /dev/null +++ b/src/boost/libs/leaf/README.md @@ -0,0 +1,31 @@ +# LEAF + +> A lightweight error handling library for C++11. + +## Documentation + +https://boostorg.github.io/leaf/ + +## Features + +* Portable single-header format, no dependencies. +* Tiny code size when configured for embedded development. +* No dynamic memory allocations, even with very large payloads. +* Deterministic unbiased efficiency on the "happy" path and the "sad" path. +* Error objects are handled in constant time, independent of call stack depth. +* Can be used with or without exception handling. + +## Support + +* [cpplang on Slack](https://Cpplang.slack.com) (use the `#boost` channel) +* [Boost Users Mailing List](https://lists.boost.org/mailman/listinfo.cgi/boost-users) +* [Boost Developers Mailing List](https://lists.boost.org/mailman/listinfo.cgi/boost) + +## Distribution + +Besides GitHub, there are two other distribution channels: + +* LEAF is included in official [Boost](https://www.boost.org/) releases, starting with Boost 1.75. +* For maximum portability, the library is also available in single-header format: simply download [leaf.hpp](https://boostorg.github.io/leaf/leaf.hpp) (direct download link). + +Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. Distributed under the http://www.boost.org/LICENSE_1_0.txt[Boost Software License, Version 1.0]. diff --git a/src/boost/libs/leaf/benchmark/b.bat b/src/boost/libs/leaf/benchmark/b.bat new file mode 100644 index 000000000..1f475c54d --- /dev/null +++ b/src/boost/libs/leaf/benchmark/b.bat @@ -0,0 +1,6 @@ +ninja +del benchmark.csv +deep_stack_leaf +deep_stack_tl +deep_stack_result +deep_stack_outcome diff --git a/src/boost/libs/leaf/benchmark/b.sh b/src/boost/libs/leaf/benchmark/b.sh new file mode 100755 index 000000000..dcd010e12 --- /dev/null +++ b/src/boost/libs/leaf/benchmark/b.sh @@ -0,0 +1,6 @@ +ninja +rm benchmark.csv +./deep_stack_leaf +./deep_stack_tl +./deep_stack_result +./deep_stack_outcome diff --git a/src/boost/libs/leaf/benchmark/benchmark.md b/src/boost/libs/leaf/benchmark/benchmark.md new file mode 100644 index 000000000..0ae9b3328 --- /dev/null +++ b/src/boost/libs/leaf/benchmark/benchmark.md @@ -0,0 +1,471 @@ +# Benchmark + +The LEAF github repository contains two similar benchmarking programs, one using LEAF, the other configurable to use `tl::expected` or Boost Outcome, that simulate transporting error objects across 10 levels of stack frames, measuring the performance of the three libraries. + +Links: +* LEAF: https://boostorg.github.io/leaf +* `tl::expected`: https://github.com/TartanLlama/expected +* Boost Outcome V2: https://www.boost.org/doc/libs/release/libs/outcome/doc/html/index.html + +## Library design considerations + +LEAF serves a similar purpose to other error handling libraries, but its design is very different. The benchmarks are comparing apples and oranges. + +The main design difference is that when using LEAF, error objects are not communicated in return values. In case of a failure, the `leaf::result<T>` object transports only an `int`, the unique error ID. + +Error objects skip the error neutral functions in the call stack and get moved directly to the the error handling scope that needs them. This mechanism does not depend on RVO or any other optimization: as soon as the program passes an error object to LEAF, it moves it to the correct error handling scope. + +Other error handling libraries instead couple the static type of the return value of *all* error neutral functions with the error type an error reporting function may return. This approach suffers from the same problems as statically-enforced exception specifications: + +* It's difficult to use in polymorphic function calls, and +* It impedes interoperability between the many different error types any non-trivial program must handle. + +(The Boost Outcome library is also capable of avoiding such excessive coupling, by passing for the third `P` argument in the `outcome<T, E, P>` template a pointer that erases the exact static type of the object being transported. However, this would require a dynamic memory allocation). + +## Syntax + +The most common check-only use case looks almost identically in LEAF and in Boost Outcome (`tl::expected` lacks a similar macro): + +```c++ +// Outcome +{ + BOOST_OUTCOME_TRY(v, f()); // Check for errors, forward failures to the caller + // If control reaches here, v is the successful result (the call succeeded). +} +``` + +```c++ +// LEAF +{ + BOOST_LEAF_AUTO(v, f()); // Check for errors, forward failures to the caller + // If control reaches here, v is the successful result (the call succeeded). +} +``` + +When we want to handle failures, in Boost Outcome and in `tl::expected`, accessing the error object (which is always stored in the return value) is a simple continuation of the error check: + +```c++ +// Outcome, tl::expected +if( auto r = f() ) +{ + auto v = r.value(); + // No error, use v +} +else +{ // Error! + switch( r.error() ) + { + error_enum::error1: + /* handle error_enum::error1 */ + break; + + error_enum::error2: + /* handle error_enum::error2 */ + break; + + default: + /* handle any other failure */ + } +} +``` + +When using LEAF, we must explicitly state our intention to handle errors, not just check for failures: + +```c++ +// LEAF +leaf::try_handle_all + + []() -> leaf::result<T> + { + BOOST_LEAF_AUTO(v, f()); + // No error, use v + }, + + []( leaf::match<error_enum, error_enum::error1> ) + { + /* handle error_enum::error1 */ + }, + + []( leaf::match<error_enum, error_enum::error2> ) + { + /* handle error_enum::error2 */ + }, + + [] + { + /* handle any other failure */ + } ); +``` + +The use of `try_handle_all` reserves storage on the stack for the error object types being handled (in this case, `error_enum`). If the failure is either `error_enum::error1` or `error_enum::error2`, the matching error handling lambda is invoked. + +## Code generation considerations + +Benchmarking C++ programs is tricky, because we want to prevent the compiler from optimizing out things it shouldn't normally be able to optimize in a real program, yet we don't want to interfere with "legitimate" optimizations. + +The primary approach we use to prevent the compiler from optimizing everything out to nothing is to base all computations on a call to `std::rand()`. + +When benchmarking error handling, it makes sense to measure the time it takes to return a result or error across multiple stack frames. This calls for disabling inlining. + +The technique used to disable inlining in this benchmark is to mark functions with `__attribute__((noinline))`. This is imperfect, because optimizers can still peek into the body of the function and optimize things out, as is seen in this example: + +```c++ +__attribute__((noinline)) int val() {return 42;} + +int main() +{ + return val(); +} +``` + +Which on clang 9 outputs: + +```x86asm +val(): + mov eax, 42 + ret +main: + mov eax, 42 + ret +``` + +It does not appear that anything like this is occurring in our case, but it is still a possibility. + +> NOTES: +> +> - The benchmarks are compiled with exception handling disabled. +> - LEAF is able to work with external `result<>` types. The benchmark uses `leaf::result<T>`. + +## Show me the code! + +The following source: + +```C++ +leaf::result<int> f(); + +leaf::result<int> g() +{ + BOOST_LEAF_AUTO(x, f()); + return x+1; +} +``` + +Generates this code on clang ([Godbolt](https://godbolt.org/z/v58drTPhq)): + +```x86asm +g(): # @g() + push rbx + sub rsp, 32 + mov rbx, rdi + lea rdi, [rsp + 8] + call f() + mov eax, dword ptr [rsp + 24] + mov ecx, eax + and ecx, 3 + cmp ecx, 3 + jne .LBB0_1 + mov eax, dword ptr [rsp + 8] + add eax, 1 + mov dword ptr [rbx], eax + mov eax, 3 + jmp .LBB0_3 +.LBB0_1: + cmp ecx, 2 + jne .LBB0_3 + mov rax, qword ptr [rsp + 8] + mov qword ptr [rbx], rax + mov rax, qword ptr [rsp + 16] + mov qword ptr [rbx + 8], rax + mov eax, 2 +.LBB0_3: + mov dword ptr [rbx + 16], eax + mov rax, rbx + add rsp, 32 + pop rbx + ret +``` + +> Description: +> +> * The happy path can be recognized by the `add eax, 1` instruction generated for `x + 1`. +> +> * `.LBB0_3`: Regular failure; the returned `result<T>` object holds only the `int` discriminant. +> +> * `.LBB0_1`: Failure; the returned `result<T>` holds the `int` discriminant and a `std::shared_ptr<leaf::polymorphic_context>` (used to hold error objects transported from another thread). + +Note that `f` is undefined, hence the `call` instruction. Predictably, if we provide a trivial definition for `f`: + +```C++ +leaf::result<int> f() +{ + return 42; +} + +leaf::result<int> g() +{ + BOOST_LEAF_AUTO(x, f()); + return x+1; +} +``` + +We get: + +```x86asm +g(): # @g() + mov rax, rdi + mov dword ptr [rdi], 43 + mov dword ptr [rdi + 16], 3 + ret +``` + +With a less trivial definition of `f`: + +```C++ +leaf::result<int> f() +{ + if( rand()%2 ) + return 42; + else + return leaf::new_error(); +} + +leaf::result<int> g() +{ + BOOST_LEAF_AUTO(x, f()); + return x+1; +} +``` + +We get ([Godbolt](https://godbolt.org/z/87Kezzrs4)): + +```x86asm +g(): # @g() + push rbx + mov rbx, rdi + call rand + test al, 1 + jne .LBB1_2 + mov eax, 4 + lock xadd dword ptr [rip + boost::leaf::leaf_detail::id_factory<void>::counter], eax + add eax, 4 + mov dword ptr fs:[boost::leaf::leaf_detail::id_factory<void>::current_id@TPOFF], eax + and eax, -4 + or eax, 1 + mov dword ptr [rbx + 16], eax + mov rax, rbx + pop rbx + ret +.LBB1_2: + mov dword ptr [rbx], 43 + mov eax, 3 + mov dword ptr [rbx + 16], eax + mov rax, rbx + pop rbx + ret +``` + +Above, the call to `f()` is inlined: + +* `.LBB1_2`: Success +* The atomic `add` is from the initial error reporting machinery in LEAF, generating a unique error ID for the error being reported. + +## Benchmark matrix dimensions + +The benchmark matrix has 2 dimensions: + +1. Error object type: + + a. The error object transported in case of a failure is of type `e_error_code`, which is a simple `enum`. + + b. The error object transported in case of a failure is of type `struct e_system_error { e_error_code value; std::string what; }`. + + c. The error object transported in case of a failure is of type `e_heavy_payload`, a `struct` of size 4096. + +2. Error rate: 2%, 98% + +Now, transporting a large error object might seem unusual, but this is only because it is impractical to return a large object as *the* return value in case of an error. LEAF has two features that make communicating any, even large error objects, practical: + +* The return type of error neutral functions is not coupled with the error object types that may be reported. This means that in case of a failure, any function can easily contribute any error information it has available. + +* LEAF will only bother with transporting a given error object if an active error handling scope needs it. This means that library functions can and should contribute any and all relevant information when reporting a failure, because if the program doesn't need it, it will simply be discarded. + +## Source code + +[deep_stack_leaf.cpp](deep_stack_leaf.cpp) + +[deep_stack_other.cpp](deep_stack_other.cpp) + +## Godbolt + +Godbolt has built-in support for Boost (Outcome/LEAF), but `tl::expected` both provide a single header, which makes it very easy to use them online as well. To see the generated code for the benchmark program, you can copy and paste the following into Godbolt: + +`leaf::result<T>` ([godbolt](https://godbolt.org/z/1hqqnfhMf)) + +```c++ +#include "https://raw.githubusercontent.com/boostorg/leaf/master/benchmark/deep_stack_leaf.cpp" +``` + +`tl::expected<T, E>` ([godbolt](https://godbolt.org/z/6dfcdsPcc)) + +```c++ +#include "https://raw.githubusercontent.com/TartanLlama/expected/master/include/tl/expected.hpp" +#include "https://raw.githubusercontent.com/boostorg/leaf/master/benchmark/deep_stack_other.cpp" +``` + +`outcome::result<T, E>` ([godbolt](https://godbolt.org/z/jMEfGMrW9)) + +```c++ +#define BENCHMARK_WHAT 1 +#include "https://raw.githubusercontent.com/boostorg/leaf/master/benchmark/deep_stack_other.cpp" +``` + +## Build options + +To build both versions of the benchmark program, the compilers are invoked using the following command line options: + +* `-std=c++17`: Required by other libraries (LEAF only requires C++11); +* `-fno-exceptions`: Disable exception handling; +* `-O3`: Maximum optimizations; +* `-DNDEBUG`: Disable asserts. + +In addition, the LEAF version is compiled with: + +* `-DBOOST_LEAF_CFG_DIAGNOSTICS=0`: Disable diagnostic information for error objects not recognized by the program. This is a debugging feature, see [Configuration Macros](https://boostorg.github.io/leaf/#configuration). + +## Results + +Below is the output the benchmark programs running on an old MacBook Pro. The tables show the elapsed time for 10,000,000 iterations of returning a result across 10 stack frames, depending on the error type and the rate of failures. In addition, the programs generate a `benchmark.csv` file in the current working directory. + +### gcc 9.2.0: + +`leaf::result<T>`: + +Error type | 2% (μs) | 98% (μs) +----------------|---------:|--------: +e_error_code | 594965 | 545882 +e_system_error | 614688 | 1203154 +e_heavy_payload | 736701 | 7397756 + +`tl::expected<T, E>`: + +Error type | 2% (μs) | 98% (μs) +----------------|---------:|--------: +e_error_code | 921410 | 820757 +e_system_error | 670191 | 5593513 +e_heavy_payload | 1331724 | 31560432 + +`outcome::result<T, E>`: + +Error type | 2% (μs) | 98% (μs) +----------------|---------:|--------: +e_error_code | 1080512 | 773206 +e_system_error | 577403 | 1201693 +e_heavy_payload | 13222387 | 32104693 + +`outcome::outcome<T, E>`: + +Error type | 2% (μs) | 98% (μs) +----------------|---------:|--------: +e_error_code | 832916 | 1170731 +e_system_error | 947298 | 2330392 +e_heavy_payload | 13342292 | 33837583 + +### clang 11.0.0: + +`leaf::result<T>`: + +Error type | 2% (μs) | 98% (μs) +----------------|---------:|--------: +e_error_code | 570847 | 493538 +e_system_error | 592685 | 982799 +e_heavy_payload | 713966 | 5144523 + +`tl::expected<T, E>`: + +Error type | 2% (μs) | 98% (μs) +----------------|---------:|--------: +e_error_code | 461639 | 312849 +e_system_error | 620479 | 3534689 +e_heavy_payload | 1037434 | 16078669 + +`outcome::result<T, E>`: + +Error type | 2% (μs) | 98% (μs) +----------------|---------:|--------: +e_error_code | 431219 | 446854 +e_system_error | 589456 | 1712739 +e_heavy_payload | 12387405 | 16216894 + +`outcome::outcome<T, E>`: + +Error type | 2% (μs) | 98% (μs) +----------------|---------:|--------: +e_error_code | 711412 | 1477505 +e_system_error | 835691 | 2374919 +e_heavy_payload | 13289404 | 29785353 + +### msvc 19.24.28314: + +`leaf::result<T>`: + +Error type | 2% (μs) | 98% (μs) +----------------|---------:|--------: +e_error_code | 1205327 | 1449117 +e_system_error | 1290277 | 2332414 +e_heavy_payload | 1503103 | 13682308 + +`tl::expected<T, E>`: + +Error type | 2% (μs) | 98% (μs) +----------------|---------:|--------: +e_error_code | 938839 | 867296 +e_system_error | 1455627 | 8943881 +e_heavy_payload | 2637494 | 49212901 + +`outcome::result<T, E>`: + +Error type | 2% (μs) | 98% (μs) +----------------|---------:|--------: +e_error_code | 935331 | 1202475 +e_system_error | 1228944 | 2269680 +e_heavy_payload | 15239084 | 55618460 + +`outcome::outcome<T, E>`: + +Error type | 2% (μs) | 98% (μs) +----------------|---------:|--------: +e_error_code | 1472035 | 2529057 +e_system_error | 1997971 | 4004965 +e_heavy_payload | 16027423 | 64572924 + +## Charts + +The charts below are generated from the results from the previous section, converted from elapsed time in microseconds to millions of calls per second. + +### gcc 9.2.0: + +![](gcc_e_error_code.png) + +![](gcc_e_system_error.png) + +![](gcc_e_heavy_payload.png) + +### clang 11.0.0: + +![](clang_e_error_code.png) + +![](clang_e_system_error.png) + +![](clang_e_heavy_payload.png) + +### msvc 19.24.28314: + +![](msvc_e_error_code.png) + +![](msvc_e_system_error.png) + +![](msvc_e_heavy_payload.png) + +## Thanks + +Thanks for the valuable feedback: Peter Dimov, Glen Fernandes, Sorin Fetche, Niall Douglas, Ben Craig, Vinnie Falco, Jason Dictos diff --git a/src/boost/libs/leaf/benchmark/clang_e_error_code.png b/src/boost/libs/leaf/benchmark/clang_e_error_code.png Binary files differnew file mode 100644 index 000000000..94a5ee6df --- /dev/null +++ b/src/boost/libs/leaf/benchmark/clang_e_error_code.png diff --git a/src/boost/libs/leaf/benchmark/clang_e_heavy_payload.png b/src/boost/libs/leaf/benchmark/clang_e_heavy_payload.png Binary files differnew file mode 100644 index 000000000..88004d5f0 --- /dev/null +++ b/src/boost/libs/leaf/benchmark/clang_e_heavy_payload.png diff --git a/src/boost/libs/leaf/benchmark/clang_e_system_error.png b/src/boost/libs/leaf/benchmark/clang_e_system_error.png Binary files differnew file mode 100644 index 000000000..267f4ec97 --- /dev/null +++ b/src/boost/libs/leaf/benchmark/clang_e_system_error.png diff --git a/src/boost/libs/leaf/benchmark/deep_stack_leaf.cpp b/src/boost/libs/leaf/benchmark/deep_stack_leaf.cpp new file mode 100644 index 000000000..1c5676a71 --- /dev/null +++ b/src/boost/libs/leaf/benchmark/deep_stack_leaf.cpp @@ -0,0 +1,276 @@ +// 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) + +// See benchmark.md + +#include <boost/leaf.hpp> + +#ifndef BOOST_LEAF_NO_EXCEPTIONS +# error Please disable exception handling. +#endif + +#if BOOST_LEAF_CFG_DIAGNOSTICS +# error Please disable diagnostics. +#endif + +#ifdef _MSC_VER +# define NOINLINE __declspec(noinline) +# define ALWAYS_INLINE __forceinline +#else +# define NOINLINE __attribute__((noinline)) +# define ALWAYS_INLINE __attribute__((always_inline)) inline +#endif + +#include <cstring> +#include <cstdlib> +#include <cassert> +#include <chrono> +#include <iostream> +#include <fstream> +#include <iomanip> +#include <numeric> +#include <algorithm> +#include <system_error> +#include <array> + +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); + } +} + +////////////////////////////////////// + +namespace leaf = boost::leaf; + +#define USING_RESULT_TYPE "leaf::result<T>" + +////////////////////////////////////// + +enum class e_error_code +{ + ec0, ec1, ec2, ec3 +}; + +struct e_system_error +{ + int value; + std::string what; +}; + +struct e_heavy_payload +{ + std::array<char, 4096> value; +}; + +template <class E> +leaf::error_id make_error() noexcept; + +template <> +inline leaf::error_id make_error<e_error_code>() noexcept +{ + switch(std::rand()%4) + { + default: return leaf::new_error(e_error_code::ec0); + case 1: return leaf::new_error(e_error_code::ec1); + case 2: return leaf::new_error(e_error_code::ec2); + case 3: return leaf::new_error(e_error_code::ec3); + } +} + +template <> +inline leaf::error_id make_error<std::error_code>() noexcept +{ + return std::error_code(std::rand(), std::system_category()); +} + +template <> +inline leaf::error_id make_error<e_system_error>() noexcept +{ + return leaf::new_error( e_system_error { std::rand(), std::string(std::rand()%32, ' ') } ); +} + +template <> +inline leaf::error_id make_error<e_heavy_payload>() noexcept +{ + e_heavy_payload e; + std::fill(e.value.begin(), e.value.end(), std::rand()); + return leaf::new_error(e); +} + +inline bool should_fail( int failure_rate ) noexcept +{ + assert(failure_rate>=0); + assert(failure_rate<=100); + return (std::rand()%100) < failure_rate; +} + +inline int handle_error( e_error_code e ) noexcept +{ + return int(e); +} + +inline int handle_error( std::error_code const & e ) noexcept +{ + return e.value(); +} + +inline int handle_error( e_system_error const & e ) noexcept +{ + return e.value + e.what.size(); +} + +inline int handle_error( e_heavy_payload const & e ) noexcept +{ + return std::accumulate(e.value.begin(), e.value.end(), 0); +} + +////////////////////////////////////// + +// This is used to change the "success" type at each level. +// Generally, functions return values of different types. +template <int N, class E, bool Odd = N%2> +struct select_result_type; + +template <int N, class E> +struct select_result_type<N, E, true> +{ + using type = leaf::result<int>; // Does not depend on E +}; + +template <int N, class E> +struct select_result_type<N, E, false> +{ + using type = leaf::result<float>; // Does not depend on E +}; + +template <int N, class E> +using select_result_t = typename select_result_type<N, E>::type; + +////////////////////////////////////// + +template <int N, class E> +struct benchmark +{ + using e_type = E; + + NOINLINE static select_result_t<N, E> f( int failure_rate ) noexcept + { + BOOST_LEAF_AUTO(x, (benchmark<N-1, E>::f(failure_rate))); + return x+1; + } +}; + +template <class E> +struct benchmark<1, E> +{ + using e_type = E; + + NOINLINE static select_result_t<1, E> f( int failure_rate ) noexcept + { + if( should_fail(failure_rate) ) + return make_error<E>(); + else + return std::rand(); + } +}; + +////////////////////////////////////// + +template <class Benchmark> +NOINLINE int runner( int failure_rate ) noexcept +{ + return leaf::try_handle_all( + [=] + { + return Benchmark::f(failure_rate); + }, + []( typename Benchmark::e_type const & e ) + { + return handle_error(e); + }, + [] + { + return -1; + } ); +} + +////////////////////////////////////// + +std::fstream append_csv() +{ + if( FILE * f = fopen("benchmark.csv","rb") ) + { + fclose(f); + return std::fstream("benchmark.csv", std::fstream::out | std::fstream::app); + } + else + { + std::fstream fs("benchmark.csv", std::fstream::out | std::fstream::app); + fs << "\"Result Type\",2%,98%\n"; + return fs; + } +} + +template <class F> +int print_elapsed_time( int iteration_count, F && f ) +{ + auto start = std::chrono::steady_clock::now(); + int val = 0; + for( int i = 0; i!=iteration_count; ++i ) + val += std::forward<F>(f)(); + auto stop = std::chrono::steady_clock::now(); + int elapsed = std::chrono::duration_cast<std::chrono::microseconds>(stop-start).count(); + std::cout << std::right << std::setw(9) << elapsed; + append_csv() << ',' << elapsed; + return val; +} + +////////////////////////////////////// + +template <int Depth, class E> +int benchmark_type( char const * type_name, int iteration_count ) +{ + int x=0; + append_csv() << "\"" USING_RESULT_TYPE "\""; + std::cout << '\n' << std::left << std::setw(16) << type_name << '|'; + std::srand(0); + x += print_elapsed_time( iteration_count, [] { return runner<benchmark<Depth, E>>(2); } ); + std::cout << " |"; + std::srand(0); + x += print_elapsed_time( iteration_count, [] { return runner<benchmark<Depth, E>>(98); } ); + append_csv() << '\n'; + return x; +} + +////////////////////////////////////// + +int main() +{ + int const depth = 10; + int const iteration_count = 10000000; + std::cout << + iteration_count << " iterations, call depth " << depth << ", sizeof(e_heavy_payload) = " << sizeof(e_heavy_payload) << "\n" + USING_RESULT_TYPE "\n" + "Error type | 2% (μs) | 98% (μs)\n" + "----------------|----------|---------"; + int r = 0; + r += benchmark_type<depth, e_error_code>("e_error_code", iteration_count); + r += benchmark_type<depth, std::error_code>("std::error_code", iteration_count); + r += benchmark_type<depth, e_system_error>("e_system_error", iteration_count); + r += benchmark_type<depth, e_heavy_payload>("e_heavy_payload", iteration_count); + std::cout << '\n'; + // std::cout << std::rand() << '\n'; + return r; +} diff --git a/src/boost/libs/leaf/benchmark/deep_stack_other.cpp b/src/boost/libs/leaf/benchmark/deep_stack_other.cpp new file mode 100644 index 000000000..aa61f67f2 --- /dev/null +++ b/src/boost/libs/leaf/benchmark/deep_stack_other.cpp @@ -0,0 +1,308 @@ +// 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) + +// See benchmark.md + +#ifndef BENCHMARK_WHAT +# define BENCHMARK_WHAT 0 +#endif + +#if BENCHMARK_WHAT == 0 + +# ifndef TL_EXPECTED_HPP +# include "tl/expected.hpp" +# endif +# define BENCHMARK_SUCCESS(e) e +# define BENCHMARK_FAILURE(e) tl::make_unexpected(e) +# define BENCHMARK_TRY(v,r)\ + auto && _r_##v = r;\ + if( !_r_##v )\ + return BENCHMARK_FAILURE(_r_##v.error());\ + auto && v = _r_##v.value() + +#else + +# include <boost/outcome/std_outcome.hpp> +# include <boost/outcome/try.hpp> +# define BENCHMARK_SUCCESS(e) boost::outcome_v2::success(e) +# define BENCHMARK_FAILURE(e) boost::outcome_v2::failure(e) +# define BENCHMARK_TRY BOOST_OUTCOME_TRY +# ifndef BOOST_NO_EXCEPTIONS +# error Please disable exception handling. +# endif + +#endif + +#ifdef _MSC_VER +# define NOINLINE __declspec(noinline) +# define ALWAYS_INLINE __forceinline +#else +# define NOINLINE __attribute__((noinline)) +# define ALWAYS_INLINE __attribute__((always_inline)) inline +#endif + +#include <cstring> +#include <cstdlib> +#include <cassert> +#include <chrono> +#include <iostream> +#include <fstream> +#include <iomanip> +#include <numeric> +#include <algorithm> +#include <system_error> +#include <array> + +namespace boost +{ + void throw_exception( std::exception const & e ) + { + std::cerr << "Terminating due to a C++ exception under BOOST_NO_EXCEPTIONS: " << e.what(); + std::terminate(); + } + + struct source_location; + void throw_exception( std::exception const & e, boost::source_location const & ) + { + throw_exception(e); + } +} + +////////////////////////////////////// + +#if BENCHMARK_WHAT == 0 // tl::expected + +# define USING_RESULT_TYPE "tl::expected<T, E>" + + template <class T, class E> + using result = tl::expected<T, E>; + +#elif BENCHMARK_WHAT == 1 // outcome::result + +# define USING_RESULT_TYPE "outcome::result<T, E>" + + template <class T, class E> + using result = boost::outcome_v2::std_result<T, E, boost::outcome_v2::policy::terminate>; + +#elif BENCHMARK_WHAT == 2 // outcome::outcome + +# define USING_RESULT_TYPE "outcome::outcome<T, E>" + + template <class T, class E> + using result = boost::outcome_v2::std_outcome<T, E>; + +#else +# error Benchmark what? +#endif + +////////////////////////////////////// + +enum class e_error_code +{ + ec0, ec1, ec2, ec3 +}; + +struct e_system_error +{ + int value; + std::string what; +}; + +struct e_heavy_payload +{ + std::array<char, 4096> value; +}; + +template <class E> +E make_error() noexcept; + +template <> +inline e_error_code make_error<e_error_code>() noexcept +{ + switch(std::rand()%4) + { + default: return e_error_code::ec0; + case 1: return e_error_code::ec1; + case 2: return e_error_code::ec2; + case 3: return e_error_code::ec3; + } +} + +template <> +inline std::error_code make_error<std::error_code>() noexcept +{ + return std::error_code(std::rand(), std::system_category()); +} + +template <> +inline e_system_error make_error<e_system_error>() noexcept +{ + return { std::rand(), std::string(std::rand()%32, ' ') }; +} + +template <> +inline e_heavy_payload make_error<e_heavy_payload>() noexcept +{ + e_heavy_payload e; + std::fill(e.value.begin(), e.value.end(), std::rand()); + return e; +} + +inline bool should_fail( int failure_rate ) noexcept +{ + assert(failure_rate>=0); + assert(failure_rate<=100); + return (std::rand()%100) < failure_rate; +} + +inline int handle_error( e_error_code e ) noexcept +{ + return int(e); +} + +inline int handle_error( std::error_code const & e ) noexcept +{ + return e.value(); +} + +inline int handle_error( e_system_error const & e ) noexcept +{ + return e.value + e.what.size(); +} + +inline int handle_error( e_heavy_payload const & e ) noexcept +{ + return std::accumulate(e.value.begin(), e.value.end(), 0); +} + +////////////////////////////////////// + +// This is used to change the "success" type at each level. +// Generally, functions return values of different types. +template <int N, class E, bool Odd = N%2> +struct select_result_type; + +template <int N, class E> +struct select_result_type<N, E, true> +{ + using type = result<int, E>; +}; + +template <int N, class E> +struct select_result_type<N, E, false> +{ + using type = result<float, E>; +}; + +template <int N, class E> +using select_result_t = typename select_result_type<N, E>::type; + +////////////////////////////////////// + +template <int N, class E> +struct benchmark +{ + using e_type = E; + + NOINLINE static select_result_t<N, E> f( int failure_rate ) noexcept + { + BENCHMARK_TRY(x, (benchmark<N-1, E>::f(failure_rate))); + return BENCHMARK_SUCCESS(x+1); + } +}; + +template <class E> +struct benchmark<1, E> +{ + using e_type = E; + + NOINLINE static select_result_t<1, E> f( int failure_rate ) noexcept + { + if( should_fail(failure_rate) ) + return BENCHMARK_FAILURE(make_error<E>()); + else + return BENCHMARK_SUCCESS(std::rand()); + } +}; + +////////////////////////////////////// + +template <class Benchmark> +NOINLINE int runner( int failure_rate ) noexcept +{ + if( auto r = Benchmark::f(failure_rate) ) + return r.value(); + else + return handle_error(r.error()); +} + +////////////////////////////////////// + +std::fstream append_csv() +{ + if( FILE * f = fopen("benchmark.csv","rb") ) + { + fclose(f); + return std::fstream("benchmark.csv", std::fstream::out | std::fstream::app); + } + else + { + std::fstream fs("benchmark.csv", std::fstream::out | std::fstream::app); + fs << "\"Result Type\",2%,98%\n"; + return fs; + } +} + +template <class F> +int print_elapsed_time( int iteration_count, F && f ) +{ + auto start = std::chrono::steady_clock::now(); + int val = 0; + for( int i = 0; i!=iteration_count; ++i ) + val += std::forward<F>(f)(); + auto stop = std::chrono::steady_clock::now(); + int elapsed = std::chrono::duration_cast<std::chrono::microseconds>(stop-start).count(); + std::cout << std::right << std::setw(9) << elapsed; + append_csv() << ',' << elapsed; + return val; +} + +////////////////////////////////////// + +template <int Depth, class E> +int benchmark_type( char const * type_name, int iteration_count ) +{ + int x=0; + append_csv() << "\"" USING_RESULT_TYPE "\""; + std::cout << '\n' << std::left << std::setw(16) << type_name << '|'; + std::srand(0); + x += print_elapsed_time( iteration_count, [] { return runner<benchmark<Depth, E>>(2); } ); + std::cout << " |"; + std::srand(0); + x += print_elapsed_time( iteration_count, [] { return runner<benchmark<Depth, E>>(98); } ); + append_csv() << '\n'; + return x; +} + +////////////////////////////////////// + +int main() +{ + int const depth = 10; + int const iteration_count = 10000000; + std::cout << + iteration_count << " iterations, call depth " << depth << ", sizeof(e_heavy_payload) = " << sizeof(e_heavy_payload) << "\n" + USING_RESULT_TYPE "\n" + "Error type | 2% (μs) | 98% (μs)\n" + "----------------|----------|---------"; + int r = 0; + r += benchmark_type<depth, e_error_code>("e_error_code", iteration_count); + r += benchmark_type<depth, std::error_code>("std::error_code", iteration_count); + r += benchmark_type<depth, e_system_error>("e_system_error", iteration_count); + r += benchmark_type<depth, e_heavy_payload>("e_heavy_payload", iteration_count); + std::cout << '\n'; + // std::cout << std::rand() << '\n'; + return r; +} diff --git a/src/boost/libs/leaf/benchmark/gcc_e_error_code.png b/src/boost/libs/leaf/benchmark/gcc_e_error_code.png Binary files differnew file mode 100644 index 000000000..48a777ab5 --- /dev/null +++ b/src/boost/libs/leaf/benchmark/gcc_e_error_code.png diff --git a/src/boost/libs/leaf/benchmark/gcc_e_heavy_payload.png b/src/boost/libs/leaf/benchmark/gcc_e_heavy_payload.png Binary files differnew file mode 100644 index 000000000..4c19bd9f9 --- /dev/null +++ b/src/boost/libs/leaf/benchmark/gcc_e_heavy_payload.png diff --git a/src/boost/libs/leaf/benchmark/gcc_e_system_error.png b/src/boost/libs/leaf/benchmark/gcc_e_system_error.png Binary files differnew file mode 100644 index 000000000..255fc9d10 --- /dev/null +++ b/src/boost/libs/leaf/benchmark/gcc_e_system_error.png diff --git a/src/boost/libs/leaf/benchmark/msvc_e_error_code.png b/src/boost/libs/leaf/benchmark/msvc_e_error_code.png Binary files differnew file mode 100644 index 000000000..20962cc7e --- /dev/null +++ b/src/boost/libs/leaf/benchmark/msvc_e_error_code.png diff --git a/src/boost/libs/leaf/benchmark/msvc_e_heavy_payload.png b/src/boost/libs/leaf/benchmark/msvc_e_heavy_payload.png Binary files differnew file mode 100644 index 000000000..e5d10844c --- /dev/null +++ b/src/boost/libs/leaf/benchmark/msvc_e_heavy_payload.png diff --git a/src/boost/libs/leaf/benchmark/msvc_e_system_error.png b/src/boost/libs/leaf/benchmark/msvc_e_system_error.png Binary files differnew file mode 100644 index 000000000..edd70fae5 --- /dev/null +++ b/src/boost/libs/leaf/benchmark/msvc_e_system_error.png 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. diff --git a/src/boost/libs/leaf/gen/generate_single_header.py b/src/boost/libs/leaf/gen/generate_single_header.py new file mode 100644 index 000000000..623fddfbe --- /dev/null +++ b/src/boost/libs/leaf/gen/generate_single_header.py @@ -0,0 +1,92 @@ +""" + + Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + Copyright (c) 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) + + This program generates a single header file from a file including multiple C/C++ headers. + + Usage: + + python3 generate_single_header.py --help + + e.g. python3 generate_single_header.py -i include/boost/leaf/detail/all.hpp -p include -o test/leaf.hpp boost/leaf + +""" + +import argparse +import os +import re +from datetime import date +import subprocess + +included = {} +total_line_count = 11 + +def append(input_file_name, input_file, output_file, regex_includes, include_folder): + global total_line_count + line_count = 1 + for line in input_file: + line_count += 1 + result = regex_includes.search(line) + if result: + next_input_file_name = result.group("include") + if next_input_file_name in included: + output_file.write("// Expanded at line %d: %s" % (included[next_input_file_name], line)) + total_line_count += 1 + else: + included[next_input_file_name] = total_line_count + print("%s (%d)" % (next_input_file_name, total_line_count)) + with open(os.path.join(include_folder, next_input_file_name), "r") as next_input_file: + output_file.write('// >>> %s#line 1 "%s"\n' % (line, next_input_file_name)) + total_line_count += 2 + append(next_input_file_name, next_input_file, output_file, regex_includes, include_folder) + if not ('include/boost/leaf/detail/all.hpp' in input_file_name): + output_file.write('// <<< %s#line %d "%s"\n' % (line, line_count, input_file_name)) + total_line_count += 2 + else: + output_file.write(line) + total_line_count += 1 + +def _main(): + parser = argparse.ArgumentParser( + description="Generates a single include file from a file including multiple C/C++ headers") + parser.add_argument("-i", "--input", action="store", type=str, default="in.cpp", + help="Input file including the headers to be merged") + parser.add_argument("-o", "--output", action="store", type=str, default="out.cpp", + help="Output file. NOTE: It will be overwritten!") + parser.add_argument("-p", "--path", action="store", type=str, default=".", + help="Include path") + parser.add_argument("--hash", action="store", type=str, + help="The git hash to print in the output file, e.g. the output of \"git rev-parse HEAD\"") + parser.add_argument("prefix", action="store", type=str, + help="Non-empty include file prefix (e.g. a/b)") + args = parser.parse_args() + + regex_includes = re.compile(r"""^\s*#[\t\s]*include[\t\s]*("|\<)(?P<include>%s.*)("|\>)""" % args.prefix) + print("Rebuilding %s:" % args.input) + with open(args.output, 'w') as output_file, open(args.input, 'r') as input_file: + output_file.write( + '#ifndef BOOST_LEAF_HPP_INCLUDED\n' + '#define BOOST_LEAF_HPP_INCLUDED\n' + '\n' + '// LEAF single header distribution. Do not edit.\n' + '\n' + '// Generated on ' + date.today().strftime("%m/%d/%Y")) + if args.hash: + output_file.write( + ' from https://github.com/boostorg/leaf/tree/' + args.hash[0:7]) + output_file.write( + '.\n' + '// Latest version of this file: https://raw.githubusercontent.com/boostorg/leaf/gh-pages/leaf.hpp.\n' + '\n') + append(args.input, input_file, output_file, regex_includes, args.path) + output_file.write( + '\n' + '#endif\n' ) +# print("%d" % total_line_count) + +if __name__ == "__main__": + _main() diff --git a/src/boost/libs/leaf/index.html b/src/boost/libs/leaf/index.html new file mode 100644 index 000000000..9eb9f0a15 --- /dev/null +++ b/src/boost/libs/leaf/index.html @@ -0,0 +1,15 @@ +<html> +<head> +<meta http-equiv="refresh" content="0; URL=doc/html/index.html"> +</head> +<body> +Automatic redirection failed, please go to +<a href="doc/html/index.html">doc/html/index.html</a>. +</body> +</html> +<!-- + � Copyright Beman Dawes, 2001 + 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 +--> diff --git a/src/boost/libs/leaf/meson.build b/src/boost/libs/leaf/meson.build new file mode 100644 index 000000000..0ac746f50 --- /dev/null +++ b/src/boost/libs/leaf/meson.build @@ -0,0 +1,293 @@ +# 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) + +project('leaf', 'cpp', default_options : ['cpp_std=c++17', 'b_pch=false'], license : 'boost') + +option_leaf_hpp = get_option('leaf_hpp') +option_boost = get_option('leaf_boost_examples') +option_lua = get_option('leaf_lua_examples') +option_diagnostics = get_option('leaf_diagnostics') +option_exceptions = (get_option('cpp_eh')!='none') +option_enable_unit_tests = get_option('leaf_enable_unit_tests') +option_enable_examples = get_option('leaf_enable_examples') +option_enable_benchmarks = get_option('leaf_enable_benchmarks') +option_embedded = get_option('leaf_embedded') + +if not option_enable_examples + if option_boost + error('The option leaf_boost_examples requires leaf_enable_examples. Aborting.') + endif + if option_lua + error('The option leaf_lua_examples requires leaf_enable_examples. Aborting.') + endif +endif + +compiler = meson.get_compiler('cpp') +compiler_id = compiler.get_id() +if not meson.is_subproject() + if option_boost + add_global_arguments( + '-DBOOST_LEAF_BOOST_AVAILABLE', + language:'cpp' ) + endif + if compiler_id=='clang' + if get_option('buildtype')!='debug' + add_global_arguments( + '-Wno-unused-variable', + language:'cpp' ) + endif + add_global_arguments( + '-fdiagnostics-absolute-paths', + '-Wno-dangling-else', + '-Wno-non-virtual-dtor', + '-Wno-delete-non-abstract-non-virtual-dtor', + language:'cpp' ) + elif compiler_id=='gcc' + if get_option('buildtype')!='debug' + add_global_arguments( + '-Wno-unused-variable', + language:'cpp' ) + endif + add_global_arguments( + '-Wno-dangling-else', + '-Wno-non-virtual-dtor', + '-Wno-misleading-indentation', + language:'cpp' ) + elif host_machine.system()=='emscripten' + add_global_arguments( + '-s', 'WASM=1', + '-s', 'USE_PTHREADS=1', + '-s', 'EXIT_RUNTIME=1', + '-s', 'PROXY_TO_PTHREAD=1', + '-s', 'DISABLE_EXCEPTION_CATCHING=0', + language:'cpp' ) + add_global_link_arguments( + '-s', 'EXPORT_ALL=1', + '-s', 'WASM=1', + '-s', 'USE_PTHREADS=1', + '-s', 'EXIT_RUNTIME=1', + '-s', 'PROXY_TO_PTHREAD=1', + '-s', 'DISABLE_EXCEPTION_CATCHING=0', + '-s', 'INITIAL_MEMORY=268435456', + language:'cpp' ) + endif +endif + +dep_boost = [ ] +if option_boost # Requires that LEAF resides under boost_root/libs/leaf. + dep_boost = declare_dependency(include_directories: '../..') +endif + +dep_lua = [ ] +if option_lua + dep_lua = subproject('lua').get_variable('all') +endif + +defines = [ '-DBOOST_LEAF_CFG_DIAGNOSTICS=' + option_diagnostics.to_string() ] + +if option_embedded + defines += '-DBOOST_LEAF_EMBEDDED' +endif + +dep_thread = dependency('threads') + +leaf = declare_dependency( include_directories: 'include', compile_args: defines ) + +################################# + +if option_enable_unit_tests + + tests = [ + 'accumulate_basic_test', + 'accumulate_nested_error_exception_test', + 'accumulate_nested_error_result_test', + 'accumulate_nested_new_error_exception_test', + 'accumulate_nested_new_error_result_test', + 'accumulate_nested_success_exception_test', + 'accumulate_nested_success_result_test', + 'BOOST_LEAF_AUTO_test', + 'BOOST_LEAF_ASSIGN_test', + 'BOOST_LEAF_CHECK_test', + 'capture_exception_async_test', + 'capture_exception_result_async_test', + 'capture_exception_state_test', + 'capture_exception_unload_test', + 'capture_result_async_test', + 'capture_result_state_test', + 'context_activator_test', + 'context_deduction_test', + 'capture_result_unload_test', + 'ctx_handle_all_test', + 'ctx_handle_some_test', + 'ctx_remote_handle_all_test', + 'ctx_remote_handle_some_test', + 'defer_basic_test', + 'defer_nested_error_exception_test', + 'defer_nested_error_result_test', + 'defer_nested_new_error_exception_test', + 'defer_nested_new_error_result_test', + 'defer_nested_success_exception_test', + 'defer_nested_success_result_test', + 'diagnostic_info_test', + 'diagnostic_info_test2', + 'e_errno_test', + 'e_LastError_test', + 'error_code_test', + 'error_id_test', + 'exception_test', + 'exception_to_result_test', + 'function_traits_test', + 'handle_all_other_result_test', + 'handle_all_test', + 'handle_basic_test', + 'handle_some_other_result_test', + 'handle_some_test', + 'match_test', + 'match_member_test', + 'match_value_test', + 'multiple_errors_test', + 'optional_test', + 'preload_basic_test', + 'preload_exception_test', + 'preload_nested_error_exception_test', + 'preload_nested_error_result_test', + 'preload_nested_new_error_exception_test', + 'preload_nested_new_error_result_test', + 'preload_nested_success_exception_test', + 'preload_nested_success_result_test', + 'print_test', + 'result_bad_result_test', + 'result_implicit_conversion_test', + 'result_load_test', + 'result_ref_test', + 'result_state_test', + 'tls_array_alloc_test1', + 'tls_array_alloc_test2', + 'tls_array_alloc_test3', + 'tls_array_test', + 'to_variant_test', + 'try_catch_error_id_test', + 'try_catch_system_error_test', + 'try_catch_test', + 'try_exception_and_result_test', + ] + if option_boost and option_exceptions + tests += [ + 'boost_exception_test' + ] + endif + + dep_test_single_header = [] + if option_leaf_hpp + dep_test_single_header = declare_dependency(compile_args: ['-DBOOST_LEAF_TEST_SINGLE_HEADER']) + endif + + foreach t : tests + test(t, executable(t, 'test/'+t+'.cpp', dependencies: [leaf, dep_thread, dep_boost, dep_test_single_header]) ) + endforeach + + header_tests = [ + '_hpp_capture_test', + '_hpp_common_test', + '_hpp_config_test', + '_hpp_context_test', + '_hpp_error_test', + '_hpp_exception_test', + '_hpp_handle_errors_test', + '_hpp_on_error_test', + '_hpp_pred_test', + '_hpp_result_test', + '_hpp_to_variant_test', + '_hpp_leaf_test', + ] + foreach t : header_tests + test(t, executable(t, 'test/'+t+'.cpp', dependencies: [leaf, dep_thread, dep_boost]) ) + endforeach + +endif + +################################# + +if option_enable_examples + + print_file_examples = [ + 'print_file_result' + ] + if option_exceptions + print_file_examples += [ + 'print_file_eh' + ] + endif + if option_boost + print_file_examples += [ + 'print_file_outcome_result' + ] + endif + + foreach e : print_file_examples + executable(e, 'example/print_file/'+e+'.cpp', dependencies: [leaf, dep_thread, dep_boost] ) + endforeach + +endif + +################################# + +if option_enable_examples + + examples = [ + 'capture_in_result', + 'error_log', + 'error_trace', + 'print_half' + ] + if option_exceptions + examples += [ + 'capture_in_exception', + 'exception_to_result' + ] + if option_lua + examples += [ + 'lua_callback_eh' + ] + endif + if option_boost + examples += [ + # 'asio_beast_leaf_rpc' #FIXME + ] + endif + endif + if option_lua + examples += [ + 'lua_callback_result' + ] + endif + + foreach e : examples + executable(e, 'example/'+e+'.cpp', dependencies: [leaf, dep_thread, dep_boost, dep_lua] ) + endforeach + +endif + +################################# + +if option_enable_benchmarks + + if get_option('optimization')=='0' + error('The option leaf_enable_benchmarks requires optimizations to be enabled. Aborting.') + endif + + if option_exceptions + error('The option leaf_enable_benchmarks requires the built-in option cpp_eh set to none. Aborting.') + endif + + dep_tl_expected = subproject('tl_expected').get_variable('headers') + executable('deep_stack_tl', 'benchmark/deep_stack_other.cpp', override_options: ['cpp_std=c++17'], cpp_args: '-DBENCHMARK_WHAT=0', dependencies: [dep_tl_expected] ) + executable('deep_stack_leaf', 'benchmark/deep_stack_leaf.cpp', dependencies: [leaf], override_options: ['cpp_std=c++17'], cpp_args: '-DBOOST_LEAF_CFG_DIAGNOSTICS=0') + if option_boost + executable('deep_stack_result', 'benchmark/deep_stack_other.cpp', dependencies: [dep_boost], override_options: ['cpp_std=c++17'], cpp_args: '-DBENCHMARK_WHAT=1' ) + executable('deep_stack_outcome', 'benchmark/deep_stack_other.cpp', dependencies: [dep_boost], override_options: ['cpp_std=c++17'], cpp_args: '-DBENCHMARK_WHAT=2' ) + endif + +endif diff --git a/src/boost/libs/leaf/meson_options.txt b/src/boost/libs/leaf/meson_options.txt new file mode 100644 index 000000000..66ddd1a88 --- /dev/null +++ b/src/boost/libs/leaf/meson_options.txt @@ -0,0 +1,8 @@ +option('leaf_hpp',type:'boolean',value:false,description:'Unit tests #include <boost/leaf.hpp> instead of individual headers') +option('leaf_boost_examples',type:'boolean',value:false,description:'Builds the Boost examples and Boost Outcome benchmark') +option('leaf_lua_examples',type:'boolean',value:false,description:'Enable or disable downloading of Lua and building the Lua examples') +option('leaf_diagnostics',type:'integer',value:1,description:'BOOST_LEAF_CFG_DIAGNOSTICS value') +option('leaf_embedded',type:'boolean',value:false,description:'Defines BOOST_LEAF_EMBEDDED') +option('leaf_enable_unit_tests',type:'boolean',value:true,description:'Enable the building of unit test programs') +option('leaf_enable_examples',type:'boolean',value:true,description:'Enable the building of example programs') +option('leaf_enable_benchmarks',type:'boolean',value:false,description:'Enable the building of benchmark programs') diff --git a/src/boost/libs/leaf/meta/libraries.json b/src/boost/libs/leaf/meta/libraries.json new file mode 100644 index 000000000..55ceebf53 --- /dev/null +++ b/src/boost/libs/leaf/meta/libraries.json @@ -0,0 +1,15 @@ +{ + "key": "leaf", + "name": "LEAF", + "authors": [ + "Emil Dotchevski" + ], + "maintainers": [ + "Emil Dotchevski" + ], + "description": "A lightweight error handling library for C++11.", + "category": [ + "Error-handling" + ], + "cxxstd": "11" +} diff --git a/src/boost/libs/leaf/subprojects/lua.wrap b/src/boost/libs/leaf/subprojects/lua.wrap new file mode 100644 index 000000000..0dedaa874 --- /dev/null +++ b/src/boost/libs/leaf/subprojects/lua.wrap @@ -0,0 +1,10 @@ +[wrap-file] +directory=lua-5.1.5 + +source_url = http://www.lua.org/ftp/lua-5.1.5.tar.gz +source_filename = lua-5.1.5.tar.gz +source_hash = 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333 + +patch_url = https://github.com/zajo/meson_wraps/blob/master/lua/subprojects/lua.tar.gz?raw=true +patch_filename = lua.tar.gz +patch_hash = 51cd288c6ce32cd338e39cf9a1343dcb2ea9f05293055c96dcce87781d3b0afb
\ No newline at end of file diff --git a/src/boost/libs/leaf/subprojects/tl_expected.wrap b/src/boost/libs/leaf/subprojects/tl_expected.wrap new file mode 100644 index 000000000..f4de3ca04 --- /dev/null +++ b/src/boost/libs/leaf/subprojects/tl_expected.wrap @@ -0,0 +1,3 @@ +[wrap-git] +url = https://github.com/zajo/expected.git +revision = head diff --git a/src/boost/libs/leaf/test/BOOST_LEAF_ASSIGN_test.cpp b/src/boost/libs/leaf/test/BOOST_LEAF_ASSIGN_test.cpp new file mode 100644 index 000000000..2724636bc --- /dev/null +++ b/src/boost/libs/leaf/test/BOOST_LEAF_ASSIGN_test.cpp @@ -0,0 +1,88 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/result.hpp> +# include <boost/leaf/handle_errors.hpp> +#endif + +#include "lightweight_test.hpp" +#ifdef BOOST_LEAF_BOOST_AVAILABLE +# include <boost/config/workaround.hpp> +#else +# define BOOST_WORKAROUND(a,b) 0 +#endif + +namespace leaf = boost::leaf; + +struct value +{ + int x; + + explicit value( int x ): x(x) { }; + +#ifndef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS + value( value const & ) = delete; + value( value && ) = default; +#endif +}; + +leaf::result<value> f1() +{ + return value { 21 }; +} + +leaf::result<value> f2() +{ + BOOST_LEAF_ASSIGN(auto a, f1()); +#if BOOST_WORKAROUND( BOOST_GCC, < 50000 ) || BOOST_WORKAROUND( BOOST_CLANG, <= 30800 ) + return std::move(a); // Older compilers are confused, but... +#else + return a; // ...this doesn't need to be return std::move(a); +#endif +} + +leaf::result<value> f3() +{ + BOOST_LEAF_ASSIGN(auto a, f2()); + BOOST_LEAF_ASSIGN(auto b, f2()); // Invoking the macro twice in the same scope, testing the temp name generation + return value { a.x + b.x }; +} + +int main() +{ + BOOST_TEST_EQ(f3()->x, 42); + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + int x = 42; + + leaf::result<int> r1(x); + BOOST_LEAF_ASSIGN(auto && rx1, r1); + BOOST_TEST_EQ(r1.value(), rx1); + + leaf::result<int &> r2(x); + BOOST_LEAF_ASSIGN(auto && rx2, r2); + BOOST_TEST_EQ(r2.value(), rx2); + + leaf::result<int &> r3(x); + BOOST_LEAF_ASSIGN(auto & rx3, r3); + BOOST_TEST_EQ(&r3.value(), &rx3); + + return 0; + }, + [] + { + return 1; + } ); + BOOST_TEST_EQ(r, 0); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/BOOST_LEAF_AUTO_test.cpp b/src/boost/libs/leaf/test/BOOST_LEAF_AUTO_test.cpp new file mode 100644 index 000000000..5a53e7fa7 --- /dev/null +++ b/src/boost/libs/leaf/test/BOOST_LEAF_AUTO_test.cpp @@ -0,0 +1,100 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/result.hpp> +# include <boost/leaf/handle_errors.hpp> +#endif + +#include "lightweight_test.hpp" +#ifdef BOOST_LEAF_BOOST_AVAILABLE +# include <boost/config/workaround.hpp> +#else +# define BOOST_WORKAROUND(a,b) 0 +#endif + +namespace leaf = boost::leaf; + +struct value +{ + int x; + + explicit value( int x ): x(x) { }; + +#ifndef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS + value( value const & ) = delete; + value( value && ) = default; +#endif +}; + +leaf::result<value> f1() +{ + return value { 21 }; +} + +leaf::result<value> f2() +{ + BOOST_LEAF_AUTO(a, f1()); +#if BOOST_WORKAROUND( BOOST_GCC, < 50000 ) || BOOST_WORKAROUND( BOOST_CLANG, <= 30800 ) + return std::move(a); // Older compilers are confused, but... +#else + return a; // ...this doesn't need to be return std::move(a); +#endif +} + +template <class Lambda> +leaf::result<value> f2_lambda( Lambda ) +{ + BOOST_LEAF_AUTO(a, f1()); +#if BOOST_WORKAROUND( BOOST_GCC, < 50000 ) || BOOST_WORKAROUND( BOOST_CLANG, <= 30800 ) + return std::move(a); // Older compilers are confused, but... +#else + return a; // ...this doesn't need to be return std::move(a); +#endif +} + +leaf::result<value> f3() +{ + BOOST_LEAF_AUTO(a, f2()); + + // Invoking the macro twice in the same scope, testing the temp name + // generation. Also making sure we can pass a lambda (See + // https://github.com/boostorg/leaf/issues/16). + BOOST_LEAF_AUTO(b, f2_lambda([]{})); + + return value { a.x + b.x }; +} + +int main() +{ + BOOST_TEST_EQ(f3()->x, 42); + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + int x = 42; + + leaf::result<int> r1(x); + BOOST_LEAF_AUTO(rx1, r1); + BOOST_TEST_EQ(r1.value(), rx1); + + leaf::result<int &> r2(x); + BOOST_LEAF_AUTO(rx2, r2); + BOOST_TEST_EQ(r2.value(), rx2); + + return 0; + }, + [] + { + return 1; + } ); + BOOST_TEST_EQ(r, 0); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/BOOST_LEAF_CHECK_test.cpp b/src/boost/libs/leaf/test/BOOST_LEAF_CHECK_test.cpp new file mode 100644 index 000000000..52510e150 --- /dev/null +++ b/src/boost/libs/leaf/test/BOOST_LEAF_CHECK_test.cpp @@ -0,0 +1,75 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/result.hpp> +# include <boost/leaf/handle_errors.hpp> +#endif + +#ifdef BOOST_LEAF_BOOST_AVAILABLE +# include <boost/config/workaround.hpp> +#else +# define BOOST_WORKAROUND(a,b) 0 +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct value +{ + int x; + + explicit value( int x ): x(x) { }; + +#ifndef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS + value( value const & ) = delete; + value( value && ) = default; +#endif +}; + +leaf::result<value> f1( bool success ) +{ + if( success ) + return value { 21 }; + else + return leaf::new_error(); +} + +#if BOOST_LEAF_GNUC_STMTEXPR + +leaf::result<value> f2( bool success ) +{ + return value { BOOST_LEAF_CHECK(f1(success)).x + BOOST_LEAF_CHECK(f1(success)).x }; +} + +#else + +leaf::result<value> f2( bool success ) +{ + BOOST_LEAF_AUTO(a, f1(success)); + BOOST_LEAF_AUTO(b, f1(success)); + return value { a.x + b.x }; +} + +#endif + +leaf::result<void> f3( bool success ) +{ + BOOST_LEAF_CHECK(f2(success)); + return { }; +} + +int main() +{ + BOOST_TEST_EQ(f2(true).value().x, 42); + BOOST_TEST(!f2(false)); + BOOST_TEST(f3(true)); + BOOST_TEST(!f3(false)); + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/Jamfile.v2 b/src/boost/libs/leaf/test/Jamfile.v2 new file mode 100644 index 000000000..bc872ada7 --- /dev/null +++ b/src/boost/libs/leaf/test/Jamfile.v2 @@ -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) + +import testing ; + +variant leaf_debug_diag0 : debug : <define>BOOST_LEAF_CFG_DIAGNOSTICS=0 ; +variant leaf_release_diag0 : release : <define>BOOST_LEAF_CFG_DIAGNOSTICS=0 ; + +variant leaf_debug_embedded : debug : <define>BOOST_LEAF_EMBEDDED <rtti>off <exception-handling>off ; +variant leaf_release_embedded : release : <define>BOOST_LEAF_EMBEDDED <rtti>off <exception-handling>off ; + +variant leaf_debug_leaf_hpp : debug : <define>BOOST_LEAF_TEST_SINGLE_HEADER ; +variant leaf_release_leaf_hpp : release : <define>BOOST_LEAF_TEST_SINGLE_HEADER ; + +project + : default-build + <cxxstd>11 + : requirements + <define>BOOST_LEAF_BOOST_AVAILABLE + <threading>single:<define>BOOST_LEAF_NO_THREADS + <toolset>gcc:<cxxflags>"-Wno-delete-non-virtual-dtor -Wno-parentheses" + <toolset>clang:<cxxflags>"-Wno-dangling-else" + <toolset>darwin:<cxxflags>"-Wno-unused-variable -Wno-delete-non-virtual-dtor -Wno-non-virtual-dtor -Wno-dangling-else" + <toolset>msvc:<cxxflags>"-wd 4267 -wd 4996 -wd 4244" + <include>../../.. + ; + +compile _hpp_capture_test.cpp ; +compile _hpp_common_test.cpp ; +compile _hpp_config_test.cpp ; +compile _hpp_context_test.cpp ; +compile _hpp_error_test.cpp ; +compile _hpp_exception_test.cpp ; +compile _hpp_handle_errors_test.cpp ; +compile _hpp_on_error_test.cpp ; +compile _hpp_pred_test.cpp ; +compile _hpp_result_test.cpp ; +compile _hpp_to_variant_test.cpp ; +compile _hpp_leaf_test.cpp ; + +run accumulate_basic_test.cpp ; +run accumulate_nested_error_exception_test.cpp ; +run accumulate_nested_error_result_test.cpp ; +run accumulate_nested_new_error_exception_test.cpp ; +run accumulate_nested_new_error_result_test.cpp ; +run accumulate_nested_success_exception_test.cpp ; +run accumulate_nested_success_result_test.cpp ; +run BOOST_LEAF_AUTO_test.cpp ; +run BOOST_LEAF_ASSIGN_test.cpp ; +run BOOST_LEAF_CHECK_test.cpp ; +run boost_exception_test.cpp ; +run capture_exception_async_test.cpp ; +run capture_exception_result_async_test.cpp ; +run capture_exception_state_test.cpp ; +run capture_exception_unload_test.cpp ; +run capture_result_async_test.cpp ; +run capture_result_state_test.cpp ; +run capture_result_unload_test.cpp ; +run ctx_handle_all_test.cpp ; +run ctx_handle_some_test.cpp ; +run ctx_remote_handle_all_test.cpp ; +run ctx_remote_handle_some_test.cpp ; +run defer_basic_test.cpp ; +run defer_nested_error_exception_test.cpp ; +run defer_nested_error_result_test.cpp ; +run defer_nested_new_error_exception_test.cpp ; +run defer_nested_new_error_result_test.cpp ; +run defer_nested_success_exception_test.cpp ; +run defer_nested_success_result_test.cpp ; +run diagnostic_info_test.cpp ; +run diagnostic_info_test2.cpp ; +run e_errno_test.cpp ; +run e_LastError_test.cpp ; +run error_code_test.cpp ; +run error_id_test.cpp ; +run exception_test.cpp ; +run exception_to_result_test.cpp ; +run function_traits_test.cpp ; +run handle_all_other_result_test.cpp ; +run handle_all_test.cpp ; +run handle_basic_test.cpp ; +run handle_some_other_result_test.cpp ; +run handle_some_test.cpp ; +run match_test.cpp ; +run match_member_test.cpp ; +run match_value_test.cpp ; +run multiple_errors_test.cpp ; +run optional_test.cpp ; +run preload_basic_test.cpp ; +run preload_exception_test.cpp ; +run preload_nested_error_exception_test.cpp ; +run preload_nested_error_result_test.cpp ; +run preload_nested_new_error_exception_test.cpp ; +run preload_nested_new_error_result_test.cpp ; +run preload_nested_success_exception_test.cpp ; +run preload_nested_success_result_test.cpp ; +run print_test.cpp ; +run result_bad_result_test.cpp ; +run result_implicit_conversion_test.cpp ; +run result_load_test.cpp ; +run result_ref_test.cpp ; +run result_state_test.cpp ; +run context_deduction_test.cpp ; +run tls_array_alloc_test1.cpp ; +run tls_array_alloc_test2.cpp ; +run tls_array_alloc_test3.cpp ; +run tls_array_test.cpp ; +run to_variant_test.cpp ; +run try_catch_error_id_test.cpp ; +run try_catch_test.cpp ; +run try_exception_and_result_test.cpp ; + +lib visibility_test_lib : visibility_test_lib.cpp : <visibility>hidden ; +run visibility_test.cpp visibility_test_lib/<link>shared ; +run visibility_test.cpp visibility_test_lib/<link>static : : : <target-os>windows ; # This can't work on Windows, so use static link for the test. + +compile-fail _compile-fail-arg_boost_error_info_1.cpp ; +compile-fail _compile-fail-arg_boost_error_info_2.cpp ; +compile-fail _compile-fail-arg_catch_1.cpp ; +compile-fail _compile-fail-arg_catch_2.cpp ; +compile-fail _compile-fail-arg_match_1.cpp ; +compile-fail _compile-fail-arg_match_2.cpp ; +compile-fail _compile-fail-arg_rvalue_ref.cpp ; +compile-fail _compile-fail-BOOST_LEAF_ASSIGN.cpp ; +compile-fail _compile-fail-BOOST_LEAF_AUTO.cpp ; +compile-fail _compile-fail-diagnostic_info.cpp ; +compile-fail _compile-fail-error_info.cpp ; +compile-fail _compile-fail-exception_1.cpp ; +compile-fail _compile-fail-exception_2.cpp ; +compile-fail _compile-fail-new_error.cpp ; +compile-fail _compile-fail-result_1.cpp ; +compile-fail _compile-fail-result_2.cpp ; +compile-fail _compile-fail-result_3.cpp ; +compile-fail _compile-fail-result_4.cpp ; +compile-fail _compile-fail-verbose_diagnostic_info.cpp ; + +exe capture_in_exception : ../example/capture_in_exception.cpp : <threading>single:<build>no <exception-handling>off:<build>no ; +exe capture_in_result : ../example/capture_in_result.cpp : <threading>single:<build>no <exception-handling>off:<build>no ; +exe error_log : ../example/error_log.cpp : <exception-handling>off:<build>no ; +exe error_trace : ../example/error_trace.cpp : <exception-handling>off:<build>no ; +exe exception_to_result : ../example/exception_to_result.cpp : <exception-handling>off:<build>no ; +# exe lua_callback_result : ../example/lua_callback_result.cpp : <exception-handling>off:<build>no ; +# exe lua_callback_eh : ../example/lua_callback_eh.cpp : <exception-handling>off:<build>no ; +exe print_file_eh : ../example/print_file/print_file_eh.cpp : <exception-handling>off:<build>no ; +# exe print_file_outcome_result : ../example/print_file/print_file_outcome_result.cpp : <exception-handling>off:<build>no ; +exe print_file_result : ../example/print_file/print_file_result.cpp : <exception-handling>off:<build>no ; +exe print_half : ../example/print_half.cpp : <exception-handling>off:<build>no ; diff --git a/src/boost/libs/leaf/test/_compile-fail-BOOST_LEAF_ASSIGN.cpp b/src/boost/libs/leaf/test/_compile-fail-BOOST_LEAF_ASSIGN.cpp new file mode 100644 index 000000000..effe3b2e7 --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-BOOST_LEAF_ASSIGN.cpp @@ -0,0 +1,36 @@ +// 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) + +#include <boost/leaf/error.hpp> + +namespace leaf = boost::leaf; + +template <class T> +struct my_result +{ + my_result( T ); + my_result( std::error_code ); + T value() const; + std::error_code error(); + explicit operator bool() const; +}; + +#if 0 +namespace boost { namespace leaf { + template <class T> + struct is_result_type<my_result<T>>: std::true_type + { + }; +} } +#endif + +my_result<int> f(); + +my_result<int> g() +{ + int a; + BOOST_LEAF_ASSIGN(a, f()); + return a; +} diff --git a/src/boost/libs/leaf/test/_compile-fail-BOOST_LEAF_AUTO.cpp b/src/boost/libs/leaf/test/_compile-fail-BOOST_LEAF_AUTO.cpp new file mode 100644 index 000000000..25be00278 --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-BOOST_LEAF_AUTO.cpp @@ -0,0 +1,35 @@ +// 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) + +#include <boost/leaf/error.hpp> + +namespace leaf = boost::leaf; + +template <class T> +struct my_result +{ + my_result( T ); + my_result( std::error_code ); + T value() const; + std::error_code error(); + explicit operator bool() const; +}; + +#if 0 +namespace boost { namespace leaf { + template <class T> + struct is_result_type<my_result<T>>: std::true_type + { + }; +} } +#endif + +my_result<int> f(); + +my_result<int> g() +{ + BOOST_LEAF_AUTO(a, f()); + return a; +} diff --git a/src/boost/libs/leaf/test/_compile-fail-arg_boost_error_info_1.cpp b/src/boost/libs/leaf/test/_compile-fail-arg_boost_error_info_1.cpp new file mode 100644 index 000000000..b3bc8fcb3 --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-arg_boost_error_info_1.cpp @@ -0,0 +1,26 @@ +// 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) + +#include <boost/leaf/handle_errors.hpp> +#include <boost/exception/info.hpp> + +namespace leaf = boost::leaf; + +struct test_ex: std::exception { }; + +typedef boost::error_info<struct test_info_, int> test_info; + +int main() +{ + leaf::try_catch( + [] + { + }, + []( test_info const & x ) // boost::error_info must be taken by value + { + } ); + + return 0; +} diff --git a/src/boost/libs/leaf/test/_compile-fail-arg_boost_error_info_2.cpp b/src/boost/libs/leaf/test/_compile-fail-arg_boost_error_info_2.cpp new file mode 100644 index 000000000..b3bc8fcb3 --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-arg_boost_error_info_2.cpp @@ -0,0 +1,26 @@ +// 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) + +#include <boost/leaf/handle_errors.hpp> +#include <boost/exception/info.hpp> + +namespace leaf = boost::leaf; + +struct test_ex: std::exception { }; + +typedef boost::error_info<struct test_info_, int> test_info; + +int main() +{ + leaf::try_catch( + [] + { + }, + []( test_info const & x ) // boost::error_info must be taken by value + { + } ); + + return 0; +} diff --git a/src/boost/libs/leaf/test/_compile-fail-arg_catch_1.cpp b/src/boost/libs/leaf/test/_compile-fail-arg_catch_1.cpp new file mode 100644 index 000000000..6d0b3f629 --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-arg_catch_1.cpp @@ -0,0 +1,28 @@ +// 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) + +#include <boost/leaf/handle_errors.hpp> +#include <boost/leaf/pred.hpp> +#include <boost/leaf/result.hpp> + +namespace leaf = boost::leaf; + +int main() +{ + return leaf::try_handle_all( + []() -> leaf::result<int> + { + return 0; + }, + []( leaf::catch_<int> const & ) // leaf::catch_<> must be taken by value + { + return 1; + }, + [] + { + return 2; + }); + return 0; +} diff --git a/src/boost/libs/leaf/test/_compile-fail-arg_catch_2.cpp b/src/boost/libs/leaf/test/_compile-fail-arg_catch_2.cpp new file mode 100644 index 000000000..d1aaf276f --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-arg_catch_2.cpp @@ -0,0 +1,28 @@ +// 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) + +#include <boost/leaf/handle_errors.hpp> +#include <boost/leaf/pred.hpp> +#include <boost/leaf/result.hpp> + +namespace leaf = boost::leaf; + +int main() +{ + return leaf::try_handle_all( + []() -> leaf::result<int> + { + return 0; + }, + []( leaf::catch_<int> & ) // leaf::catch_<> must be taken by value + { + return 1; + }, + [] + { + return 2; + }); + return 0; +} diff --git a/src/boost/libs/leaf/test/_compile-fail-arg_match_1.cpp b/src/boost/libs/leaf/test/_compile-fail-arg_match_1.cpp new file mode 100644 index 000000000..6ad721fc6 --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-arg_match_1.cpp @@ -0,0 +1,28 @@ +// 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) + +#include <boost/leaf/handle_errors.hpp> +#include <boost/leaf/pred.hpp> +#include <boost/leaf/result.hpp> + +namespace leaf = boost::leaf; + +int main() +{ + return leaf::try_handle_all( + []() -> leaf::result<int> + { + return 0; + }, + []( leaf::match<int,4> const & ) // leaf::match<> must be taken by value + { + return 1; + }, + [] + { + return 2; + }); + return 0; +} diff --git a/src/boost/libs/leaf/test/_compile-fail-arg_match_2.cpp b/src/boost/libs/leaf/test/_compile-fail-arg_match_2.cpp new file mode 100644 index 000000000..0d1ae1cb6 --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-arg_match_2.cpp @@ -0,0 +1,28 @@ +// 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) + +#include <boost/leaf/handle_errors.hpp> +#include <boost/leaf/pred.hpp> +#include <boost/leaf/result.hpp> + +namespace leaf = boost::leaf; + +int main() +{ + return leaf::try_handle_all( + []() -> leaf::result<int> + { + return 0; + }, + []( leaf::match<int,4> & ) // leaf::match<> must be taken by value + { + return 1; + }, + [] + { + return 2; + }); + return 0; +} diff --git a/src/boost/libs/leaf/test/_compile-fail-arg_rvalue_ref.cpp b/src/boost/libs/leaf/test/_compile-fail-arg_rvalue_ref.cpp new file mode 100644 index 000000000..458cf4ba5 --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-arg_rvalue_ref.cpp @@ -0,0 +1,27 @@ +// 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) + +#include <boost/leaf/handle_errors.hpp> +#include <boost/leaf/result.hpp> + +namespace leaf = boost::leaf; + +int main() +{ + return leaf::try_handle_all( + []() -> leaf::result<int> + { + return 0; + }, + []( int && ) // && arguments are not allowed + { + return 1; + }, + [] + { + return 2; + }); + return 0; +} diff --git a/src/boost/libs/leaf/test/_compile-fail-diagnostic_info.cpp b/src/boost/libs/leaf/test/_compile-fail-diagnostic_info.cpp new file mode 100644 index 000000000..59df9664e --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-diagnostic_info.cpp @@ -0,0 +1,12 @@ +// 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) + +#include <boost/leaf/handle_errors.hpp> + +namespace leaf = boost::leaf; + +leaf::diagnostic_info f(); +leaf::diagnostic_info a = f(); +leaf::diagnostic_info b = a; diff --git a/src/boost/libs/leaf/test/_compile-fail-error_info.cpp b/src/boost/libs/leaf/test/_compile-fail-error_info.cpp new file mode 100644 index 000000000..39d7267c3 --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-error_info.cpp @@ -0,0 +1,12 @@ +// 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) + +#include <boost/leaf/handle_errors.hpp> + +namespace leaf = boost::leaf; + +leaf::error_info f(); +leaf::error_info a = f(); +leaf::error_info b = a; diff --git a/src/boost/libs/leaf/test/_compile-fail-exception_1.cpp b/src/boost/libs/leaf/test/_compile-fail-exception_1.cpp new file mode 100644 index 000000000..15fdc922a --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-exception_1.cpp @@ -0,0 +1,12 @@ +// 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) + +#include <boost/leaf/exception.hpp> + +namespace leaf = boost::leaf; + +struct test: std::exception { }; + +auto e = leaf::exception(std::exception(), test()); diff --git a/src/boost/libs/leaf/test/_compile-fail-exception_2.cpp b/src/boost/libs/leaf/test/_compile-fail-exception_2.cpp new file mode 100644 index 000000000..540a6e61b --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-exception_2.cpp @@ -0,0 +1,12 @@ +// 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) + +#include <boost/leaf/exception.hpp> + +namespace leaf = boost::leaf; + +struct test: std::exception { }; + +auto e = leaf::exception(42, test()); diff --git a/src/boost/libs/leaf/test/_compile-fail-new_error.cpp b/src/boost/libs/leaf/test/_compile-fail-new_error.cpp new file mode 100644 index 000000000..6aca1ff57 --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-new_error.cpp @@ -0,0 +1,9 @@ +// 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) + +#include <boost/leaf/error.hpp> + +int a; +auto x = boost::leaf::new_error(&a); // Pointer error objects not allowed diff --git a/src/boost/libs/leaf/test/_compile-fail-result_1.cpp b/src/boost/libs/leaf/test/_compile-fail-result_1.cpp new file mode 100644 index 000000000..c4bec3df1 --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-result_1.cpp @@ -0,0 +1,15 @@ +// 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) + +#include <boost/leaf/result.hpp> + +namespace leaf = boost::leaf; + +leaf::result<int *> f(); + +leaf::result<int> g() +{ + return f(); // Types are incompatible +} diff --git a/src/boost/libs/leaf/test/_compile-fail-result_2.cpp b/src/boost/libs/leaf/test/_compile-fail-result_2.cpp new file mode 100644 index 000000000..1acdd95af --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-result_2.cpp @@ -0,0 +1,17 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. +// Thanks @strager + +// 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) + +#include <boost/leaf.hpp> +#include <string> +#include <vector> + +size_t g() { + return 42; +} + +boost::leaf::result<std::vector<std::string>> f() { + return g(); // vector constructor is explicit +} diff --git a/src/boost/libs/leaf/test/_compile-fail-result_3.cpp b/src/boost/libs/leaf/test/_compile-fail-result_3.cpp new file mode 100644 index 000000000..47bd73cf9 --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-result_3.cpp @@ -0,0 +1,17 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. +// Thanks @strager + +// 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) + +#include <boost/leaf.hpp> +#include <string> +#include <vector> + +boost::leaf::result<std::size_t> g() { + return 42; +} + +boost::leaf::result<std::vector<std::string>> f() { + return g(); // vector constructor is explicit +} diff --git a/src/boost/libs/leaf/test/_compile-fail-result_4.cpp b/src/boost/libs/leaf/test/_compile-fail-result_4.cpp new file mode 100644 index 000000000..f25f64030 --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-result_4.cpp @@ -0,0 +1,10 @@ +// 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) + +#include <boost/leaf/result.hpp> + +namespace leaf = boost::leaf; + +leaf::result<int &> x; // Default constructor not available for references diff --git a/src/boost/libs/leaf/test/_compile-fail-verbose_diagnostic_info.cpp b/src/boost/libs/leaf/test/_compile-fail-verbose_diagnostic_info.cpp new file mode 100644 index 000000000..61ecc6504 --- /dev/null +++ b/src/boost/libs/leaf/test/_compile-fail-verbose_diagnostic_info.cpp @@ -0,0 +1,12 @@ +// 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) + +#include <boost/leaf/handle_errors.hpp> + +namespace leaf = boost::leaf; + +leaf::verbose_diagnostic_info f(); +leaf::verbose_diagnostic_info a = f(); +leaf::verbose_diagnostic_info b = a; diff --git a/src/boost/libs/leaf/test/_hpp_capture_test.cpp b/src/boost/libs/leaf/test/_hpp_capture_test.cpp new file mode 100644 index 000000000..4107de3ea --- /dev/null +++ b/src/boost/libs/leaf/test/_hpp_capture_test.cpp @@ -0,0 +1,8 @@ +// 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) + +#include <boost/leaf/capture.hpp> +#include <boost/leaf/capture.hpp> +int main() { return 0; } diff --git a/src/boost/libs/leaf/test/_hpp_common_test.cpp b/src/boost/libs/leaf/test/_hpp_common_test.cpp new file mode 100644 index 000000000..b3afd0167 --- /dev/null +++ b/src/boost/libs/leaf/test/_hpp_common_test.cpp @@ -0,0 +1,8 @@ +// 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) + +#include <boost/leaf/common.hpp> +#include <boost/leaf/common.hpp> +int main() { return 0; } diff --git a/src/boost/libs/leaf/test/_hpp_config_test.cpp b/src/boost/libs/leaf/test/_hpp_config_test.cpp new file mode 100644 index 000000000..a517cafbb --- /dev/null +++ b/src/boost/libs/leaf/test/_hpp_config_test.cpp @@ -0,0 +1,8 @@ +// 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) + +#include <boost/leaf/config.hpp> +#include <boost/leaf/config.hpp> +int main() { return 0; } diff --git a/src/boost/libs/leaf/test/_hpp_context_test.cpp b/src/boost/libs/leaf/test/_hpp_context_test.cpp new file mode 100644 index 000000000..667e244ef --- /dev/null +++ b/src/boost/libs/leaf/test/_hpp_context_test.cpp @@ -0,0 +1,8 @@ +// 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) + +#include <boost/leaf/context.hpp> +#include <boost/leaf/context.hpp> +int main() { return 0; } diff --git a/src/boost/libs/leaf/test/_hpp_error_test.cpp b/src/boost/libs/leaf/test/_hpp_error_test.cpp new file mode 100644 index 000000000..1dc20a546 --- /dev/null +++ b/src/boost/libs/leaf/test/_hpp_error_test.cpp @@ -0,0 +1,8 @@ +// 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) + +#include <boost/leaf/error.hpp> +#include <boost/leaf/error.hpp> +int main() { return 0; } diff --git a/src/boost/libs/leaf/test/_hpp_exception_test.cpp b/src/boost/libs/leaf/test/_hpp_exception_test.cpp new file mode 100644 index 000000000..bda129195 --- /dev/null +++ b/src/boost/libs/leaf/test/_hpp_exception_test.cpp @@ -0,0 +1,8 @@ +// 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) + +#include <boost/leaf/exception.hpp> +#include <boost/leaf/exception.hpp> +int main() { return 0; } diff --git a/src/boost/libs/leaf/test/_hpp_handle_errors_test.cpp b/src/boost/libs/leaf/test/_hpp_handle_errors_test.cpp new file mode 100644 index 000000000..9e7790137 --- /dev/null +++ b/src/boost/libs/leaf/test/_hpp_handle_errors_test.cpp @@ -0,0 +1,8 @@ +// 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) + +#include <boost/leaf/handle_errors.hpp> +#include <boost/leaf/handle_errors.hpp> +int main() { return 0; } diff --git a/src/boost/libs/leaf/test/_hpp_leaf_test.cpp b/src/boost/libs/leaf/test/_hpp_leaf_test.cpp new file mode 100644 index 000000000..306c2bfea --- /dev/null +++ b/src/boost/libs/leaf/test/_hpp_leaf_test.cpp @@ -0,0 +1,8 @@ +// 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) + +#include <boost/leaf.hpp> +#include <boost/leaf.hpp> +int main() { return 0; } diff --git a/src/boost/libs/leaf/test/_hpp_on_error_test.cpp b/src/boost/libs/leaf/test/_hpp_on_error_test.cpp new file mode 100644 index 000000000..578f299aa --- /dev/null +++ b/src/boost/libs/leaf/test/_hpp_on_error_test.cpp @@ -0,0 +1,8 @@ +// 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) + +#include <boost/leaf/on_error.hpp> +#include <boost/leaf/on_error.hpp> +int main() { return 0; } diff --git a/src/boost/libs/leaf/test/_hpp_pred_test.cpp b/src/boost/libs/leaf/test/_hpp_pred_test.cpp new file mode 100644 index 000000000..e7826451a --- /dev/null +++ b/src/boost/libs/leaf/test/_hpp_pred_test.cpp @@ -0,0 +1,8 @@ +// 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) + +#include <boost/leaf/pred.hpp> +#include <boost/leaf/pred.hpp> +int main() { return 0; } diff --git a/src/boost/libs/leaf/test/_hpp_result_test.cpp b/src/boost/libs/leaf/test/_hpp_result_test.cpp new file mode 100644 index 000000000..3cf95d650 --- /dev/null +++ b/src/boost/libs/leaf/test/_hpp_result_test.cpp @@ -0,0 +1,8 @@ +// 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) + +#include <boost/leaf/result.hpp> +#include <boost/leaf/result.hpp> +int main() { return 0; } diff --git a/src/boost/libs/leaf/test/_hpp_to_variant_test.cpp b/src/boost/libs/leaf/test/_hpp_to_variant_test.cpp new file mode 100644 index 000000000..3bedbe54b --- /dev/null +++ b/src/boost/libs/leaf/test/_hpp_to_variant_test.cpp @@ -0,0 +1,8 @@ +// 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) + +#include <boost/leaf/to_variant.hpp> +#include <boost/leaf/to_variant.hpp> +int main() { return 0; } diff --git a/src/boost/libs/leaf/test/_test_ec.hpp b/src/boost/libs/leaf/test/_test_ec.hpp new file mode 100644 index 000000000..77399c3ce --- /dev/null +++ b/src/boost/libs/leaf/test/_test_ec.hpp @@ -0,0 +1,199 @@ +#ifndef BOOST_LEAF_TEST_EC_HPP_INCLUDED +#define BOOST_LEAF_TEST_EC_HPP_INCLUDED + +// 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) + +#include <system_error> + +enum class errc_a { a0 = 10, a1, a2, a3 }; + +namespace std { template <> struct is_error_code_enum<errc_a>: true_type { }; } + +inline std::error_category const & cat_errc_a() +{ + class cat : public std::error_category + { + char const * name() const noexcept + { + return "errc_a"; + } + std::string message(int code) const + { + switch( errc_a(code) ) + { + case errc_a::a0: return "a0"; + case errc_a::a1: return "a1"; + case errc_a::a2: return "a2"; + case errc_a::a3: return "a3"; + default: return "error"; + } + } + }; + static cat c; + return c; +} + +inline std::error_code make_error_code( errc_a code ) +{ + return std::error_code(int(code), cat_errc_a()); +} + +template <int> struct e_errc_a { std::error_code value; }; + +/////////////////////////////////// + +enum class errc_b { b0 = 20, b1, b2, b3 }; + +namespace std { template <> struct is_error_code_enum<errc_b>: true_type { }; }; + +inline std::error_category const & cat_errc_b() +{ + class cat : public std::error_category + { + char const * name() const noexcept + { + return "errc_b"; + } + std::string message(int code) const + { + switch( errc_b(code) ) + { + case errc_b::b0: return "b0"; + case errc_b::b1: return "b1"; + case errc_b::b2: return "b2"; + case errc_b::b3: return "b3"; + default: return "error"; + } + } + }; + static cat c; + return c; +} + +inline std::error_code make_error_code( errc_b code ) +{ + return std::error_code(int(code), cat_errc_b()); +} + +template <int> struct e_errc_b { std::error_code value; }; + +/////////////////////////////////// + +enum class cond_x { x00 = 30, x11, x22, x33 }; + +namespace std { template <> struct is_error_condition_enum<cond_x>: true_type { }; }; + +inline std::error_category const & cat_cond_x() +{ + class cat : public std::error_category + { + char const * name() const noexcept + { + return "cond_x"; + } + std::string message(int cond) const + { + switch( cond_x(cond) ) + { + case cond_x::x00: + return "x00"; + case cond_x::x11: + return "x11"; + case cond_x::x22: + return "x22"; + case cond_x::x33: + return "x33"; + default: + return "error"; + } + } + bool equivalent(std::error_code const & code, int cond) const noexcept + { + switch( cond_x(cond) ) + { + case cond_x::x00: + return code==make_error_code(errc_a::a0) || code==make_error_code(errc_b::b0); + case cond_x::x11: + return code==make_error_code(errc_a::a1) || code==make_error_code(errc_b::b1); + case cond_x::x22: + return code==make_error_code(errc_a::a2) || code==make_error_code(errc_b::b2); + case cond_x::x33: + return code==make_error_code(errc_a::a3) || code==make_error_code(errc_b::b3); + default: + return false; + } + } + }; + static cat c; + return c; +} + +inline std::error_condition make_error_condition( cond_x cond ) +{ + return std::error_condition(int(cond), cat_cond_x()); +} + +template <int> struct e_cond_x { cond_x value; }; + +/////////////////////////////////// + +enum class cond_y { y03 = 40, y12, y21, y30 }; + +namespace std { template <> struct is_error_condition_enum<cond_y>: true_type { }; }; + +inline std::error_category const & cat_cond_y() +{ + class cat : public std::error_category + { + char const * name() const noexcept + { + return "cond_y"; + } + std::string message( int cond ) const + { + switch( cond_y(cond) ) + { + case cond_y::y03: + return "y03"; + case cond_y::y12: + return "y12"; + case cond_y::y21: + return "y21"; + case cond_y::y30: + return "y30"; + default: + return "error"; + } + } + bool equivalent(std::error_code const & code, int cond) const noexcept + { + switch( cond_y(cond) ) + { + case cond_y::y03: + return code==make_error_code(errc_a::a0) || code==make_error_code(errc_b::b0); + case cond_y::y12: + return code==make_error_code(errc_a::a1) || code==make_error_code(errc_b::b1); + case cond_y::y21: + return code==make_error_code(errc_a::a2) || code==make_error_code(errc_b::b2); + case cond_y::y30: + return code==make_error_code(errc_a::a3) || code==make_error_code(errc_b::b3); + default: + return false; + } + } + }; + static cat c; + return c; +} + +inline std::error_condition make_error_condition( cond_y cond ) +{ + return std::error_condition(int(cond), cat_cond_y()); +} + +template <int> struct e_cond_y { cond_y value; }; + +#endif diff --git a/src/boost/libs/leaf/test/_test_res.hpp b/src/boost/libs/leaf/test/_test_res.hpp new file mode 100644 index 000000000..918671786 --- /dev/null +++ b/src/boost/libs/leaf/test/_test_res.hpp @@ -0,0 +1,112 @@ +#ifndef BOOST_LEAF_TEST_RES_HPP_INCLUDED +#define BOOST_LEAF_TEST_RES_HPP_INCLUDED + +// 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) + +#include "_test_ec.hpp" + +template <class T, class E> +class test_res +{ + enum class variant + { + value, + error + }; + T value_; + E error_; + variant which_; +public: + test_res( T const & value ) noexcept: + value_(value), + error_(), + which_(variant::value) + { + } + test_res( E const & error ) noexcept: + value_(), + error_(error), + which_(variant::error) + { + } + template <class Enum> + test_res( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ): + value_(), + error_(make_error_code(e)), + which_(variant::error) + { + } + explicit operator bool() const noexcept + { + return which_==variant::value; + } + T const & value() const + { + BOOST_LEAF_ASSERT(which_==variant::value); + return value_; + } + E const & error() const + { + BOOST_LEAF_ASSERT(which_==variant::error); + return error_; + } +}; + +template <class E> +class test_res<void, E> +{ + enum class variant + { + value, + error + }; + E error_; + variant which_; +public: + test_res() noexcept: + error_(), + which_(variant::value) + { + } + test_res( E const & error ) noexcept: + error_(error), + which_(variant::error) + { + } + template <class Enum> + test_res( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ): + error_(make_error_code(e)), + which_(variant::error) + { + } + explicit operator bool() const noexcept + { + return which_==variant::value; + } + void value() const + { + BOOST_LEAF_ASSERT(which_==variant::value); + } + E const & error() const + { + BOOST_LEAF_ASSERT(which_==variant::error); + return error_; + } +}; + +namespace boost { namespace leaf { + + template <class T> + struct is_result_type; + + template <class T, class E> + struct is_result_type<test_res<T, E>>: std::true_type + { + }; + +} } + +#endif diff --git a/src/boost/libs/leaf/test/accumulate_basic_test.cpp b/src/boost/libs/leaf/test/accumulate_basic_test.cpp new file mode 100644 index 000000000..928d70259 --- /dev/null +++ b/src/boost/libs/leaf/test/accumulate_basic_test.cpp @@ -0,0 +1,55 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> +struct info +{ + int value; +}; + +leaf::error_id g() +{ + auto load = leaf::on_error( [](info<1> & x) {++x.value;} ); + return leaf::new_error(); +} + +leaf::error_id f() +{ + auto load = leaf::on_error( [](info<1> & x) {++x.value;}, [](info<2> & x) {++x.value;} ); + return g(); +} + +int main() +{ + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return f(); + }, + []( info<1> const & a, info<2> const & b ) + { + BOOST_TEST_EQ(a.value, 2); + BOOST_TEST_EQ(b.value, 1); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/accumulate_nested_error_exception_test.cpp b/src/boost/libs/leaf/test/accumulate_nested_error_exception_test.cpp new file mode 100644 index 000000000..163be9acd --- /dev/null +++ b/src/boost/libs/leaf/test/accumulate_nested_error_exception_test.cpp @@ -0,0 +1,88 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/exception.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> +struct info +{ + int value; +}; + +void f0() +{ + auto load = leaf::on_error( []( info<0> & ) { } ); + throw leaf::exception(info<2>{2}); +} + +void f1() +{ + auto load = leaf::on_error( info<0>{-1}, info<2>{-1}, []( info<1> & x ) {++x.value;} ); + f0(); +} + +void f2() +{ + try + { + f1(); + } + catch( leaf::error_id const & err ) + { + err.load( info<3>{3} ); + throw; + } +} + +int main() +{ + int r = leaf::try_catch( + [] + { + f2(); + return 0; + }, + []( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST_EQ(i0.value, 0); + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, 2); + BOOST_TEST_EQ(i3.value, 3); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/accumulate_nested_error_result_test.cpp b/src/boost/libs/leaf/test/accumulate_nested_error_result_test.cpp new file mode 100644 index 000000000..f5896b44b --- /dev/null +++ b/src/boost/libs/leaf/test/accumulate_nested_error_result_test.cpp @@ -0,0 +1,63 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> +struct info +{ + int value; +}; + +leaf::error_id f0() +{ + auto load = leaf::on_error( []( info<0> & ) { } ); + return leaf::new_error( info<2>{2} ); +} + +leaf::error_id f1() +{ + auto load = leaf::on_error( info<0>{-1}, info<2>{-1}, []( info<1> & x ) {++x.value;} ); + return f0(); +} + +leaf::error_id f2() +{ + return f1().load( info<3>{3} ); +} + +int main() +{ + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return f2(); + }, + []( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST_EQ(i0.value, 0); + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, 2); + BOOST_TEST_EQ(i3.value, 3); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/accumulate_nested_new_error_exception_test.cpp b/src/boost/libs/leaf/test/accumulate_nested_new_error_exception_test.cpp new file mode 100644 index 000000000..91b299710 --- /dev/null +++ b/src/boost/libs/leaf/test/accumulate_nested_new_error_exception_test.cpp @@ -0,0 +1,95 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/exception.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int A> +struct info +{ + int value; +}; + +void f0() +{ + auto load = leaf::on_error( info<0>{-1} ); + throw leaf::exception(info<1>{-1}); +} + +void f1() +{ + auto load = leaf::on_error( []( info<0> & x ) { }, []( info<1> & x ) { ++x.value; }, []( info<2> & x ) { ++x.value; } ); + try { f0(); } catch(...) { } + throw leaf::exception(); +} + +leaf::error_id f2() +{ + try + { + f1(); + BOOST_TEST(false); + } + catch( leaf::error_id const & err ) + { + err.load( info<3>{3} ); + throw; + } + catch(...) + { + BOOST_TEST(false); + } + return leaf::new_error(); +} + +int main() +{ + int r = leaf::try_catch( + [] + { + f2(); + return 0; + }, + []( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST_EQ(i0.value, 0); + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, 1); + BOOST_TEST_EQ(i3.value, 3); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/accumulate_nested_new_error_result_test.cpp b/src/boost/libs/leaf/test/accumulate_nested_new_error_result_test.cpp new file mode 100644 index 000000000..59c8cef36 --- /dev/null +++ b/src/boost/libs/leaf/test/accumulate_nested_new_error_result_test.cpp @@ -0,0 +1,64 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int A> +struct info +{ + int value; +}; + +leaf::error_id f0() +{ + auto load = leaf::on_error( info<0>{-1} ); + return leaf::new_error( info<1>{-1} ); +} + +leaf::error_id f1() +{ + auto load = leaf::on_error( []( info<0> & x ) { }, []( info<1> & x ) { ++x.value; }, []( info<2> & x ) { ++x.value; } ); + (void) f0(); + return leaf::new_error(); +} + +leaf::error_id f2() +{ + return f1().load( info<3>{3} ); +} + +int main() +{ + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return f2(); + }, + []( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST_EQ(i0.value, 0); + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, 1); + BOOST_TEST_EQ(i3.value, 3); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/accumulate_nested_success_exception_test.cpp b/src/boost/libs/leaf/test/accumulate_nested_success_exception_test.cpp new file mode 100644 index 000000000..ad8edc66f --- /dev/null +++ b/src/boost/libs/leaf/test/accumulate_nested_success_exception_test.cpp @@ -0,0 +1,72 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +void g1() +{ + auto load = leaf::on_error( []( info & x ) { ++x.value; } ); +} + +void g2() +{ + throw std::exception(); +} + +void f() +{ + auto load = leaf::on_error( info{42} ); + g1(); + g2(); +} + +int main() +{ + int r = leaf::try_catch( + [] + { + f(); + return 0; + }, + []( info x ) + { + BOOST_TEST_EQ(x.value, 42); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/accumulate_nested_success_result_test.cpp b/src/boost/libs/leaf/test/accumulate_nested_success_result_test.cpp new file mode 100644 index 000000000..fe068eb13 --- /dev/null +++ b/src/boost/libs/leaf/test/accumulate_nested_success_result_test.cpp @@ -0,0 +1,58 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +leaf::result<void> g1() +{ + auto load = leaf::on_error( []( info & x ) { ++x.value; } ); + return { }; +} + +leaf::result<void> g2() +{ + return leaf::new_error(); +} + +leaf::result<void> f() +{ + auto load = leaf::on_error( info{2} ); + BOOST_LEAF_CHECK(g1()); + return g2(); +} + +int main() +{ + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_CHECK(f()); + return 1; + }, + []( info x ) + { + BOOST_TEST_EQ(x.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/boost/core/current_function.hpp b/src/boost/libs/leaf/test/boost/core/current_function.hpp new file mode 100644 index 000000000..0641c42df --- /dev/null +++ b/src/boost/libs/leaf/test/boost/core/current_function.hpp @@ -0,0 +1,75 @@ +#ifndef BOOST_CURRENT_FUNCTION_HPP_INCLUDED +#define BOOST_CURRENT_FUNCTION_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// boost/current_function.hpp - BOOST_CURRENT_FUNCTION +// +// Copyright (c) 2002 Peter Dimov and Multi Media Ltd. +// +// 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 +// +// http://www.boost.org/libs/assert/current_function.html +// + +namespace boost +{ + +namespace detail +{ + +inline void current_function_helper() +{ + +#if defined( BOOST_DISABLE_CURRENT_FUNCTION ) + +# define BOOST_CURRENT_FUNCTION "(unknown)" + +#elif defined(__GNUC__) || (defined(__MWERKS__) && (__MWERKS__ >= 0x3000)) || (defined(__ICC) && (__ICC >= 600)) || defined(__ghs__) || defined(__clang__) + +# define BOOST_CURRENT_FUNCTION __PRETTY_FUNCTION__ + +#elif defined(__DMC__) && (__DMC__ >= 0x810) + +# define BOOST_CURRENT_FUNCTION __PRETTY_FUNCTION__ + +#elif defined(__FUNCSIG__) + +# define BOOST_CURRENT_FUNCTION __FUNCSIG__ + +#elif (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 600)) || (defined(__IBMCPP__) && (__IBMCPP__ >= 500)) + +# define BOOST_CURRENT_FUNCTION __FUNCTION__ + +#elif defined(__BORLANDC__) && (__BORLANDC__ >= 0x550) + +# define BOOST_CURRENT_FUNCTION __FUNC__ + +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901) + +# define BOOST_CURRENT_FUNCTION __func__ + +#elif defined(__cplusplus) && (__cplusplus >= 201103) + +# define BOOST_CURRENT_FUNCTION __func__ + +#else + +# define BOOST_CURRENT_FUNCTION "(unknown)" + +#endif + +} + +} // namespace detail + +} // namespace boost + +#endif // #ifndef BOOST_CURRENT_FUNCTION_HPP_INCLUDED diff --git a/src/boost/libs/leaf/test/boost/core/lightweight_test.hpp b/src/boost/libs/leaf/test/boost/core/lightweight_test.hpp new file mode 100644 index 000000000..05c19b49e --- /dev/null +++ b/src/boost/libs/leaf/test/boost/core/lightweight_test.hpp @@ -0,0 +1,435 @@ +#ifndef BOOST_CORE_LIGHTWEIGHT_TEST_HPP +#define BOOST_CORE_LIGHTWEIGHT_TEST_HPP + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) +# pragma once +#endif + +// +// boost/core/lightweight_test.hpp - lightweight test library +// +// Copyright (c) 2002, 2009, 2014 Peter Dimov +// Copyright (2) Beman Dawes 2010, 2011 +// Copyright (3) Ion Gaztanaga 2013 +// +// Copyright 2018-2019 Glen Joseph Fernandes +// (glenjofe@gmail.com) +// +// 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 +// + +#include "current_function.hpp" +#include <iostream> +#include <iterator> +#include <cstdlib> +#include <cstring> +#include <cstddef> + +// IDE's like Visual Studio perform better if output goes to std::cout or +// some other stream, so allow user to configure output stream: +#ifndef BOOST_LIGHTWEIGHT_TEST_OSTREAM +# define BOOST_LIGHTWEIGHT_TEST_OSTREAM std::cerr +#endif + +namespace boost +{ + +namespace detail +{ + +class test_result { +public: + test_result() + : report_(false) + , errors_(0) { +#if defined(_MSC_VER) && (_MSC_VER > 1310) + ::_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); +#endif + } + + ~test_result() { + if (!report_) { + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "main() should return report_errors()" << std::endl; + std::abort(); + } + } + + int& errors() { + return errors_; + } + + void done() { + report_ = true; + } + +private: + bool report_; + int errors_; +}; + +inline test_result& test_results() +{ + static test_result instance; + return instance; +} + +inline int& test_errors() +{ + return test_results().errors(); +} + +inline void test_failed_impl(char const * expr, char const * file, int line, char const * function) +{ + BOOST_LIGHTWEIGHT_TEST_OSTREAM + << file << "(" << line << "): test '" << expr << "' failed in function '" + << function << "'" << std::endl; + ++test_results().errors(); +} + +inline void error_impl(char const * msg, char const * file, int line, char const * function) +{ + BOOST_LIGHTWEIGHT_TEST_OSTREAM + << file << "(" << line << "): " << msg << " in function '" + << function << "'" << std::endl; + ++test_results().errors(); +} + +inline void throw_failed_impl(char const * excep, char const * file, int line, char const * function) +{ + BOOST_LIGHTWEIGHT_TEST_OSTREAM + << file << "(" << line << "): Exception '" << excep << "' not thrown in function '" + << function << "'" << std::endl; + ++test_results().errors(); +} + +// In the comparisons below, it is possible that T and U are signed and unsigned integer types, which generates warnings in some compilers. +// A cleaner fix would require common_type trait or some meta-programming, which would introduce a dependency on Boost.TypeTraits. To avoid +// the dependency we just disable the warnings. +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4389) +#elif defined(__clang__) && defined(__has_warning) +# if __has_warning("-Wsign-compare") +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wsign-compare" +# endif +#elif defined(__GNUC__) && !(defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wsign-compare" +#endif + +// specialize test output for char pointers to avoid printing as cstring +template <class T> inline const T& test_output_impl(const T& v) { return v; } +inline const void* test_output_impl(const char* v) { return v; } +inline const void* test_output_impl(const unsigned char* v) { return v; } +inline const void* test_output_impl(const signed char* v) { return v; } +inline const void* test_output_impl(char* v) { return v; } +inline const void* test_output_impl(unsigned char* v) { return v; } +inline const void* test_output_impl(signed char* v) { return v; } +template<class T> inline const void* test_output_impl(T volatile* v) { return const_cast<T*>(v); } + +#if !defined( BOOST_NO_CXX11_NULLPTR ) +inline const void* test_output_impl(std::nullptr_t) { return nullptr; } +#endif + +struct lw_test_eq { + template <typename T, typename U> + bool operator()(const T& t, const U& u) const { return t == u; } + static const char* op() { return "=="; } +}; + +struct lw_test_ne { + template <typename T, typename U> + bool operator()(const T& t, const U& u) const { return t != u; } + static const char* op() { return "!="; } +}; + +struct lw_test_lt { + template <typename T, typename U> + bool operator()(const T& t, const U& u) const { return t < u; } + static const char* op() { return "<"; } +}; + +struct lw_test_le { + template <typename T, typename U> + bool operator()(const T& t, const U& u) const { return t <= u; } + static const char* op() { return "<="; } +}; + +struct lw_test_gt { + template <typename T, typename U> + bool operator()(const T& t, const U& u) const { return t > u; } + static const char* op() { return ">"; } +}; + +struct lw_test_ge { + template <typename T, typename U> + bool operator()(const T& t, const U& u) const { return t >= u; } + static const char* op() { return ">="; } +}; + +template<class BinaryPredicate, class T, class U> +inline void test_with_impl(BinaryPredicate pred, char const * expr1, char const * expr2, + char const * file, int line, char const * function, + T const & t, U const & u) +{ + if( pred(t, u) ) + { + test_results(); + } + else + { + BOOST_LIGHTWEIGHT_TEST_OSTREAM + << file << "(" << line << "): test '" << expr1 << " " << pred.op() << " " << expr2 + << "' ('" << test_output_impl(t) << "' " << pred.op() << " '" << test_output_impl(u) + << "') failed in function '" << function << "'" << std::endl; + ++test_results().errors(); + } +} + +inline void test_cstr_eq_impl( char const * expr1, char const * expr2, + char const * file, int line, char const * function, char const * const t, char const * const u ) +{ + if( std::strcmp(t, u) == 0 ) + { + test_results(); + } + else + { + BOOST_LIGHTWEIGHT_TEST_OSTREAM + << file << "(" << line << "): test '" << expr1 << " == " << expr2 << "' ('" << t + << "' == '" << u << "') failed in function '" << function << "'" << std::endl; + ++test_results().errors(); + } +} + +inline void test_cstr_ne_impl( char const * expr1, char const * expr2, + char const * file, int line, char const * function, char const * const t, char const * const u ) +{ + if( std::strcmp(t, u) != 0 ) + { + test_results(); + } + else + { + BOOST_LIGHTWEIGHT_TEST_OSTREAM + << file << "(" << line << "): test '" << expr1 << " != " << expr2 << "' ('" << t + << "' != '" << u << "') failed in function '" << function << "'" << std::endl; + ++test_results().errors(); + } +} + +template<class FormattedOutputFunction, class InputIterator1, class InputIterator2> +void test_all_eq_impl(FormattedOutputFunction& output, + char const * file, int line, char const * function, + InputIterator1 first_begin, InputIterator1 first_end, + InputIterator2 second_begin, InputIterator2 second_end) +{ + InputIterator1 first_it = first_begin; + InputIterator2 second_it = second_begin; + typename std::iterator_traits<InputIterator1>::difference_type first_index = 0; + typename std::iterator_traits<InputIterator2>::difference_type second_index = 0; + std::size_t error_count = 0; + const std::size_t max_count = 8; + do + { + while ((first_it != first_end) && (second_it != second_end) && (*first_it == *second_it)) + { + ++first_it; + ++second_it; + ++first_index; + ++second_index; + } + if ((first_it == first_end) || (second_it == second_end)) + { + break; // do-while + } + if (error_count == 0) + { + output << file << "(" << line << "): Container contents differ in function '" << function << "':"; + } + else if (error_count >= max_count) + { + output << " ..."; + break; + } + output << " [" << first_index << "] '" << test_output_impl(*first_it) << "' != '" << test_output_impl(*second_it) << "'"; + ++first_it; + ++second_it; + ++first_index; + ++second_index; + ++error_count; + } while (first_it != first_end); + + first_index += std::distance(first_it, first_end); + second_index += std::distance(second_it, second_end); + if (first_index != second_index) + { + if (error_count == 0) + { + output << file << "(" << line << "): Container sizes differ in function '" << function << "': size(" << first_index << ") != size(" << second_index << ")"; + } + else + { + output << " [*] size(" << first_index << ") != size(" << second_index << ")"; + } + ++error_count; + } + + if (error_count == 0) + { + test_results(); + } + else + { + output << std::endl; + ++test_results().errors(); + } +} + +template<class FormattedOutputFunction, class InputIterator1, class InputIterator2, typename BinaryPredicate> +void test_all_with_impl(FormattedOutputFunction& output, + char const * file, int line, char const * function, + InputIterator1 first_begin, InputIterator1 first_end, + InputIterator2 second_begin, InputIterator2 second_end, + BinaryPredicate predicate) +{ + InputIterator1 first_it = first_begin; + InputIterator2 second_it = second_begin; + typename std::iterator_traits<InputIterator1>::difference_type first_index = 0; + typename std::iterator_traits<InputIterator2>::difference_type second_index = 0; + std::size_t error_count = 0; + const std::size_t max_count = 8; + do + { + while ((first_it != first_end) && (second_it != second_end) && predicate(*first_it, *second_it)) + { + ++first_it; + ++second_it; + ++first_index; + ++second_index; + } + if ((first_it == first_end) || (second_it == second_end)) + { + break; // do-while + } + if (error_count == 0) + { + output << file << "(" << line << "): Container contents differ in function '" << function << "':"; + } + else if (error_count >= max_count) + { + output << " ..."; + break; + } + output << " [" << first_index << "]"; + ++first_it; + ++second_it; + ++first_index; + ++second_index; + ++error_count; + } while (first_it != first_end); + + first_index += std::distance(first_it, first_end); + second_index += std::distance(second_it, second_end); + if (first_index != second_index) + { + if (error_count == 0) + { + output << file << "(" << line << "): Container sizes differ in function '" << function << "': size(" << first_index << ") != size(" << second_index << ")"; + } + else + { + output << " [*] size(" << first_index << ") != size(" << second_index << ")"; + } + ++error_count; + } + + if (error_count == 0) + { + test_results(); + } + else + { + output << std::endl; + ++test_results().errors(); + } +} + +#if defined(_MSC_VER) +# pragma warning(pop) +#elif defined(__clang__) && defined(__has_warning) +# if __has_warning("-Wsign-compare") +# pragma clang diagnostic pop +# endif +#elif defined(__GNUC__) && !(defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 +# pragma GCC diagnostic pop +#endif + +} // namespace detail + +inline int report_errors() +{ + boost::detail::test_result& result = boost::detail::test_results(); + result.done(); + + int errors = result.errors(); + if( errors == 0 ) + { + BOOST_LIGHTWEIGHT_TEST_OSTREAM + << "No errors detected." << std::endl; + return 0; + } + else + { + BOOST_LIGHTWEIGHT_TEST_OSTREAM + << errors << " error" << (errors == 1? "": "s") << " detected." << std::endl; + return 1; + } +} + +} // namespace boost + +#define BOOST_TEST(expr) ((expr)? (void)::boost::detail::test_results(): ::boost::detail::test_failed_impl(#expr, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION)) +#define BOOST_TEST_NOT(expr) BOOST_TEST(!(expr)) + +#define BOOST_ERROR(msg) ( ::boost::detail::error_impl(msg, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION) ) + +#define BOOST_TEST_EQ(expr1,expr2) ( ::boost::detail::test_with_impl(::boost::detail::lw_test_eq(), #expr1, #expr2, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, expr1, expr2) ) +#define BOOST_TEST_NE(expr1,expr2) ( ::boost::detail::test_with_impl(::boost::detail::lw_test_ne(), #expr1, #expr2, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, expr1, expr2) ) + +#define BOOST_TEST_LT(expr1,expr2) ( ::boost::detail::test_with_impl(::boost::detail::lw_test_lt(), #expr1, #expr2, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, expr1, expr2) ) +#define BOOST_TEST_LE(expr1,expr2) ( ::boost::detail::test_with_impl(::boost::detail::lw_test_le(), #expr1, #expr2, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, expr1, expr2) ) +#define BOOST_TEST_GT(expr1,expr2) ( ::boost::detail::test_with_impl(::boost::detail::lw_test_gt(), #expr1, #expr2, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, expr1, expr2) ) +#define BOOST_TEST_GE(expr1,expr2) ( ::boost::detail::test_with_impl(::boost::detail::lw_test_ge(), #expr1, #expr2, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, expr1, expr2) ) + +#define BOOST_TEST_CSTR_EQ(expr1,expr2) ( ::boost::detail::test_cstr_eq_impl(#expr1, #expr2, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, expr1, expr2) ) +#define BOOST_TEST_CSTR_NE(expr1,expr2) ( ::boost::detail::test_cstr_ne_impl(#expr1, #expr2, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, expr1, expr2) ) + +#define BOOST_TEST_ALL_EQ(begin1, end1, begin2, end2) ( ::boost::detail::test_all_eq_impl(BOOST_LIGHTWEIGHT_TEST_OSTREAM, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, begin1, end1, begin2, end2) ) +#define BOOST_TEST_ALL_WITH(begin1, end1, begin2, end2, predicate) ( ::boost::detail::test_all_with_impl(BOOST_LIGHTWEIGHT_TEST_OSTREAM, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, begin1, end1, begin2, end2, predicate) ) + +#ifndef BOOST_NO_EXCEPTIONS + #define BOOST_TEST_THROWS( EXPR, EXCEP ) \ + try { \ + EXPR; \ + ::boost::detail::throw_failed_impl \ + (#EXCEP, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION); \ + } \ + catch(EXCEP const&) { \ + ::boost::detail::test_results(); \ + } \ + catch(...) { \ + ::boost::detail::throw_failed_impl \ + (#EXCEP, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION); \ + } \ + // +#else + #define BOOST_TEST_THROWS( EXPR, EXCEP ) +#endif + +#endif // #ifndef BOOST_CORE_LIGHTWEIGHT_TEST_HPP diff --git a/src/boost/libs/leaf/test/boost_exception_test.cpp b/src/boost/libs/leaf/test/boost_exception_test.cpp new file mode 100644 index 000000000..1ed1bb972 --- /dev/null +++ b/src/boost/libs/leaf/test/boost_exception_test.cpp @@ -0,0 +1,126 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#if defined(BOOST_LEAF_NO_EXCEPTIONS) || !defined(BOOST_LEAF_BOOST_AVAILABLE) + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/pred.hpp> +#endif + +#include "lightweight_test.hpp" +#include <boost/exception/info.hpp> +#include <boost/exception/get_error_info.hpp> + +namespace leaf = boost::leaf; + +struct test_ex: std::exception { }; + +typedef boost::error_info<struct test_info_, int> test_info; + +int main() +{ + static_assert(std::is_same<test_info, decltype(std::declval<leaf::match<test_info, 42>>().matched)>::value, "handler_argument_traits deduction bug"); + + using tr = leaf::leaf_detail::handler_argument_traits<leaf::match<test_info, 42>>; + static_assert(std::is_same<void, tr::error_type>::value, "handler_argument_traits deduction bug"); + + { + int r = leaf::try_catch( + [] + { + try + { + boost::throw_exception(test_ex()); + } + catch( boost::exception & ex ) + { + ex << test_info(42); + throw; + } + return 0; + }, + []( test_info x ) + { + BOOST_TEST_EQ(x.value(), 42); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + + { + int r = leaf::try_catch( + [] + { + try + { + boost::throw_exception(test_ex()); + } + catch( boost::exception & ex ) + { + ex << test_info(42); + throw; + } + return 0; + }, + []( leaf::match<test_info, 42> ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + + { + int r = leaf::try_catch( + [] + { + try + { + boost::throw_exception(test_ex()); + } + catch( boost::exception & ex ) + { + ex << test_info(42); + throw; + } + return 0; + }, + []( leaf::match<test_info, 41> ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 2); + } + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/capture_exception_async_test.cpp b/src/boost/libs/leaf/test/capture_exception_async_test.cpp new file mode 100644 index 000000000..57a5ea7b0 --- /dev/null +++ b/src/boost/libs/leaf/test/capture_exception_async_test.cpp @@ -0,0 +1,164 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#if defined(BOOST_LEAF_NO_EXCEPTIONS) || defined(BOOST_LEAF_NO_THREADS) || !BOOST_LEAF_CFG_CAPTURE + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/capture.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/exception.hpp> +# include <boost/leaf/on_error.hpp> +#endif + +#include "lightweight_test.hpp" +#include <vector> +#include <future> +#include <iterator> +#include <algorithm> + +namespace leaf = boost::leaf; + +template <int> struct info { int value; }; + +struct fut_info +{ + int a; + int b; + int result; + std::future<int> fut; +}; + +template <class H, class F> +std::vector<fut_info> launch_tasks( int task_count, F f ) +{ + BOOST_LEAF_ASSERT(task_count>0); + std::vector<fut_info> fut; + std::generate_n( std::back_inserter(fut), task_count, + [=] + { + int const a = rand(); + int const b = rand(); + int const res = (rand()%10) - 5; + return fut_info { a, b, res, std::async( std::launch::async, + [=] + { + return leaf::capture(leaf::make_shared_context<H>(), f, a, b, res); + } ) }; + } ); + return fut; +} + +int main() +{ + int received_a, received_b; + auto error_handlers = std::make_tuple( + [&received_a, &received_b]( info<1> const & x1, info<2> const & x2, info<4> const & ) + { + received_a = x1.value; + received_b = x2.value; + return -1; + }, + [] + { + return -2; + } ); + + { + std::vector<fut_info> fut = launch_tasks<decltype(error_handlers)>( + 100, + []( int a, int b, int res ) + { + if( res >= 0 ) + return res; + else + throw leaf::exception(info<1>{a}, info<2>{b}, info<3>{}); + } ); + + for( auto & f : fut ) + { + f.fut.wait(); + received_a = received_b = 0; + int r = leaf::try_catch( + [&] + { + auto load = leaf::on_error( info<4>{} ); + + // Calling future_get is required in order to make the on_error (above) work. + return leaf::future_get(f.fut); + }, + error_handlers ); + if( f.result>=0 ) + BOOST_TEST_EQ(r, f.result); + else + { + BOOST_TEST_EQ(r, -1); + BOOST_TEST_EQ(received_a, f.a); + BOOST_TEST_EQ(received_b, f.b); + } + } + } + + { + std::vector<fut_info> fut = launch_tasks<decltype(error_handlers)>( + 100, + []( int a, int b, int res ) + { + if( res >= 0 ) + return res; + else + throw leaf::exception(info<1>{a}, info<2>{b}, info<3>{}); + } ); + + for( auto & f : fut ) + { + f.fut.wait(); + received_a = received_b = 0; + int r = leaf::try_catch( + [&] + { + auto load = leaf::on_error( info<4>{} ); + + return leaf::try_catch( + [&] + { + // Not calling future_get, a on_error in this scope won't work correctly. + // This is to verify that the on_error in the outer scope (above) works. + return f.fut.get(); + }, + []() -> int + { + throw; + } ); + }, + error_handlers ); + if( f.result>=0 ) + BOOST_TEST_EQ(r, f.result); + else + { + BOOST_TEST_EQ(r, -1); + BOOST_TEST_EQ(received_a, f.a); + BOOST_TEST_EQ(received_b, f.b); + } + } + } + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/capture_exception_result_async_test.cpp b/src/boost/libs/leaf/test/capture_exception_result_async_test.cpp new file mode 100644 index 000000000..48df5ead2 --- /dev/null +++ b/src/boost/libs/leaf/test/capture_exception_result_async_test.cpp @@ -0,0 +1,163 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#if defined(BOOST_LEAF_NO_EXCEPTIONS) || defined(BOOST_LEAF_NO_THREADS) || !BOOST_LEAF_CFG_CAPTURE + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/capture.hpp> +# include <boost/leaf/result.hpp> +# include <boost/leaf/handle_errors.hpp> +#endif + +#include "lightweight_test.hpp" +#include <vector> +#include <future> +#include <iterator> +#include <algorithm> + +namespace leaf = boost::leaf; + +template <int> struct info { int value; }; + +struct fut_info +{ + int a; + int b; + int result; + std::future<leaf::result<int>> fut; +}; + +template <class H, class F> +std::vector<fut_info> launch_tasks( int task_count, F f ) +{ + BOOST_LEAF_ASSERT(task_count>0); + std::vector<fut_info> fut; + std::generate_n( std::back_inserter(fut), task_count, + [=] + { + int const a = rand(); + int const b = rand(); + int const res = (rand()%10) - 5; + return fut_info { a, b, res, std::async( std::launch::async, + [=] + { + return leaf::capture(leaf::make_shared_context<H>(), f, a, b, res); + } ) }; + } ); + return fut; +} + +int main() +{ + int received_a, received_b; + auto error_handlers = std::make_tuple( + [&]( info<1> const & x1, info<2> const & x2, info<4> const & ) + { + received_a = x1.value; + received_b = x2.value; + return -1; + }, + [] + { + return -2; + } ); + + { + std::vector<fut_info> fut = launch_tasks<decltype(error_handlers)>( + 100, + []( int a, int b, int res ) -> leaf::result<int> + { + if( res >= 0 ) + return res; + else + return leaf::new_error( info<1>{a}, info<2>{b}, info<3>{} ); + } ); + + for( auto & f : fut ) + { + f.fut.wait(); + received_a = received_b = 0; + int r = leaf::try_handle_all( + [&] + { + auto load = leaf::on_error( info<4>{} ); + + // Calling future_get is required in order to make the on_error (above) work. + return leaf::future_get(f.fut); + }, + error_handlers ); + if( f.result>=0 ) + BOOST_TEST_EQ(r, f.result); + else + { + BOOST_TEST_EQ(r, -1); + BOOST_TEST_EQ(received_a, f.a); + BOOST_TEST_EQ(received_b, f.b); + } + } + } + + { + std::vector<fut_info> fut = launch_tasks<decltype(error_handlers)>( + 100, + []( int a, int b, int res ) -> leaf::result<int> + { + if( res >= 0 ) + return res; + else + return leaf::new_error( info<1>{a}, info<2>{b}, info<3>{} ); + } ); + + for( auto & f : fut ) + { + f.fut.wait(); + received_a = received_b = 0; + int r = leaf::try_handle_all( + [&] + { + auto load = leaf::on_error( info<4>{} ); + + return leaf::try_handle_some( + [&] + { + // Not calling future_get, a on_error in this scope won't work correctly. + // This is to verify that the on_error in the outer scope (above) works. + return f.fut.get(); + }, + []( leaf::error_info const & err ) + { + return err.error(); + } ); + }, + error_handlers ); + if( f.result>=0 ) + BOOST_TEST_EQ(r, f.result); + else + { + BOOST_TEST_EQ(r, -1); + BOOST_TEST_EQ(received_a, f.a); + BOOST_TEST_EQ(received_b, f.b); + } + } + } + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/capture_exception_state_test.cpp b/src/boost/libs/leaf/test/capture_exception_state_test.cpp new file mode 100644 index 000000000..9e3b9cfa8 --- /dev/null +++ b/src/boost/libs/leaf/test/capture_exception_state_test.cpp @@ -0,0 +1,93 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#if defined(BOOST_LEAF_NO_EXCEPTIONS) || !BOOST_LEAF_CFG_CAPTURE + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/capture.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/exception.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +int count = 0; + +template <int> +struct info +{ + info() noexcept + { + ++count; + } + + info( info const & ) noexcept + { + ++count; + } + + ~info() noexcept + { + --count; + } +}; + +int main() +{ + auto error_handlers = std::make_tuple( + []( info<1>, info<3> ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(count, 0); + std::exception_ptr ep; + try + { + leaf::capture( + leaf::make_shared_context(error_handlers), + [] + { + throw leaf::exception(info<1>{}, info<3>{}); + } ); + BOOST_TEST(false); + } + catch(...) + { + ep = std::current_exception(); + } + BOOST_TEST_EQ(count, 2); + int r = leaf::try_catch( + [&]() -> int + { + std::rethrow_exception(ep); + }, + error_handlers ); + BOOST_TEST_EQ(r, 1); + ep = std::exception_ptr(); + BOOST_TEST_EQ(count, 0); + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/capture_exception_unload_test.cpp b/src/boost/libs/leaf/test/capture_exception_unload_test.cpp new file mode 100644 index 000000000..c784c8310 --- /dev/null +++ b/src/boost/libs/leaf/test/capture_exception_unload_test.cpp @@ -0,0 +1,214 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#if defined(BOOST_LEAF_NO_EXCEPTIONS) || !BOOST_LEAF_CFG_CAPTURE + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/capture.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/exception.hpp> +# include <boost/leaf/on_error.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> struct info { int value; }; + +template <class... E, class F> +void test( F f_ ) +{ + auto f = + [=] + { + try + { + leaf::capture( std::make_shared<leaf::leaf_detail::polymorphic_context_impl<leaf::context<info<1>, info<2>, info<3>>>>(), f_); + BOOST_TEST(false); + return std::exception_ptr(); + } + catch(...) + { + return std::current_exception(); + } + }; + + { + int c=0; + auto ep = f(); + leaf::try_catch( + [&ep] + { + return std::rethrow_exception(ep); + }, + [&c]( info<1> const & x ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c] + { + BOOST_TEST_EQ(c, 0); + c = 2; + } ); + BOOST_TEST_EQ(c, 1); + } + + { + int c=0; + auto ep = f(); + leaf::try_catch( + [&ep] + { + return std::rethrow_exception(ep); + }, + [&c]( info<2> const & x ) + { + BOOST_TEST_EQ(x.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c] + { + BOOST_TEST_EQ(c, 0); + c = 2; + } ); + BOOST_TEST_EQ(c, 2); + } + + { + auto ep = f(); + int what = leaf::try_catch( + [&ep] + { + std::rethrow_exception(ep); return 0; + }, + []( info<1> const & x ) + { + BOOST_TEST_EQ(x.value, 1); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(what, 1); + } + + { + auto ep = f(); + int what = leaf::try_catch( + [&ep] + { + std::rethrow_exception(ep); return 0; + }, + []( info<2> const & x ) + { + BOOST_TEST_EQ(x.value, 2); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(what, 2); + } + + { + auto ep = f(); + bool what = leaf::try_catch( + [&ep] + { + std::rethrow_exception(ep); return true; + }, + []( info<1> const & x, info<2> const & ) + { + return true; + }, + []( info<1> const & x, info<3> const & y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 3); + return false; + }, + []( info<1> const & x ) + { + return true; + }, + [] + { + return true; + } ); + BOOST_TEST(!what); + } + + { + auto ep = f(); + bool what = leaf::try_catch( + [&ep] + { + std::rethrow_exception(ep); return false; + }, + []( info<1> const & x, info<2> const & ) + { + return false; + }, + []( info<1> const & x, info<3> const & y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 3); + return true; + }, + []( info<1> const & x ) + { + return false; + }, + [] + { + return false; + } ); + BOOST_TEST(what); + } +} + +int main() +{ + test<info<1>, info<2>, info<3>>( + [] + { + throw leaf::exception(info<1>{1}, info<3>{3}); // Derives from leaf::leaf::error_id + } ); + test<info<1>, info<2>, info<3>>( + [] + { + auto load = leaf::on_error( info<1>{1}, info<3>{3} ); + throw leaf::exception(); // Derives from leaf::leaf::error_id + } ); + test<info<1>, info<2>, info<3>>( + [] + { + auto load = leaf::on_error( info<1>{1}, info<3>{3} ); + throw std::exception(); // Does not derive from leaf::leaf::error_id + } ); + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/capture_result_async_test.cpp b/src/boost/libs/leaf/test/capture_result_async_test.cpp new file mode 100644 index 000000000..c6166dc5e --- /dev/null +++ b/src/boost/libs/leaf/test/capture_result_async_test.cpp @@ -0,0 +1,162 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#if defined(BOOST_LEAF_NO_THREADS) || !BOOST_LEAF_CFG_CAPTURE + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/capture.hpp> +# include <boost/leaf/result.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/on_error.hpp> +#endif + +#include "lightweight_test.hpp" +#include <vector> +#include <future> +#include <iterator> +#include <algorithm> + +namespace leaf = boost::leaf; + +template <int> struct info { int value; }; + +struct fut_info +{ + int a; + int b; + int result; + std::future<leaf::result<int>> fut; +}; + +template <class ErrorHandlers, class F> +std::vector<fut_info> launch_tasks( int task_count, F f ) +{ + BOOST_LEAF_ASSERT(task_count>0); + std::vector<fut_info> fut; + std::generate_n( std::back_inserter(fut), task_count, + [=] + { + int const a = rand(); + int const b = rand(); + int const res = (rand()%10) - 5; + return fut_info { a, b, res, std::async( std::launch::async, + [=] + { + return leaf::capture(leaf::make_shared_context<ErrorHandlers>(), f, a, b, res); + } ) }; + } ); + return fut; +} + +int main() +{ + int received_a, received_b; + auto error_handlers = std::make_tuple( + [&received_a, &received_b]( info<1> const & x1, info<2> const & x2, info<4> const & x4 ) + { + received_a = x1.value; + received_b = x2.value; + return -1; + }, + [] + { + return -2; + } ); + + { + std::vector<fut_info> fut = launch_tasks<decltype(error_handlers)>( + 100, + []( int a, int b, int res ) -> leaf::result<int> + { + if( res >= 0 ) + return res; + else + return leaf::new_error( info<1>{a}, info<2>{b}, info<3>{} ); + } ); + + for( auto & f : fut ) + { + f.fut.wait(); + int r = leaf::try_handle_all( + [&] + { + auto load = leaf::on_error( info<4>{} ); + + // Calling future_get is required in order to make the on_error (above) work. + return leaf::future_get(f.fut); + }, + error_handlers ); + if( f.result>=0 ) + BOOST_TEST_EQ(r, f.result); + else + { + BOOST_TEST_EQ(r, -1); + BOOST_TEST_EQ(f.a, received_a); + BOOST_TEST_EQ(f.b, received_b); + } + } + } + + { + std::vector<fut_info> fut = launch_tasks<decltype(error_handlers)>( + 100, + []( int a, int b, int res ) -> leaf::result<int> + { + if( res >= 0 ) + return res; + else + return leaf::new_error( info<1>{a}, info<2>{b}, info<3>{} ); + } ); + + for( auto & f : fut ) + { + f.fut.wait(); + int r = leaf::try_handle_all( + [&] + { + auto load = leaf::on_error( info<4>{} ); + + return leaf::try_handle_some( + [&] + { + // Not calling future_get, a on_error in this scope won't work correctly. + // This is to verify that the on_error in the outer scope (above) works. + return f.fut.get(); + }, + []( leaf::error_info const & err ) + { + return err.error(); + } ); + }, + error_handlers ); + if( f.result>=0 ) + BOOST_TEST_EQ(r, f.result); + else + { + BOOST_TEST_EQ(r, -1); + BOOST_TEST_EQ(f.a, received_a); + BOOST_TEST_EQ(f.b, received_b); + } + } + } + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/capture_result_state_test.cpp b/src/boost/libs/leaf/test/capture_result_state_test.cpp new file mode 100644 index 000000000..1d1162315 --- /dev/null +++ b/src/boost/libs/leaf/test/capture_result_state_test.cpp @@ -0,0 +1,87 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#if !BOOST_LEAF_CFG_CAPTURE + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/capture.hpp> +# include <boost/leaf/result.hpp> +# include <boost/leaf/handle_errors.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +int count = 0; + +template <int> +struct info +{ + info() noexcept + { + ++count; + } + + info( info const & ) noexcept + { + ++count; + } + + ~info() noexcept + { + --count; + } +}; + +int main() +{ + auto error_handlers = std::make_tuple( + []( info<1>, info<3> ) + { + return 42; + }, + [] + { + return -42; + } ); + + { + auto r = leaf::capture( + leaf::make_shared_context(error_handlers), + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{}, info<3>{} ); + } ); + BOOST_TEST_EQ(count, 2); + + int answer = leaf::try_handle_all( + [&r] + { + return std::move(r); + }, + error_handlers); + BOOST_TEST_EQ(answer, 42); + } + BOOST_TEST_EQ(count, 0); + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/capture_result_unload_test.cpp b/src/boost/libs/leaf/test/capture_result_unload_test.cpp new file mode 100644 index 000000000..add325c05 --- /dev/null +++ b/src/boost/libs/leaf/test/capture_result_unload_test.cpp @@ -0,0 +1,150 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#if !BOOST_LEAF_CFG_CAPTURE + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/capture.hpp> +# include <boost/leaf/result.hpp> +# include <boost/leaf/handle_errors.hpp> +#endif + +#include "_test_ec.hpp" +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> struct info { int value; }; + +template <class F> +void test( F f ) +{ + { + int c=0; + auto r = f(); + leaf::try_handle_all( + [&r]() -> leaf::result<void> + { + BOOST_LEAF_CHECK(std::move(r)); + return { }; + }, + [&c]( info<1> const & x ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c] + { + BOOST_TEST_EQ(c, 0); + c = 2; + } ); + BOOST_TEST_EQ(c, 1); + } + + { + int c=0; + auto r = f(); + leaf::try_handle_all( + [&r]() -> leaf::result<void> + { + BOOST_LEAF_CHECK(std::move(r)); + return { }; + }, + [&c]( info<2> const & x ) + { + BOOST_TEST_EQ(x.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c] + { + BOOST_TEST_EQ(c, 0); + c = 2; + } ); + BOOST_TEST_EQ(c, 2); + } + + { + auto r = f(); + int what = leaf::try_handle_all( + [&r]() -> leaf::result<int> + { + BOOST_LEAF_CHECK(std::move(r)); + return 0; + }, + []( info<1> const & x ) + { + BOOST_TEST_EQ(x.value, 1); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(what, 1); + } + + { + auto r = f(); + int what = leaf::try_handle_all( + [&r]() -> leaf::result<int> + { + BOOST_LEAF_CHECK(std::move(r)); + return 0; + }, + []( info<2> const & x ) + { + BOOST_TEST_EQ(x.value, 2); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(what, 2); + } +} + +int main() +{ + test( [] + { + return leaf::capture( + std::make_shared<leaf::leaf_detail::polymorphic_context_impl<leaf::context<info<1>, info<2>, info<3>>>>(), + []() -> leaf::result<int> + { + return leaf::new_error(errc_a::a0, info<1>{1}, info<3>{3}); + } ); + } ); + + test( [] + { + return leaf::capture( + std::make_shared<leaf::leaf_detail::polymorphic_context_impl<leaf::context<info<1>, info<2>, info<3>>>>(), + []() -> leaf::result<void> + { + return leaf::new_error(errc_a::a0, info<1>{1}, info<3>{3}); + } ); + } ); + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/context_activator_test.cpp b/src/boost/libs/leaf/test/context_activator_test.cpp new file mode 100644 index 000000000..5639c067b --- /dev/null +++ b/src/boost/libs/leaf/test/context_activator_test.cpp @@ -0,0 +1,60 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/context.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> +struct info +{ + int value; +}; + +template <class Ctx> +leaf::result<int> f( Ctx & ctx ) +{ + auto active_context = activate_context(ctx); + return leaf::new_error( info<1>{1} ); +} + +int main() +{ + auto error_handlers = std::make_tuple( + []( info<1> x ) + { + BOOST_TEST(x.value==1); + return 1; + }, + [] + { + return 2; + } ); + + int r = leaf::try_handle_all( + [&] + { + auto ctx = leaf::make_context(error_handlers); + leaf::result<int> r; + { + auto active_context = activate_context(ctx); + r = f(ctx); + } + ctx.propagate(r.error()); + return r; + }, + error_handlers ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/context_deduction_test.cpp b/src/boost/libs/leaf/test/context_deduction_test.cpp new file mode 100644 index 000000000..893f58326 --- /dev/null +++ b/src/boost/libs/leaf/test/context_deduction_test.cpp @@ -0,0 +1,179 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/config.hpp> +# include <boost/leaf/context.hpp> +# include <boost/leaf/pred.hpp> +# include <boost/leaf/handle_errors.hpp> +#endif + +#include "_test_ec.hpp" + +namespace leaf = boost::leaf; + +template <class... T> +struct unwrap_tuple; + +template <> +struct unwrap_tuple<std::tuple<>> +{ + using type = std::tuple<>; +}; + +template <template <class> class S, class... E> +struct unwrap_tuple<std::tuple<S<E>...>> +{ + using type = std::tuple<E...>; +}; + +template <class... H> +typename unwrap_tuple<typename std::decay<decltype(std::declval<typename leaf::context_type_from_handlers<H...>>().tup())>::type>::type * expd( H && ... ) +{ + return 0; +} + +template <class T, class U> +void test( U * ) +{ + static_assert(std::is_same<T,U>::value,"context deduction"); +} + +template <int> struct info { int value; }; + +enum class my_error_code +{ + ok, + error1, + error2, + error3 +}; + +namespace std +{ + template <> struct is_error_code_enum<my_error_code>: std::true_type { }; +} + +enum class my_error_condition +{ + ok, + cond1 +}; + +namespace std +{ + template <> struct is_error_condition_enum<my_error_condition>: std::true_type { }; +} + +void not_called_on_purpose() +{ + test< std::tuple<> >( expd([]( leaf::error_info const & ){ }) ); + + test< std::tuple<info<1>> >( expd([]( info<1> ){ }) ); + test< std::tuple<info<1>> >( expd([]( info<1> const ){ }) ); + test< std::tuple<info<1>> >( expd([]( info<1> const & ){ }) ); + test< std::tuple<info<1>> >( expd([]( info<1>, leaf::error_info const & ){ }) ); + + test< std::tuple<info<1>,info<2>> >( expd([]( info<1> ){ }, []( info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( info<1> ){ }, []( info<1>, info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( info<1>, info<2> ){ }, []( info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( info<1>, info<2> ){ }, []( info<1>, info<2> ){ }) ); + + test< std::tuple<info<1>,info<2>> >( expd([]( leaf::error_info const &, info<1> ){ }, []( info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( leaf::error_info const &, info<1> ){ }, []( info<1>, info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( leaf::error_info const &, info<1>, info<2> ){ }, []( info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( leaf::error_info const &, info<1>, info<2> ){ }, []( info<1>, info<2> ){ }) ); + + test< std::tuple<info<1>,info<2>> >( expd([]( info<1>, leaf::error_info const & ){ }, []( info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( info<1>, leaf::error_info const & ){ }, []( info<1>, info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( info<1>, leaf::error_info const &, info<2> ){ }, []( info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( info<1>, leaf::error_info const &, info<2> ){ }, []( info<1>, info<2> ){ }) ); + + test< std::tuple<info<1>,info<2>> >( expd([]( info<1> ){ }, []( leaf::error_info const &, info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( info<1> ){ }, []( leaf::error_info const &, info<1>, info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( info<1>, info<2> ){ }, []( leaf::error_info const &, info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( info<1>, info<2> ){ }, []( leaf::error_info const &, info<1>, info<2> ){ }) ); + + test< std::tuple<info<1>,info<2>> >( expd([]( info<1> ){ }, []( info<2>, leaf::error_info const & ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( info<1> ){ }, []( info<1>, leaf::error_info const &, info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( info<1>, info<2> ){ }, []( info<2>, leaf::error_info const & ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( info<1>, info<2> ){ }, []( info<1>, leaf::error_info const &, info<2> ){ }) ); + + test< std::tuple<info<1>,info<2>,info<3>> >( expd([]( info<1> ){ }, []( info<2> ){ }, []( info<3> ){ }) ); + test< std::tuple<info<1>,info<2>,info<3>> >( expd([]( info<1> ){ }, []( info<1>, info<2> ){ }, []( info<1>, info<3> ){ }) ); + test< std::tuple<info<1>,info<2>,info<3>> >( expd([]( info<1> ){ }, []( info<1>, info<2> ){ }, []( info<1>, info<3> ){ }) ); + test< std::tuple<info<1>,info<2>,info<3>> >( expd([]( info<1>, info<2> ){ }, []( info<2> ){ }, []( info<3> ){ }) ); + test< std::tuple<info<1>,info<2>,info<3>> >( expd([]( info<1>, info<2> ){ }, []( info<2> ){ }, []( info<3> ){ }) ); + test< std::tuple<info<1>,info<2>,info<3>> >( expd([]( info<1> ){ }, []( info<2> ){ }, []( info<3>, info<2> ){ }) ); + test< std::tuple<info<1>,info<2>,info<3>> >( expd([]( info<1> ){ }, []( info<2> ){ }, []( info<3>, info<2> ){ }) ); + test< std::tuple<info<1>,info<3>,info<2>> >( expd([]( info<1>, info<3> ){ }, []( info<2> ){ }, []( info<3> ){ }) ); + test< std::tuple<info<1>,info<3>,info<2>> >( expd([]( info<1>, info<3> ){ }, []( info<2> ){ }, []( info<3> ){ }) ); + test< std::tuple<info<1>,info<2>,info<3>> >( expd([]( info<1> ){ }, []( info<2>, info<3> ){ }, []( info<3> ){ }) ); + test< std::tuple<info<1>,info<2>,info<3>> >( expd([]( info<1> ){ }, []( info<2>, info<3> ){ }, []( info<3> ){ }) ); + + test< std::tuple<my_error_code> >( expd([]( leaf::match<my_error_code, my_error_code::error1> ){ }) ); +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + test< std::tuple<std::error_code> >( expd([]( leaf::match<leaf::condition<cond_x>, cond_x::x00> ){ }) ); +#endif + + test< std::tuple<info<1>> >( expd([]( leaf::match_value<info<1>,42> ){ }) ); + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + test< std::tuple<std::error_code> >( expd([]( leaf::match<leaf::condition<my_error_condition>, my_error_condition::cond1> ){ }) ); +#if __cplusplus >= 201703L + test< std::tuple<std::error_code> >( expd([]( leaf::match<std::error_code, my_error_code::error1> ){ }) ); + test< std::tuple<std::error_code> >( expd([]( leaf::match<std::error_code, my_error_condition::cond1> ){ }) ); +#endif +#endif + +#ifndef BOOST_LEAF_NO_EXCEPTIONS + test< std::tuple<info<1>> >( expd([]( leaf::catch_<std::exception>, info<1> ){ }) ); +#endif + + test< std::tuple<info<1>,info<2>,info<3>> >( expd([]( info<1> const *, info<2> ){ }, []( info<1>, info<3> const * ){ }) ); + test< std::tuple<info<1>,info<2>,info<3>> >( expd([]( info<1> const, info<2> ){ }, []( info<1> const *, info<3> ){ }) ); + +#if BOOST_LEAF_CFG_DIAGNOSTICS + test< std::tuple<info<1>,info<2>,leaf::leaf_detail::e_unexpected_count> >( expd([]( info<1>, info<2>, leaf::diagnostic_info const & ){ }, []( info<1>, info<2> ){ }) ); + test< std::tuple<info<1>,info<2>,leaf::leaf_detail::e_unexpected_info> >( expd([]( info<1>, info<2> ){ }, []( info<1>, leaf::verbose_diagnostic_info const &, info<2> ){ }) ); + test< std::tuple<info<1>,info<2>,leaf::leaf_detail::e_unexpected_count,leaf::leaf_detail::e_unexpected_info> >( expd([]( info<1>, info<2>, leaf::diagnostic_info const & ){ }, []( info<1>, leaf::verbose_diagnostic_info const &, info<2> ){ }) ); +#else + test< std::tuple<info<1>,info<2>> >( expd([]( info<1>, info<2>, leaf::diagnostic_info const & ){ }, []( info<1>, info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( info<1>, info<2> ){ }, []( info<1>, leaf::verbose_diagnostic_info const &, info<2> ){ }) ); + test< std::tuple<info<1>,info<2>> >( expd([]( info<1>, info<2>, leaf::diagnostic_info const & ){ }, []( info<1>, leaf::verbose_diagnostic_info const &, info<2> ){ }) ); +#endif + + { + auto error_handlers = std::make_tuple( + []( info<2> ) { }, + []( info<3> ) { } ); + test< std::tuple<info<1>, info<2>, info<3>, info<4>, info<5>>>( expd( + []( info<1> ) { }, + error_handlers, + []( info<4>, info<5> ) { } ) ); + } + + { + auto error_handlers1 = std::make_tuple( + []( info<4> ) { }, + []( info<5> ) { } ); + auto error_handlers2 = std::make_tuple( + []( info<2> ) { }, + []( info<3> ) { }, + error_handlers1 ); + test< std::tuple<info<1>, info<2>, info<3>, info<4>, info<5>, info<6>>>( expd( + []( info<1> ) { }, + error_handlers2, + []( info<4>, info<5> ) { }, + []( info<6> ) { } ) ); + } +} + +int main() +{ + return 0; +} diff --git a/src/boost/libs/leaf/test/ctx_handle_all_test.cpp b/src/boost/libs/leaf/test/ctx_handle_all_test.cpp new file mode 100644 index 000000000..1357a8b68 --- /dev/null +++ b/src/boost/libs/leaf/test/ctx_handle_all_test.cpp @@ -0,0 +1,56 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/context.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" +#include <iostream> + +namespace leaf = boost::leaf; + +template <int> +struct info +{ + int value; +}; + +template <class Ctx> +leaf::result<int> f( Ctx & ctx ) +{ + auto active_context = activate_context(ctx); + return leaf::new_error( info<1>{1} ); +} + +int main() +{ + leaf::context<info<1>, leaf::verbose_diagnostic_info const &> ctx; + + { + leaf::result<int> r1 = f(ctx); + BOOST_TEST(!r1); + + int r2 = ctx.handle_error<int>( + r1.error(), + []( info<1> x ) + { + BOOST_TEST(x.value==1); + return 1; + }, + []( leaf::verbose_diagnostic_info const & info ) + { + std::cout << info; + return 2; + } ); + BOOST_TEST_EQ(r2, 1); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/ctx_handle_some_test.cpp b/src/boost/libs/leaf/test/ctx_handle_some_test.cpp new file mode 100644 index 000000000..099ed2151 --- /dev/null +++ b/src/boost/libs/leaf/test/ctx_handle_some_test.cpp @@ -0,0 +1,54 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/context.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> +struct info +{ + int value; +}; + +template <class Ctx> +leaf::result<int> f( Ctx & ctx ) +{ + auto active_context = activate_context(ctx); + return leaf::new_error( info<1>{1} ); +} + +int main() +{ + leaf::context<info<1>> ctx; + + { + leaf::result<int> r1 = f(ctx); + BOOST_TEST(!r1); + + leaf::result<int> r2 = ctx.handle_error<leaf::result<int>>( + r1.error(), + []( info<1> x ) -> leaf::result<int> + { + BOOST_TEST(x.value==1); + return 1; + }, + [&r1] + { + return std::move(r1); + } ); + BOOST_TEST_EQ(r2.value(), 1); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/ctx_remote_handle_all_test.cpp b/src/boost/libs/leaf/test/ctx_remote_handle_all_test.cpp new file mode 100644 index 000000000..71ceb3458 --- /dev/null +++ b/src/boost/libs/leaf/test/ctx_remote_handle_all_test.cpp @@ -0,0 +1,59 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/context.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" +#include <iostream> + +namespace leaf = boost::leaf; + +template <int> +struct info +{ + int value; +}; + +template <class Ctx> +leaf::result<int> f( Ctx & ctx ) +{ + auto active_context = activate_context(ctx); + return leaf::new_error( info<1>{1} ); +} + +int main() +{ + auto handlers = std::make_tuple( + []( info<1> x ) + { + BOOST_TEST(x.value==1); + return 1; + }, + []( leaf::verbose_diagnostic_info const & info ) + { + std::cout << info; + return 2; + } ); + + auto ctx = leaf::make_context(handlers); + + { + leaf::result<int> r1 = f(ctx); + BOOST_TEST(!r1); + + int r2 = ctx.handle_error<int>( + r1.error(), + std::move(handlers)); + BOOST_TEST_EQ(r2, 1); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/ctx_remote_handle_some_test.cpp b/src/boost/libs/leaf/test/ctx_remote_handle_some_test.cpp new file mode 100644 index 000000000..fd5f8bad4 --- /dev/null +++ b/src/boost/libs/leaf/test/ctx_remote_handle_some_test.cpp @@ -0,0 +1,57 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/context.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> +struct info +{ + int value; +}; + +template <class Ctx> +leaf::result<int> f( Ctx & ctx ) +{ + auto active_context = activate_context(ctx); + return leaf::new_error( info<1>{1} ); +} + +int main() +{ + auto handle_error = std::make_tuple( + []( info<1> x ) + { + BOOST_TEST(x.value==1); + return 1; + }, + []( leaf::error_info const & ei ) + { + return ei.error(); + }); + + auto ctx = leaf::make_context(handle_error); + + { + leaf::result<int> r1 = f(ctx); + BOOST_TEST(!r1); + + leaf::result<int> r2 = ctx.handle_error<leaf::result<int>>( + r1.error(), + handle_error ); + BOOST_TEST_EQ(r2.value(), 1); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/defer_basic_test.cpp b/src/boost/libs/leaf/test/defer_basic_test.cpp new file mode 100644 index 000000000..0f460f800 --- /dev/null +++ b/src/boost/libs/leaf/test/defer_basic_test.cpp @@ -0,0 +1,73 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" +#include <sstream> + +namespace leaf = boost::leaf; + +int global; + +int get_global() noexcept +{ + return global; +} + +template <int> +struct info +{ + int value; +}; + +leaf::error_id g() +{ + global = 0; + auto load = leaf::on_error( []{ return info<42>{get_global()}; }, []{ return info<-42>{-42}; } ); + global = 42; + return leaf::new_error(); +} + +leaf::error_id f() +{ + return g(); +} + +int main() +{ + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return f(); + }, + []( info<42> const & i42, leaf::diagnostic_info const & di ) + { + BOOST_TEST_EQ(i42.value, 42); +#if BOOST_LEAF_CFG_STD_STRING + std::stringstream ss; ss << di; + std::string s = ss.str(); + std::cout << s; +#if BOOST_LEAF_CFG_DIAGNOSTICS + BOOST_TEST(s.find("info<-42>")!=s.npos); +#else + BOOST_TEST(s.find("BOOST_LEAF_CFG_DIAGNOSTICS")!=s.npos); +#endif +#endif + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/defer_nested_error_exception_test.cpp b/src/boost/libs/leaf/test/defer_nested_error_exception_test.cpp new file mode 100644 index 000000000..b85d3b608 --- /dev/null +++ b/src/boost/libs/leaf/test/defer_nested_error_exception_test.cpp @@ -0,0 +1,88 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/exception.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> +struct info +{ + int value; +}; + +void f0() +{ + auto load = leaf::on_error( [] { return info<0>{0}; } ); + throw leaf::exception(info<2>{2}); +} + +void f1() +{ + auto load = leaf::on_error( [] { return info<0>{-1}; }, [] { return info<1>{1}; }, [] { return info<2>{-1}; } ); + f0(); +} + +void f2() +{ + try + { + f1(); + } + catch( leaf::error_id const & err ) + { + err.load( info<3>{3} ); + throw; + } +} + +int main() +{ + int r = leaf::try_catch( + [] + { + f2(); + return 0; + }, + []( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST_EQ(i0.value, 0); + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, 2); + BOOST_TEST_EQ(i3.value, 3); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/defer_nested_error_result_test.cpp b/src/boost/libs/leaf/test/defer_nested_error_result_test.cpp new file mode 100644 index 000000000..5d2a7dcc3 --- /dev/null +++ b/src/boost/libs/leaf/test/defer_nested_error_result_test.cpp @@ -0,0 +1,63 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> +struct info +{ + int value; +}; + +leaf::error_id f0() +{ + auto load = leaf::on_error( [] { return info<0>{0}; } ); + return leaf::new_error( info<2>{2} ); +} + +leaf::error_id f1() +{ + auto load = leaf::on_error( [] { return info<0>{-1}; }, [] { return info<1>{1}; }, [] { return info<2>{-1}; } ); + return f0(); +} + +leaf::error_id f2() +{ + return f1().load( info<3>{3} ); +} + +int main() +{ + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return f2(); + }, + []( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST_EQ(i0.value, 0); + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, 2); + BOOST_TEST_EQ(i3.value, 3); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/defer_nested_new_error_exception_test.cpp b/src/boost/libs/leaf/test/defer_nested_new_error_exception_test.cpp new file mode 100644 index 000000000..928d556a0 --- /dev/null +++ b/src/boost/libs/leaf/test/defer_nested_new_error_exception_test.cpp @@ -0,0 +1,95 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/exception.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int A> +struct info +{ + int value; +}; + +void f0() +{ + auto load = leaf::on_error( [] { return info<0>{-1}; } ); + throw leaf::exception(info<1>{-1}); +} + +void f1() +{ + auto load = leaf::on_error( [] { return info<0>{0}; }, [] { return info<1>{1}; }, [] { return info<2>{2}; } ); + try { f0(); } catch(...) { } + throw leaf::exception(); +} + +leaf::error_id f2() +{ + try + { + f1(); + BOOST_TEST(false); + } + catch( leaf::error_id const & err ) + { + err.load( info<3>{3} ); + throw; + } + catch(...) + { + BOOST_TEST(false); + } + return leaf::new_error(); +} + +int main() +{ + int r = leaf::try_catch( + [] + { + f2(); + return 0; + }, + []( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST_EQ(i0.value, 0); + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, 2); + BOOST_TEST_EQ(i3.value, 3); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/defer_nested_new_error_result_test.cpp b/src/boost/libs/leaf/test/defer_nested_new_error_result_test.cpp new file mode 100644 index 000000000..9ba7f4d8c --- /dev/null +++ b/src/boost/libs/leaf/test/defer_nested_new_error_result_test.cpp @@ -0,0 +1,64 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int A> +struct info +{ + int value; +}; + +leaf::error_id f0() +{ + auto load = leaf::on_error( [] { return info<0>{-1}; } ); + return leaf::new_error( info<1>{-1} ); +} + +leaf::error_id f1() +{ + auto load = leaf::on_error( [] { return info<0>{0}; }, [] { return info<1>{1}; }, [] { return info<2>{2}; } ); + (void) f0(); + return leaf::new_error(); +} + +leaf::error_id f2() +{ + return f1().load( info<3>{3} ); +} + +int main() +{ + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return f2(); + }, + []( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST_EQ(i0.value, 0); + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, 2); + BOOST_TEST_EQ(i3.value, 3); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/defer_nested_success_exception_test.cpp b/src/boost/libs/leaf/test/defer_nested_success_exception_test.cpp new file mode 100644 index 000000000..2e559890d --- /dev/null +++ b/src/boost/libs/leaf/test/defer_nested_success_exception_test.cpp @@ -0,0 +1,72 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +void g1() +{ + auto load = leaf::on_error( [] { return info{1}; } ); +} + +void g2() +{ + throw std::exception(); +} + +void f() +{ + auto load = leaf::on_error( [] { return info{2}; } ); + g1(); + g2(); +} + +int main() +{ + int r = leaf::try_catch( + [] + { + f(); + return 0; + }, + []( info x ) + { + BOOST_TEST_EQ(x.value, 2); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/defer_nested_success_result_test.cpp b/src/boost/libs/leaf/test/defer_nested_success_result_test.cpp new file mode 100644 index 000000000..37c3c2827 --- /dev/null +++ b/src/boost/libs/leaf/test/defer_nested_success_result_test.cpp @@ -0,0 +1,58 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +leaf::result<void> g1() +{ + auto load = leaf::on_error( [] { return info{1}; } ); + return { }; +} + +leaf::result<void> g2() +{ + return leaf::new_error(); +} + +leaf::result<void> f() +{ + auto load = leaf::on_error( [] { return info{2}; } ); + BOOST_LEAF_CHECK(g1()); + return g2(); +} + +int main() +{ + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_CHECK(f()); + return 1; + }, + []( info x ) + { + BOOST_TEST_EQ(x.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/diagnostic_info_test.cpp b/src/boost/libs/leaf/test/diagnostic_info_test.cpp new file mode 100644 index 000000000..cbc9afe0c --- /dev/null +++ b/src/boost/libs/leaf/test/diagnostic_info_test.cpp @@ -0,0 +1,375 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/config.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +# include <boost/leaf/common.hpp> +#endif + +#include "lightweight_test.hpp" +#include <sstream> + +namespace leaf = boost::leaf; + +enum class enum_class_payload +{ + value +}; + +template <int A> +struct unexpected_test +{ + int value; +}; + +struct my_exception: + virtual std::exception +{ + char const * what() const noexcept + { + return "my_exception"; + } +}; + +struct printable_payload +{ + friend std::ostream & operator<<( std::ostream & os, printable_payload const & x ) + { + return os << "printed printable_payload"; + } +}; + +struct non_printable_payload +{ +}; + +struct printable_info_printable_payload +{ + printable_payload value; + + friend std::ostream & operator<<( std::ostream & os, printable_info_printable_payload const & x ) + { + return os << "*** printable_info_printable_payload " << x.value << " ***"; + } +}; + +struct printable_info_non_printable_payload +{ + non_printable_payload value; + + friend std::ostream & operator<<( std::ostream & os, printable_info_non_printable_payload const & x ) + { + return os << "*** printable_info_non_printable_payload ***"; + } +}; + +struct non_printable_info_printable_payload +{ + printable_payload value; +}; + +struct non_printable_info_non_printable_payload +{ + non_printable_payload value; +}; + +int main() +{ + leaf::try_handle_all( + []() -> leaf::result<void> + { + return BOOST_LEAF_NEW_ERROR( + printable_info_printable_payload(), + printable_info_non_printable_payload(), + non_printable_info_printable_payload(), + non_printable_info_non_printable_payload(), + unexpected_test<1>{1}, + unexpected_test<2>{2}, + enum_class_payload{}, + leaf::e_errno{ENOENT} ); + }, + []( + leaf::e_source_location, + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload, + enum_class_payload, + leaf::e_errno, + leaf::error_info const & unmatched ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::ostringstream st; + st << unmatched; + std::string s = st.str(); + BOOST_TEST_NE(s.find("leaf::error_info: Error ID = "), s.npos); + std::cout << s; +#endif + }, + []() + { + BOOST_ERROR("Bad error dispatch"); + } ); + + std::cout << __LINE__ << " ----\n"; + + leaf::try_handle_all( + []() -> leaf::result<void> + { + return BOOST_LEAF_NEW_ERROR( + printable_info_printable_payload(), + printable_info_non_printable_payload(), + non_printable_info_printable_payload(), + non_printable_info_non_printable_payload(), + unexpected_test<1>{1}, + unexpected_test<2>{2}, + enum_class_payload{}, + leaf::e_errno{ENOENT} ); + }, + []( + leaf::e_source_location, + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload, + enum_class_payload, + leaf::e_errno, + leaf::diagnostic_info const & unmatched ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::ostringstream st; + st << unmatched; + std::string s = st.str(); +#if BOOST_LEAF_CFG_DIAGNOSTICS + BOOST_TEST_NE(s.find("leaf::diagnostic_info for Error ID = "), s.npos); + BOOST_TEST_NE(s.find("e_source_location"), s.npos); + BOOST_TEST_NE(s.find("*** printable_info_printable_payload printed printable_payload ***"), s.npos); + BOOST_TEST_NE(s.find("*** printable_info_non_printable_payload ***"), s.npos); + BOOST_TEST_NE(s.find(": printed printable_payload"), s.npos); + BOOST_TEST_NE(s.find(": {Non-Printable}"), s.npos); + BOOST_TEST_NE(s.find("enum_class_payload"), s.npos); + BOOST_TEST_NE(s.find("Detected 2 attempts"), s.npos); + BOOST_TEST_NE(s.find("unexpected_test<1>"), s.npos); + BOOST_TEST_EQ(s.find("unexpected_test<2>"), s.npos); +#else + BOOST_TEST_NE(s.find("leaf::diagnostic_info requires #define BOOST_LEAF_CFG_DIAGNOSTICS 1"), s.npos); + BOOST_TEST_NE(s.find("leaf::error_info: Error ID = "), s.npos); +#endif + std::cout << s; +#endif + }, + []() + { + BOOST_ERROR("Bad error dispatch"); + } ); + + std::cout << __LINE__ << " ----\n"; + + leaf::try_handle_all( + []() -> leaf::result<void> + { + return BOOST_LEAF_NEW_ERROR( + printable_info_printable_payload(), + printable_info_non_printable_payload(), + non_printable_info_printable_payload(), + non_printable_info_non_printable_payload(), + unexpected_test<1>{1}, + unexpected_test<2>{2}, + enum_class_payload{}, + leaf::e_errno{ENOENT} ); + }, + []( + leaf::e_source_location, + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload, + enum_class_payload, + leaf::e_errno, + leaf::verbose_diagnostic_info const & di ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::ostringstream st; + st << di; + std::string s = st.str(); +#if BOOST_LEAF_CFG_DIAGNOSTICS + BOOST_TEST_NE(s.find("leaf::verbose_diagnostic_info for Error ID = "), s.npos); + BOOST_TEST_NE(s.find("e_source_location"), s.npos); + BOOST_TEST_NE(s.find("*** printable_info_printable_payload printed printable_payload ***"), s.npos); + BOOST_TEST_NE(s.find("*** printable_info_non_printable_payload ***"), s.npos); + BOOST_TEST_NE(s.find(": printed printable_payload"), s.npos); + BOOST_TEST_NE(s.find(": {Non-Printable}"), s.npos); + BOOST_TEST_NE(s.find("enum_class"), s.npos); + BOOST_TEST_NE(s.find("Unhandled error objects:"), s.npos); + BOOST_TEST_NE(s.find("unexpected_test<1>"), s.npos); + BOOST_TEST_NE(s.find("unexpected_test<2>"), s.npos); + BOOST_TEST_NE(s.find(": 1"), s.npos); + BOOST_TEST_NE(s.find(": 2"), s.npos); +#else + BOOST_TEST_NE(s.find("leaf::verbose_diagnostic_info requires #define BOOST_LEAF_CFG_DIAGNOSTICS 1"), s.npos); + BOOST_TEST_NE(s.find("leaf::error_info: Error ID = "), s.npos); +#endif + std::cout << s; +#endif + }, + []() + { + BOOST_ERROR("Bad error dispatch"); + } ); + + /////////////////////////////////// + +#ifndef BOOST_LEAF_NO_EXCEPTIONS + + std::cout << __LINE__ << " ----\n"; + + leaf::try_catch( + [] + { + BOOST_LEAF_THROW_EXCEPTION( my_exception(), + printable_info_printable_payload(), + printable_info_non_printable_payload(), + non_printable_info_printable_payload(), + non_printable_info_non_printable_payload(), + unexpected_test<1>{1}, + unexpected_test<2>{2}, + enum_class_payload{}, + leaf::e_errno{ENOENT} ); + }, + []( + leaf::e_source_location, + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload, + enum_class_payload, + leaf::e_errno, + leaf::error_info const & unmatched ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::ostringstream st; + st << unmatched; + std::string s = st.str(); + BOOST_TEST_NE(s.find("leaf::error_info: Error ID = "), s.npos); + BOOST_TEST_NE(s.find("Exception dynamic type: "), s.npos); + BOOST_TEST_NE(s.find("std::exception::what(): my_exception"), s.npos); + std::cout << s; +#endif + } ); + + std::cout << __LINE__ << " ----\n"; + + leaf::try_catch( + [] + { + BOOST_LEAF_THROW_EXCEPTION( my_exception(), + printable_info_printable_payload(), + printable_info_non_printable_payload(), + non_printable_info_printable_payload(), + non_printable_info_non_printable_payload(), + enum_class_payload{}, + unexpected_test<1>{1}, + unexpected_test<2>{2}, + leaf::e_errno{ENOENT} ); + }, + []( + leaf::e_source_location, + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload, + enum_class_payload, + leaf::e_errno, + leaf::diagnostic_info const & unmatched ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::ostringstream st; + st << unmatched; + std::string s = st.str(); +#if BOOST_LEAF_CFG_DIAGNOSTICS + BOOST_TEST_NE(s.find("leaf::diagnostic_info for Error ID = "), s.npos); + BOOST_TEST_NE(s.find("Exception dynamic type: "), s.npos); + BOOST_TEST_NE(s.find("std::exception::what(): my_exception"), s.npos); + BOOST_TEST_NE(s.find("e_source_location"), s.npos); + BOOST_TEST_NE(s.find("*** printable_info_printable_payload printed printable_payload ***"), s.npos); + BOOST_TEST_NE(s.find("*** printable_info_non_printable_payload ***"), s.npos); + BOOST_TEST_NE(s.find(": printed printable_payload"), s.npos); + BOOST_TEST_NE(s.find(": {Non-Printable}"), s.npos); + BOOST_TEST_NE(s.find("enum_class_payload"), s.npos); + BOOST_TEST_NE(s.find("Detected 2 attempts"), s.npos); + BOOST_TEST_NE(s.find("unexpected_test<1>"), s.npos); + BOOST_TEST_EQ(s.find("unexpected_test<2>"), s.npos); +#else + BOOST_TEST_NE(s.find("leaf::diagnostic_info requires #define BOOST_LEAF_CFG_DIAGNOSTICS 1"), s.npos); + BOOST_TEST_NE(s.find("leaf::error_info: Error ID = "), s.npos); + BOOST_TEST_NE(s.find("Exception dynamic type: "), s.npos); + BOOST_TEST_NE(s.find("std::exception::what(): my_exception"), s.npos); +#endif + std::cout << s; +#endif + } ); + + std::cout << __LINE__ << " ----\n"; + + leaf::try_catch( + [] + { + BOOST_LEAF_THROW_EXCEPTION( my_exception(), + printable_info_printable_payload(), + printable_info_non_printable_payload(), + non_printable_info_printable_payload(), + non_printable_info_non_printable_payload(), + enum_class_payload{}, + unexpected_test<1>{1}, + unexpected_test<2>{2}, + leaf::e_errno{ENOENT} ); + }, + []( + leaf::e_source_location, + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload, + enum_class_payload, + leaf::e_errno, + leaf::verbose_diagnostic_info const & di ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::ostringstream st; + st << di; + std::string s = st.str(); +#if BOOST_LEAF_CFG_DIAGNOSTICS + BOOST_TEST_NE(s.find("leaf::verbose_diagnostic_info for Error ID = "), s.npos); + BOOST_TEST_NE(s.find("Exception dynamic type: "), s.npos); + BOOST_TEST_NE(s.find("std::exception::what(): my_exception"), s.npos); + BOOST_TEST_NE(s.find("e_source_location"), s.npos); + BOOST_TEST_NE(s.find("*** printable_info_printable_payload printed printable_payload ***"), s.npos); + BOOST_TEST_NE(s.find("*** printable_info_non_printable_payload ***"), s.npos); + BOOST_TEST_NE(s.find(": printed printable_payload"), s.npos); + BOOST_TEST_NE(s.find(": {Non-Printable}"), s.npos); + BOOST_TEST_NE(s.find("enum_class_payload"), s.npos); + BOOST_TEST_NE(s.find("Unhandled error objects:"), s.npos); + BOOST_TEST_NE(s.find("unexpected_test<1>"), s.npos); + BOOST_TEST_NE(s.find("unexpected_test<2>"), s.npos); + BOOST_TEST_NE(s.find(": 1"), s.npos); + BOOST_TEST_NE(s.find(": 2"), s.npos); +#else + BOOST_TEST_NE(s.find("leaf::verbose_diagnostic_info requires #define BOOST_LEAF_CFG_DIAGNOSTICS 1"), s.npos); + BOOST_TEST_NE(s.find("leaf::error_info: Error ID = "), s.npos); + BOOST_TEST_NE(s.find("Exception dynamic type: "), s.npos); + BOOST_TEST_NE(s.find("std::exception::what(): my_exception"), s.npos); +#endif + std::cout << s; +#endif + } ); + +#endif + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/diagnostic_info_test2.cpp b/src/boost/libs/leaf/test/diagnostic_info_test2.cpp new file mode 100644 index 000000000..fbbae7af6 --- /dev/null +++ b/src/boost/libs/leaf/test/diagnostic_info_test2.cpp @@ -0,0 +1,140 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/config.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" +#include <sstream> + +namespace leaf = boost::leaf; + +template <int I> +struct info +{ + friend std::ostream & operator<<( std::ostream & os, info const & x ) + { + return os << "printed info<" << I << '>'; + } +}; + +int main() +{ + leaf::try_handle_all( + []() -> leaf::result<void> + { + return leaf::try_handle_some( + []() -> leaf::result<void> + { + leaf::try_handle_all( + []() -> leaf::result<void> + { + return leaf::new_error(info<1>{}); + }, + [] + { + } ); + return leaf::new_error(info<2>{}); + }, + []( info<1> ) + { + } ); + }, + []( leaf::verbose_diagnostic_info const & di ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::ostringstream st; + st << di; + std::string s = st.str(); +#if BOOST_LEAF_CFG_DIAGNOSTICS + BOOST_TEST_NE(s.find("info<2>"), s.npos); +#endif + std::cout << s; +#endif + } ); + +#ifndef BOOST_LEAF_NO_EXCEPTIONS + + std::cout << __LINE__ << " ----\n"; + + leaf::try_catch( + [] + { + leaf::try_catch( + [] + { + leaf::try_catch( + [] + { + throw leaf::exception(info<1>{}); + }, + [] + { + } ); + throw leaf::exception(info<2>{}); + }, + []( info<1> ) + { + } ); + }, + []( leaf::verbose_diagnostic_info const & di ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::ostringstream st; + st << di; + std::string s = st.str(); +#if BOOST_LEAF_CFG_DIAGNOSTICS + BOOST_TEST_NE(s.find("info<2>"), s.npos); +#endif + std::cout << s; +#endif + } ); + + std::cout << __LINE__ << " ----\n"; + + leaf::try_catch( + [] + { + leaf::try_catch( + [] + { + leaf::try_catch( + [] + { + auto load = leaf::on_error(info<1>{}); + throw std::exception(); + }, + [] + { + } ); + auto load = leaf::on_error(info<2>{}); + throw std::exception(); + }, + []( info<1> ) + { + } ); + }, + []( leaf::verbose_diagnostic_info const & di ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::ostringstream st; + st << di; + std::string s = st.str(); +#if BOOST_LEAF_CFG_DIAGNOSTICS + BOOST_TEST_NE(s.find("info<2>"), s.npos); +#endif + std::cout << s; +#endif + } ); + +#endif + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/e_LastError_test.cpp b/src/boost/libs/leaf/test/e_LastError_test.cpp new file mode 100644 index 000000000..ff8ffc032 --- /dev/null +++ b/src/boost/libs/leaf/test/e_LastError_test.cpp @@ -0,0 +1,63 @@ +// 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) + +#ifndef _WIN32 + +#include <iostream> + +int main() +{ + std::cout << "This test requires Windows"; + return 0; +} + +#else + +#define BOOST_LEAF_CFG_WIN32 1 + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/common.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" +#include <sstream> + +namespace leaf = boost::leaf; + +int main() +{ + SetLastError(ERROR_FILE_NOT_FOUND); +#if BOOST_LEAF_CFG_STD_STRING + std::stringstream ss; + ss << leaf::windows::e_LastError{}; + BOOST_TEST(ss.str().find("The system cannot find the file specified") != std::string::npos); +#endif + + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + SetLastError(ERROR_FILE_NOT_FOUND); + struct reset_LastError { ~reset_LastError() {SetLastError(0); } } reset; + return leaf::new_error( leaf::windows::e_LastError{} ); + }, + []( leaf::windows::e_LastError e ) + { + BOOST_TEST_EQ(GetLastError(), 0); + BOOST_TEST_EQ(e.value, ERROR_FILE_NOT_FOUND); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/e_errno_test.cpp b/src/boost/libs/leaf/test/e_errno_test.cpp new file mode 100644 index 000000000..f8e60a158 --- /dev/null +++ b/src/boost/libs/leaf/test/e_errno_test.cpp @@ -0,0 +1,46 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/common.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" +#include <sstream> + +namespace leaf = boost::leaf; + +int main() +{ + errno = ENOENT; +#if BOOST_LEAF_CFG_STD_STRING + std::stringstream ss; + ss << leaf::e_errno{}; + BOOST_TEST(ss.str().find(std::strerror(ENOENT)) != std::string::npos); +#endif + + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + struct reset_errno { ~reset_errno() { errno=0; } } reset; + return leaf::new_error( leaf::e_errno{} ); + }, + []( leaf::e_errno e ) + { + BOOST_TEST_EQ(errno, 0); + BOOST_TEST_EQ(e.value, ENOENT); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/error_code_test.cpp b/src/boost/libs/leaf/test/error_code_test.cpp new file mode 100644 index 000000000..1a3ec3e8c --- /dev/null +++ b/src/boost/libs/leaf/test/error_code_test.cpp @@ -0,0 +1,618 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#if !BOOST_LEAF_CFG_STD_SYSTEM_ERROR + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/pred.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "_test_res.hpp" +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct e_wrapped_error_code { std::error_code value; }; + +template <class R> +void test() +{ +#if __cplusplus >= 201703L + { + int r = leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_a::a0); + }, + []( leaf::match<std::error_code, leaf::category<errc_a>, leaf::category<errc_b>> code ) + { + std::error_code const & ec = code.matched; + BOOST_TEST_EQ(&ec.category(), &cat_errc_a()); + BOOST_TEST_EQ(ec, errc_a::a0); + return 42; + }, + [] + { + return -42; + } ); + BOOST_TEST_EQ(r, 42); + } + { + int r = leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_b::b0); + }, + []( leaf::match<std::error_code, leaf::category<errc_a>, leaf::category<errc_b>> code ) + { + std::error_code const & ec = code.matched; + BOOST_TEST_EQ(&ec.category(), &cat_errc_b()); + BOOST_TEST_EQ(ec, errc_b::b0); + return 42; + }, + [] + { + return -42; + } ); + BOOST_TEST_EQ(r, 42); + } + { + int r = leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_b::b0); + }, + []( leaf::match<std::error_code, leaf::category<errc_a>, errc_b::b0> code ) + { + std::error_code const & ec = code.matched; + BOOST_TEST_EQ(&ec.category(), &cat_errc_b()); + BOOST_TEST_EQ(ec, errc_b::b0); + return 42; + }, + [] + { + return -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#endif + + { + int r = leaf::try_handle_all( + []() -> R + { + return errc_a::a0; // testing without make_error_code + }, + []( std::error_code const & ec ) + { + BOOST_TEST(!leaf::is_error_id(ec)); + BOOST_TEST_EQ(ec, errc_a::a0); + return 42; + }, + [] + { + return -42; + } ); + BOOST_TEST_EQ(r, 42); + } + { + int r = leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_a::a0); + }, + []( leaf::match<leaf::condition<errc_a>, errc_a::a0> code ) + { + std::error_code const & ec = code.matched; + BOOST_TEST_EQ(ec, errc_a::a0); + return 42; + }, + [] + { + return -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#if __cplusplus >= 201703L + { + int r = leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_a::a0); + }, + []( leaf::match<std::error_code, errc_a::a0> code ) + { + std::error_code const & ec = code.matched; + BOOST_TEST_EQ(ec, errc_a::a0); + return 42; + }, + [] + { + return -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#endif + { + int r = leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_a::a0); + }, + []( leaf::match<leaf::condition<errc_a>, errc_a::a0> code ) + { + std::error_code const & ec = code.matched; + BOOST_TEST_EQ(ec, errc_a::a0); + return 42; + }, + [] + { + return -42; + } ); + BOOST_TEST_EQ(r, 42); + } + { + int r = leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_a::a0); + }, + []( leaf::match<leaf::condition<cond_x>, cond_x::x00> cond ) + { + std::error_code const & ec = cond.matched; + BOOST_TEST_EQ(ec, errc_a::a0); + BOOST_TEST(ec==make_error_condition(cond_x::x00)); + return 42; + }, + [] + { + return -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#if __cplusplus >= 201703L + { + int r = leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_a::a0); + }, + []( leaf::match<std::error_code, cond_x::x00> cond ) + { + std::error_code const & ec = cond.matched; + BOOST_TEST_EQ(ec, errc_a::a0); + BOOST_TEST(ec==make_error_condition(cond_x::x00)); + return 42; + }, + [] + { + return -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#endif + + { + int r = leaf::try_handle_all( + []() -> R + { + return leaf::new_error( e_wrapped_error_code { make_error_code(errc_a::a0) } ).to_error_code(); + }, + []( e_wrapped_error_code const & wec ) + { + std::error_code const & ec = wec.value; + BOOST_TEST_EQ(ec, errc_a::a0); + return 42; + }, + [] + { + return -42; + } ); + BOOST_TEST_EQ(r, 42); + } + { + int r = leaf::try_handle_all( + []() -> R + { + return leaf::new_error( e_wrapped_error_code { make_error_code(errc_a::a0) } ).to_error_code(); + }, + []( leaf::match_value<leaf::condition<e_wrapped_error_code, errc_a>, errc_a::a0> code ) + { + e_wrapped_error_code const & wec = code.matched; + std::error_code const & ec = wec.value; + BOOST_TEST_EQ(ec, errc_a::a0); + return 42; + }, + [] + { + return -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#if __cplusplus >= 201703L + { + int r = leaf::try_handle_all( + []() -> R + { + return leaf::new_error( e_wrapped_error_code { make_error_code(errc_a::a0) } ).to_error_code(); + }, + []( leaf::match_value<e_wrapped_error_code, errc_a::a0> code ) + { + e_wrapped_error_code const & wec = code.matched; + std::error_code const & ec = wec.value; + BOOST_TEST_EQ(ec, errc_a::a0); + return 42; + }, + [] + { + return -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#endif + { + int r = leaf::try_handle_all( + []() -> R + { + return leaf::new_error( e_wrapped_error_code { make_error_code(errc_a::a0) } ).to_error_code(); + }, + []( leaf::match_value<leaf::condition<e_wrapped_error_code, cond_x>, cond_x::x00> cond ) + { + e_wrapped_error_code const & wec = cond.matched; + std::error_code const & ec = wec.value; + BOOST_TEST_EQ(ec, errc_a::a0); + BOOST_TEST(ec==make_error_condition(cond_x::x00)); + return 42; + }, + [] + { + return -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#if __cplusplus >= 201703L + { + int r = leaf::try_handle_all( + []() -> R + { + return leaf::new_error( e_wrapped_error_code { make_error_code(errc_a::a0) } ).to_error_code(); + }, + []( leaf::match_value<e_wrapped_error_code, cond_x::x00> cond ) + { + e_wrapped_error_code const & wec = cond.matched; + std::error_code const & ec = wec.value; + BOOST_TEST_EQ(ec, errc_a::a0); + BOOST_TEST(ec==make_error_condition(cond_x::x00)); + return 42; + }, + [] + { + return -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#endif +} + +template <class R> +void test_void() +{ +#if __cplusplus >= 201703L + { + int r = 0; + leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_a::a0); + }, + [&]( leaf::match<std::error_code, leaf::category<errc_a>, leaf::category<errc_b>> code ) + { + std::error_code const & ec = code.matched; + BOOST_TEST_EQ(&ec.category(), &cat_errc_a()); + BOOST_TEST_EQ(ec, errc_a::a0); + r = 42; + }, + [&] + { + r = -42; + } ); + BOOST_TEST_EQ(r, 42); + } + { + int r = 0; + leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_b::b0); + }, + [&]( leaf::match<std::error_code, leaf::category<errc_a>, leaf::category<errc_b>> code ) + { + std::error_code const & ec = code.matched; + BOOST_TEST_EQ(&ec.category(), &cat_errc_b()); + BOOST_TEST_EQ(ec, errc_b::b0); + r = 42; + }, + [&] + { + r = -42; + } ); + BOOST_TEST_EQ(r, 42); + } + { + int r = 0; + leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_b::b0); + }, + [&]( leaf::match<std::error_code, leaf::category<errc_a>, errc_b::b0> code ) + { + std::error_code const & ec = code.matched; + BOOST_TEST_EQ(&ec.category(), &cat_errc_b()); + BOOST_TEST_EQ(ec, errc_b::b0); + r = 42; + }, + [&] + { + r = -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#endif + + { + int r = 0; + leaf::try_handle_all( + [&]() -> R + { + return errc_a::a0; // testing without make_error_code + }, + [&]( std::error_code const & ec ) + { + BOOST_TEST(!leaf::is_error_id(ec)); + BOOST_TEST_EQ(ec, errc_a::a0); + r = 42; + }, + [&] + { + r = -42; + } ); + BOOST_TEST_EQ(r, 42); + } + { + int r = 0; + leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_a::a0); + }, + [&]( leaf::match<leaf::condition<errc_a>, errc_a::a0> code ) + { + std::error_code const & ec = code.matched; + BOOST_TEST_EQ(ec, errc_a::a0); + r = 42; + }, + [&] + { + r = -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#if __cplusplus >= 201703L + { + int r = 0; + leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_a::a0); + }, + [&]( leaf::match<std::error_code, errc_a::a0> code ) + { + std::error_code const & ec = code.matched; + BOOST_TEST_EQ(ec, errc_a::a0); + r = 42; + }, + [&] + { + r = -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#endif + { + int r = 0; + leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_a::a0); + }, + [&]( leaf::match<leaf::condition<errc_a>, errc_a::a0> code ) + { + std::error_code const & ec = code.matched; + BOOST_TEST_EQ(ec, errc_a::a0); + r = 42; + }, + [&] + { + r = -42; + } ); + BOOST_TEST_EQ(r, 42); + } + { + int r = 0; + leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_a::a0); + }, + [&]( leaf::match<leaf::condition<cond_x>, cond_x::x00> cond ) + { + std::error_code const & ec = cond.matched; + BOOST_TEST_EQ(ec, errc_a::a0); + BOOST_TEST(ec==make_error_condition(cond_x::x00)); + r = 42; + }, + [&] + { + r = -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#if __cplusplus >= 201703L + { + int r = 0; + leaf::try_handle_all( + []() -> R + { + return make_error_code(errc_a::a0); + }, + [&]( leaf::match<std::error_code, cond_x::x00> cond ) + { + std::error_code const & ec = cond.matched; + BOOST_TEST_EQ(ec, errc_a::a0); + BOOST_TEST(ec==make_error_condition(cond_x::x00)); + r = 42; + }, + [&] + { + r = -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#endif + + { + int r = 0; + leaf::try_handle_all( + []() -> R + { + return leaf::new_error( e_wrapped_error_code { make_error_code(errc_a::a0) } ).to_error_code(); + }, + [&]( e_wrapped_error_code const & wec ) + { + std::error_code const & ec = wec.value; + BOOST_TEST_EQ(ec, errc_a::a0); + r = 42; + }, + [&] + { + r = -42; + } ); + BOOST_TEST_EQ(r, 42); + } + { + int r = 0; + leaf::try_handle_all( + []() -> R + { + return leaf::new_error( e_wrapped_error_code { make_error_code(errc_a::a0) } ).to_error_code(); + }, + [&]( leaf::match_value<leaf::condition<e_wrapped_error_code, errc_a>, errc_a::a0> code ) + { + e_wrapped_error_code const & wec = code.matched; + std::error_code const & ec = wec.value; + BOOST_TEST_EQ(ec, errc_a::a0); + r = 42; + }, + [&] + { + r = -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#if __cplusplus >= 201703L + { + int r = 0; + leaf::try_handle_all( + []() -> R + { + return leaf::new_error( e_wrapped_error_code { make_error_code(errc_a::a0) } ).to_error_code(); + }, + [&]( leaf::match_value<e_wrapped_error_code, errc_a::a0> code ) + { + e_wrapped_error_code const & wec = code.matched; + std::error_code const & ec = wec.value; + BOOST_TEST_EQ(ec, errc_a::a0); + r = 42; + }, + [&] + { + r = -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#endif + { + int r = 0; + leaf::try_handle_all( + []() -> R + { + return leaf::new_error( e_wrapped_error_code { make_error_code(errc_a::a0) } ).to_error_code(); + }, + [&]( leaf::match_value<leaf::condition<e_wrapped_error_code, cond_x>, cond_x::x00> cond ) + { + e_wrapped_error_code const & wec = cond.matched; + std::error_code const & ec = wec.value; + BOOST_TEST_EQ(ec, errc_a::a0); + BOOST_TEST(ec==make_error_condition(cond_x::x00)); + r = 42; + }, + [&] + { + r = -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#if __cplusplus >= 201703L + { + int r = 0; + leaf::try_handle_all( + []() -> R + { + return leaf::new_error( e_wrapped_error_code { make_error_code(errc_a::a0) } ).to_error_code(); + }, + [&]( leaf::match_value<e_wrapped_error_code, cond_x::x00> cond ) + { + e_wrapped_error_code const & wec = cond.matched; + std::error_code const & ec = wec.value; + BOOST_TEST_EQ(ec, errc_a::a0); + BOOST_TEST(ec==make_error_condition(cond_x::x00)); + r = 42; + }, + [&] + { + r = -42; + } ); + BOOST_TEST_EQ(r, 42); + } +#endif +} + +int main() +{ + test<leaf::result<int>>(); + test<test_res<int, std::error_code>>(); + test_void<leaf::result<void>>(); + test_void<test_res<void, std::error_code>>(); + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/error_id_test.cpp b/src/boost/libs/leaf/test/error_id_test.cpp new file mode 100644 index 000000000..904aecd7e --- /dev/null +++ b/src/boost/libs/leaf/test/error_id_test.cpp @@ -0,0 +1,127 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/error.hpp> +#endif + +#include "lightweight_test.hpp" +#include <future> +#include <vector> +#include <algorithm> +#include <iterator> + +namespace leaf = boost::leaf; + +constexpr int ids_per_thread = 10000; + +std::vector<int> generate_ids() +{ + std::vector<int> ids; + ids.reserve(ids_per_thread); + for(int i=0; i!=ids_per_thread-1; ++i) + { + int id = leaf::leaf_detail::new_id(); + BOOST_TEST_NE(id&1, 0); + int last = leaf::leaf_detail::current_id(); + BOOST_TEST_EQ(last, leaf::leaf_detail::current_id()); + BOOST_TEST_NE(last&1, 0); + BOOST_TEST_EQ(last, id); + ids.push_back(id); + } + return ids; +} + +int main() +{ + { + leaf::error_id e1; + leaf::error_id e2; + BOOST_TEST(!e1); + BOOST_TEST_EQ(e1.value(), 0); + BOOST_TEST(!e2); + BOOST_TEST_EQ(e2.value(), 0); + BOOST_TEST(e1==e2); + BOOST_TEST(!(e1!=e2)); + BOOST_TEST(!(e1<e2)); + BOOST_TEST(!(e2<e1)); + } + { + leaf::error_id e1; + leaf::error_id e2 = leaf::new_error(); + BOOST_TEST(!e1); + BOOST_TEST_EQ(e1.value(), 0); + BOOST_TEST(e2); + BOOST_TEST_EQ(e2.value(), 1); + BOOST_TEST(!(e1==e2)); + BOOST_TEST(e1!=e2); + BOOST_TEST(e1<e2); + BOOST_TEST(!(e2<e1)); + } + { + leaf::error_id e1 = leaf::new_error(); + leaf::error_id e2 = leaf::new_error(); + BOOST_TEST(e1); + BOOST_TEST_EQ(e1.value(), 5); + BOOST_TEST(e2); + BOOST_TEST_EQ(e2.value(), 9); + BOOST_TEST(!(e1==e2)); + BOOST_TEST(e1!=e2); + BOOST_TEST(e1<e2); + BOOST_TEST(!(e2<e1)); + } + { + leaf::error_id e1 = leaf::new_error(); + leaf::error_id e2 = e1; + BOOST_TEST(e1); + BOOST_TEST_EQ(e1.value(), 13); + BOOST_TEST(e2); + BOOST_TEST_EQ(e2.value(), 13); + BOOST_TEST(e1==e2); + BOOST_TEST(!(e1!=e2)); + BOOST_TEST(!(e1<e2)); + BOOST_TEST(!(e2<e1)); + } + { + leaf::error_id e1 = leaf::new_error(); + leaf::error_id e2; e2 = e1; + BOOST_TEST(e1); + BOOST_TEST_EQ(e1.value(), 17); + BOOST_TEST(e2); + BOOST_TEST_EQ(e2.value(), 17); + BOOST_TEST(e1==e2); + BOOST_TEST(!(e1!=e2)); + BOOST_TEST(!(e1<e2)); + BOOST_TEST(!(e2<e1)); + } +#ifdef BOOST_LEAF_NO_THREADS + std::vector<int> all_ids = generate_ids(); +#else + constexpr int thread_count = 100; + using thread_ids = std::future<std::vector<int>>; + std::vector<thread_ids> fut; + fut.reserve(thread_count); + std::generate_n( + std::back_inserter(fut), + thread_count, + [=] + { + return std::async(std::launch::async, &generate_ids); + }); + std::vector<int> all_ids; + for(auto & f : fut) + { + auto fv = f.get(); + all_ids.insert(all_ids.end(), fv.begin(), fv.end()); + } +#endif + std::sort(all_ids.begin(), all_ids.end()); + auto u = std::unique(all_ids.begin(), all_ids.end()); + BOOST_TEST(u==all_ids.end()); + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/exception_test.cpp b/src/boost/libs/leaf/test/exception_test.cpp new file mode 100644 index 000000000..50949b54a --- /dev/null +++ b/src/boost/libs/leaf/test/exception_test.cpp @@ -0,0 +1,311 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/pred.hpp> +# include <boost/leaf/exception.hpp> +# include <boost/leaf/on_error.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +struct abstract_base_exception +{ + virtual ~abstract_base_exception() { } + virtual int get_val() const = 0; +}; + +struct my_exception: + std::exception, + abstract_base_exception +{ + int val;; + explicit my_exception(int val): val{val} { } + int get_val() const { return val; } +}; + +int get_val( abstract_base_exception const & ex ) +{ + return ex.get_val(); +} + +int get_val( my_exception const & ex ) +{ + return ex.val; +} + +int get_val( leaf::catch_<abstract_base_exception> const & ex ) +{ + return ex.matched.get_val(); +} + +int get_val( leaf::catch_<my_exception> const & ex ) +{ + return ex.matched.val; +} + +template <class Ex, class F> +int test( F && f ) +{ + return leaf::try_catch( + [&]() -> int + { + f(); + return 0; + }, + + []( Ex ex, leaf::match_value<info,42>, leaf::e_source_location ) + { + BOOST_TEST_EQ(get_val(ex), 42); + return 20; + }, + []( Ex ex, leaf::match_value<info,42>, info x ) + { + BOOST_TEST_EQ(get_val(ex), 42); + return 21; + }, + []( Ex ex, leaf::e_source_location ) + { + BOOST_TEST_EQ(get_val(ex), 42); + return 22; + }, + []( Ex ex ) + { + BOOST_TEST_EQ(get_val(ex), 42); + return 23; + }, + []( leaf::match_value<info,42>, leaf::e_source_location ) + { + return 40; + }, + []( leaf::match_value<info,42>, info x ) + { + return 41; + }, + []( leaf::e_source_location ) + { + return 42; + }, + [] + { + return 43; + } ); +} + +int main() +{ + BOOST_TEST_EQ(20, test<leaf::catch_<my_exception>>([]{ BOOST_LEAF_THROW_EXCEPTION(my_exception(42), info{42}); })); + BOOST_TEST_EQ(20, test<leaf::catch_<my_exception>>([]{ throw BOOST_LEAF_EXCEPTION(my_exception(42), info{42}); })); + BOOST_TEST_EQ(21, test<leaf::catch_<my_exception>>([]{ throw leaf::exception(my_exception(42), info{42}); })); + BOOST_TEST_EQ(21, test<leaf::catch_<my_exception>>([]{ my_exception exc(42); throw leaf::exception(exc, info{42}); })); + BOOST_TEST_EQ(21, test<leaf::catch_<my_exception>>([]{ my_exception const exc(42); throw leaf::exception(exc, info{42}); })); + BOOST_TEST_EQ(22, test<leaf::catch_<my_exception>>([]{ BOOST_LEAF_THROW_EXCEPTION(my_exception(42)); })); + BOOST_TEST_EQ(22, test<leaf::catch_<my_exception>>([]{ throw BOOST_LEAF_EXCEPTION(my_exception(42)); })); + BOOST_TEST_EQ(23, test<leaf::catch_<my_exception>>([]{ throw leaf::exception(my_exception(42)); })); + BOOST_TEST_EQ(23, test<leaf::catch_<my_exception>>([]{ my_exception exc(42); throw leaf::exception(exc); })); + BOOST_TEST_EQ(23, test<leaf::catch_<my_exception>>([]{ my_exception const exc(42); throw leaf::exception(exc); })); + + BOOST_TEST_EQ(20, test<my_exception const &>([]{ BOOST_LEAF_THROW_EXCEPTION(my_exception(42), info{42}); })); + BOOST_TEST_EQ(20, test<my_exception const &>([]{ throw BOOST_LEAF_EXCEPTION(my_exception(42), info{42}); })); + BOOST_TEST_EQ(21, test<my_exception const &>([]{ throw leaf::exception(my_exception(42), info{42}); })); + BOOST_TEST_EQ(21, test<my_exception const &>([]{ my_exception exc(42); throw leaf::exception(exc, info{42}); })); + BOOST_TEST_EQ(21, test<my_exception const &>([]{ my_exception const exc(42); throw leaf::exception(exc, info{42}); })); + BOOST_TEST_EQ(22, test<my_exception const &>([]{ BOOST_LEAF_THROW_EXCEPTION(my_exception(42)); })); + BOOST_TEST_EQ(22, test<my_exception const &>([]{ throw BOOST_LEAF_EXCEPTION(my_exception(42)); })); + BOOST_TEST_EQ(23, test<my_exception const &>([]{ throw leaf::exception(my_exception(42)); })); + BOOST_TEST_EQ(23, test<my_exception const &>([]{ my_exception exc(42); throw leaf::exception(exc); })); + BOOST_TEST_EQ(23, test<my_exception const &>([]{ my_exception const exc(42); throw leaf::exception(exc); })); + + BOOST_TEST_EQ(40, test<my_exception &>([]{ BOOST_LEAF_THROW_EXCEPTION(info{42}); })); + BOOST_TEST_EQ(40, test<my_exception &>([]{ throw BOOST_LEAF_EXCEPTION(info{42}); })); + BOOST_TEST_EQ(41, test<my_exception &>([]{ throw leaf::exception(info{42}); })); + BOOST_TEST_EQ(41, test<my_exception &>([]{ info inf{42}; throw leaf::exception(inf); })); + BOOST_TEST_EQ(41, test<my_exception &>([]{ info const inf{42}; throw leaf::exception(inf); })); + BOOST_TEST_EQ(42, test<my_exception &>([]{ BOOST_LEAF_THROW_EXCEPTION(); })); + BOOST_TEST_EQ(42, test<my_exception &>([]{ throw BOOST_LEAF_EXCEPTION(); })); + BOOST_TEST_EQ(43, test<my_exception &>([]{ throw leaf::exception(); })); + BOOST_TEST_EQ(23, test<my_exception &>([]{ my_exception exc(42); throw leaf::exception(exc); })); + BOOST_TEST_EQ(23, test<my_exception &>([]{ my_exception const exc(42); throw leaf::exception(exc); })); + + BOOST_TEST_EQ(20, test<my_exception const>([]{ BOOST_LEAF_THROW_EXCEPTION(my_exception(42), info{42}); })); + BOOST_TEST_EQ(20, test<my_exception const>([]{ throw BOOST_LEAF_EXCEPTION(my_exception(42), info{42}); })); + BOOST_TEST_EQ(21, test<my_exception const>([]{ throw leaf::exception(my_exception(42), info{42}); })); + BOOST_TEST_EQ(21, test<my_exception const>([]{ my_exception exc(42); throw leaf::exception(exc, info{42}); })); + BOOST_TEST_EQ(21, test<my_exception const>([]{ my_exception const exc(42); throw leaf::exception(exc, info{42}); })); + BOOST_TEST_EQ(22, test<my_exception const>([]{ BOOST_LEAF_THROW_EXCEPTION(my_exception(42)); })); + BOOST_TEST_EQ(22, test<my_exception const>([]{ throw BOOST_LEAF_EXCEPTION(my_exception(42)); })); + BOOST_TEST_EQ(23, test<my_exception const>([]{ throw leaf::exception(my_exception(42)); })); + BOOST_TEST_EQ(23, test<my_exception const>([]{ my_exception exc(42); throw leaf::exception(exc); })); + BOOST_TEST_EQ(23, test<my_exception const>([]{ my_exception const exc(42); throw leaf::exception(exc); })); + + BOOST_TEST_EQ(40, test<my_exception>([]{ BOOST_LEAF_THROW_EXCEPTION(info{42}); })); + BOOST_TEST_EQ(40, test<my_exception>([]{ throw BOOST_LEAF_EXCEPTION(info{42}); })); + BOOST_TEST_EQ(41, test<my_exception>([]{ throw leaf::exception(info{42}); })); + BOOST_TEST_EQ(41, test<my_exception>([]{ info inf{42}; throw leaf::exception(inf); })); + BOOST_TEST_EQ(41, test<my_exception>([]{ info const inf{42}; throw leaf::exception(inf); })); + BOOST_TEST_EQ(42, test<my_exception>([]{ BOOST_LEAF_THROW_EXCEPTION(); })); + BOOST_TEST_EQ(42, test<my_exception>([]{ throw BOOST_LEAF_EXCEPTION(); })); + BOOST_TEST_EQ(43, test<my_exception>([]{ throw leaf::exception(); })); + BOOST_TEST_EQ(23, test<my_exception>([]{ my_exception exc(42); throw leaf::exception(exc); })); + BOOST_TEST_EQ(23, test<my_exception>([]{ my_exception const exc(42); throw leaf::exception(exc); })); + + BOOST_TEST_EQ(20, test<leaf::catch_<abstract_base_exception>>([]{ BOOST_LEAF_THROW_EXCEPTION(my_exception(42), info{42}); })); + BOOST_TEST_EQ(20, test<leaf::catch_<abstract_base_exception>>([]{ throw BOOST_LEAF_EXCEPTION(my_exception(42), info{42}); })); + BOOST_TEST_EQ(21, test<leaf::catch_<abstract_base_exception>>([]{ throw leaf::exception(my_exception(42), info{42}); })); + BOOST_TEST_EQ(21, test<leaf::catch_<abstract_base_exception>>([]{ my_exception exc(42); throw leaf::exception(exc, info{42}); })); + BOOST_TEST_EQ(21, test<leaf::catch_<abstract_base_exception>>([]{ my_exception const exc(42); throw leaf::exception(exc, info{42}); })); + BOOST_TEST_EQ(22, test<leaf::catch_<abstract_base_exception>>([]{ BOOST_LEAF_THROW_EXCEPTION(my_exception(42)); })); + BOOST_TEST_EQ(22, test<leaf::catch_<abstract_base_exception>>([]{ throw BOOST_LEAF_EXCEPTION(my_exception(42)); })); + BOOST_TEST_EQ(23, test<leaf::catch_<abstract_base_exception>>([]{ throw leaf::exception(my_exception(42)); })); + BOOST_TEST_EQ(23, test<leaf::catch_<abstract_base_exception>>([]{ my_exception exc(42); throw leaf::exception(exc); })); + BOOST_TEST_EQ(23, test<leaf::catch_<abstract_base_exception>>([]{ my_exception const exc(42); throw leaf::exception(exc); })); + + BOOST_TEST_EQ(20, test<abstract_base_exception const &>([]{ BOOST_LEAF_THROW_EXCEPTION(my_exception(42), info{42}); })); + BOOST_TEST_EQ(20, test<abstract_base_exception const &>([]{ throw BOOST_LEAF_EXCEPTION(my_exception(42), info{42}); })); + BOOST_TEST_EQ(21, test<abstract_base_exception const &>([]{ throw leaf::exception(my_exception(42), info{42}); })); + BOOST_TEST_EQ(21, test<abstract_base_exception const &>([]{ my_exception exc(42); throw leaf::exception(exc, info{42}); })); + BOOST_TEST_EQ(21, test<abstract_base_exception const &>([]{ my_exception const exc(42); throw leaf::exception(exc, info{42}); })); + BOOST_TEST_EQ(22, test<abstract_base_exception const &>([]{ BOOST_LEAF_THROW_EXCEPTION(my_exception(42)); })); + BOOST_TEST_EQ(22, test<abstract_base_exception const &>([]{ throw BOOST_LEAF_EXCEPTION(my_exception(42)); })); + BOOST_TEST_EQ(23, test<abstract_base_exception const &>([]{ throw leaf::exception(my_exception(42)); })); + BOOST_TEST_EQ(23, test<abstract_base_exception const &>([]{ my_exception exc(42); throw leaf::exception(exc); })); + BOOST_TEST_EQ(23, test<abstract_base_exception const &>([]{ my_exception const exc(42); throw leaf::exception(exc); })); + + BOOST_TEST_EQ(40, test<abstract_base_exception &>([]{ BOOST_LEAF_THROW_EXCEPTION(info{42}); })); + BOOST_TEST_EQ(40, test<abstract_base_exception &>([]{ throw BOOST_LEAF_EXCEPTION(info{42}); })); + BOOST_TEST_EQ(41, test<abstract_base_exception &>([]{ throw leaf::exception(info{42}); })); + BOOST_TEST_EQ(41, test<abstract_base_exception &>([]{ info inf{42}; throw leaf::exception(inf); })); + BOOST_TEST_EQ(41, test<abstract_base_exception &>([]{ info const inf{42}; throw leaf::exception(inf); })); + BOOST_TEST_EQ(42, test<abstract_base_exception &>([]{ BOOST_LEAF_THROW_EXCEPTION(); })); + BOOST_TEST_EQ(42, test<abstract_base_exception &>([]{ throw BOOST_LEAF_EXCEPTION(); })); + BOOST_TEST_EQ(43, test<abstract_base_exception &>([]{ throw leaf::exception(); })); + BOOST_TEST_EQ(23, test<abstract_base_exception &>([]{ my_exception exc(42); throw leaf::exception(exc); })); + BOOST_TEST_EQ(23, test<abstract_base_exception &>([]{ my_exception const exc(42); throw leaf::exception(exc); })); + + { + char const * wh = 0; + leaf::try_catch( + [] + { + throw std::runtime_error("Test"); + }, + [&]( std::exception const & ex ) + { + wh = ex.what(); + } ); + BOOST_TEST(wh!=0 || !strcmp(wh,"Test")); + } + + { + int const id = leaf::leaf_detail::current_id(); + BOOST_TEST_EQ( 21, test<my_exception const &>( [] + { + auto load = leaf::on_error(info{42}); + throw my_exception(42); + } ) ); + BOOST_TEST_NE(id, leaf::leaf_detail::current_id()); + } + + { + int const id = leaf::leaf_detail::current_id(); + BOOST_TEST_EQ( 21, test<my_exception &>( [] + { + auto load = leaf::on_error(info{42}); + throw my_exception(42); + } ) ); + BOOST_TEST_NE(id, leaf::leaf_detail::current_id()); + } + + { + BOOST_TEST_EQ( 23, test<my_exception const &>( [] + { + int const id = leaf::leaf_detail::current_id(); + try + { + leaf::try_catch( + [] + { + throw my_exception(42); + } ); + } + catch(...) + { + BOOST_TEST_EQ(id, leaf::leaf_detail::current_id()); + throw; + } + } ) ); + } + + { + BOOST_TEST_EQ( 23, test<my_exception &>( [] + { + int const id = leaf::leaf_detail::current_id(); + try + { + leaf::try_catch( + [] + { + throw my_exception(42); + } ); + } + catch(...) + { + BOOST_TEST_EQ(id, leaf::leaf_detail::current_id()); + throw; + } + } ) ); + } + + { + leaf::try_catch( + [] + { + throw leaf::exception( info{42} ); + }, + []( info x ) + { + BOOST_TEST_EQ(x.value, 42); + } ); + int r = leaf::try_catch( + []() -> int + { + throw std::exception(); + }, + []( info x ) + { + return -1; + }, + [] + { + return 1; + } ); + BOOST_TEST_EQ(r, 1); + } + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/exception_to_result_test.cpp b/src/boost/libs/leaf/test/exception_to_result_test.cpp new file mode 100644 index 000000000..c58387558 --- /dev/null +++ b/src/boost/libs/leaf/test/exception_to_result_test.cpp @@ -0,0 +1,132 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/exception.hpp> +# include <boost/leaf/result.hpp> +# include <boost/leaf/handle_errors.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> struct my_exception: std::exception { }; + +int main() +{ + { + int r = leaf::try_handle_all( + [] + { + return leaf::exception_to_result<my_exception<1>,my_exception<2>>( + []() -> int + { + throw my_exception<1>(); + } ); + }, + []( my_exception<1> const &, std::exception_ptr const & ep ) + { + try + { + std::rethrow_exception(ep); + } + catch( my_exception<1> const & ) + { + } + return 1; + }, + []( my_exception<2> const & ) + { + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 1); + } + { + int r = leaf::try_handle_all( + [] + { + return leaf::exception_to_result<my_exception<1>,my_exception<2>>( + []() -> int + { + throw my_exception<2>(); + } ); + }, + []( my_exception<1> const & ) + { + return 1; + }, + []( my_exception<2> const &, std::exception_ptr const & ep ) + { + try + { + std::rethrow_exception(ep); + } + catch( my_exception<2> const & ) + { + } + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + { + int r = leaf::try_handle_all( + [] + { + return leaf::exception_to_result<std::exception,my_exception<1>>( + []() -> int + { + throw my_exception<1>(); + } ); + }, + []( std::exception const &, std::exception_ptr const & ep ) + { + try + { + std::rethrow_exception(ep); + } + catch( my_exception<1> const & ) + { + } + return 1; + }, + []( my_exception<1> const & ) + { + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 1); + } + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/function_traits_test.cpp b/src/boost/libs/leaf/test/function_traits_test.cpp new file mode 100644 index 000000000..e8b8ce5ba --- /dev/null +++ b/src/boost/libs/leaf/test/function_traits_test.cpp @@ -0,0 +1,49 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/detail/function_traits.hpp> +#endif + +#include <functional> + +namespace leaf = boost::leaf; + +template <class F> +void check_traits( F ) +{ + using namespace leaf::leaf_detail; + using leaf::leaf_detail_mp11::mp_list; + static_assert(function_traits<F>::arity==4,"arity"); + static_assert(std::is_same<fn_return_type<F>,double>::value,"return_type"); + static_assert(std::is_same<fn_arg_type<F,0>,int>::value,"arg<0>"); + static_assert(std::is_same<fn_arg_type<F,1>,float>::value,"arg<1>"); + static_assert(std::is_same<fn_arg_type<F,2>,int const &>::value,"arg<2>"); + static_assert(std::is_same<fn_arg_type<F,3>,float &&>::value,"arg<3>"); + static_assert(std::is_same<fn_mp_args<F>,mp_list<int,float,int const &,float &&>>::value,"mp_args"); +} + +double f1( int, float, int const &, float && ) +{ + return 42; +} + +int main() +{ + check_traits(&f1); + check_traits(std::function<double(int const volatile, float const, int const &, float &&)>(f1)); + check_traits( []( int const volatile, float const, int const &, float && ) -> double + { + return 42; + } ); + check_traits( []( int const volatile, float const, int const &, float && ) noexcept -> double + { + return 42; + } ); + static_assert(leaf::leaf_detail::function_traits<int>::arity==-1, "int arity"); + return 0; +} diff --git a/src/boost/libs/leaf/test/handle_all_other_result_test.cpp b/src/boost/libs/leaf/test/handle_all_other_result_test.cpp new file mode 100644 index 000000000..5520dc25c --- /dev/null +++ b/src/boost/libs/leaf/test/handle_all_other_result_test.cpp @@ -0,0 +1,100 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#if !BOOST_LEAF_CFG_STD_SYSTEM_ERROR + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +#endif + +#include "_test_res.hpp" +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> struct info { int value; }; + +template <class ResType> +ResType f( bool succeed ) +{ + if( succeed ) + return 42; + else + return make_error_code(std::errc::no_such_file_or_directory); +} + +template <class ResType> +ResType g( bool succeed ) +{ + if( auto r = f<ResType>(succeed) ) + return r; + else + return leaf::error_id(r.error()).load(info<42>{42}).to_error_code(); +} + +template <class ResType> +void test() +{ + { + int r = leaf::try_handle_all( + [] + { + return g<ResType>(true); + }, + [] + { + return -1; + } ); + BOOST_TEST_EQ(r, 42); + } + { + int r = leaf::try_handle_all( + [&] + { + auto r = g<ResType>(false); + BOOST_TEST(!r); + auto ec = r.error(); + BOOST_TEST_EQ(ec.message(), "LEAF error"); + BOOST_TEST(!std::strcmp(ec.category().name(),"LEAF error")); + return r; + }, + []( info<42> const & x, std::error_code const & ec ) + { + BOOST_TEST_EQ(x.value, 42); + BOOST_TEST_EQ(ec, make_error_code(std::errc::no_such_file_or_directory)); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } +} + +int main() +{ + test<test_res<int, std::error_code>>(); + test<test_res<int const, std::error_code>>(); + test<test_res<int, std::error_code> const>(); + test<test_res<int const, std::error_code> const>(); + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/handle_all_test.cpp b/src/boost/libs/leaf/test/handle_all_test.cpp new file mode 100644 index 000000000..53d6c54d4 --- /dev/null +++ b/src/boost/libs/leaf/test/handle_all_test.cpp @@ -0,0 +1,704 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/pred.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "_test_ec.hpp" +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> struct info { int value; }; + +enum class my_error_code +{ + ok, + error1, + error2, + error3 +}; + +struct e_my_error_code { my_error_code value; }; + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR +struct e_std_error_code { std::error_code value; }; +#endif + +template <class R> +leaf::result<R> f( my_error_code ec ) +{ + if( ec==my_error_code::ok ) + return R(42); + else + return leaf::new_error(ec, e_my_error_code{ec}, info<1>{1}, info<2>{2}, info<3>{3}); +} + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR +template <class R, class Errc> +leaf::result<R> f_errc( Errc ec ) +{ + return leaf::new_error(make_error_code(ec), info<1>{1}, info<2>{2}, info<3>{3}); +} + +template <class R, class Errc> +leaf::result<R> f_errc_wrapped( Errc ec ) +{ + return leaf::new_error(e_std_error_code{make_error_code(ec)}, info<1>{1}, info<2>{2}, info<3>{3}); +} +#endif + +struct move_only +{ + explicit move_only( int value ): value(value) { } + int value; + +#ifndef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS + move_only( move_only const & ) = delete; + move_only( move_only && ) = default; +#endif +}; + +int main() +{ + // void, try_handle_all (success) + { + int c=0; + leaf::try_handle_all( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::ok)); + c = answer; + return { }; + }, + [&c] + { + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST_EQ(c, 42); + } + + // void, try_handle_all (failure) + { + int c=0; + leaf::try_handle_all( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( my_error_code ec, info<1> const & x,info<2> y ) + { + BOOST_TEST(ec==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 2; + } ); + BOOST_TEST_EQ(c, 1); + } + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + // void, try_handle_all (failure), match cond_x (single enum value) + { + int c=0; + leaf::try_handle_all( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f_errc<int>(errc_a::a0)); + c = answer; + return { }; + }, + [&c]( leaf::match<leaf::condition<cond_x>, cond_x::x11> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]( leaf::match<leaf::condition<cond_x>, cond_x::x00> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(ec.matched, make_error_code(errc_a::a0)); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } +#endif + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + // void, try_handle_all (failure), match cond_x (wrapped std::error_code) + { + int c=0; + leaf::try_handle_all( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f_errc_wrapped<int>(errc_a::a0)); + c = answer; + return { }; + }, + [&c]( leaf::match_value<leaf::condition<e_std_error_code, cond_x>, cond_x::x11> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]( leaf::match_value<leaf::condition<e_std_error_code, cond_x>, cond_x::x00> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(ec.matched.value, make_error_code(errc_a::a0)); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } +#endif + + // void, try_handle_all (failure), match enum (single enum value) + { + int c=0; + leaf::try_handle_all( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match<my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]( leaf::match<my_error_code, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + + // void, try_handle_all (failure), match enum (multiple enum values) + { + int c=0; + leaf::try_handle_all( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match<my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]( leaf::match<my_error_code, my_error_code::error2, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + + // void, try_handle_all (failure), match value (single value) + { + int c=0; + leaf::try_handle_all( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + + // void, try_handle_all (failure), match value (multiple values) + { + int c=0; + leaf::try_handle_all( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error2, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + + ////////////////////////////////////// + + // int, try_handle_all (success) + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::ok)); + return answer; + }, + [] + { + return 1; + } ); + BOOST_TEST_EQ(r, 42); + } + + // int, try_handle_all (failure) + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( my_error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + // int, try_handle_all (failure), match cond_x (single enum value) + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f_errc<int>(errc_a::a0)); + return answer; + }, + []( leaf::match<leaf::condition<cond_x>, cond_x::x11> ) + { + return 1; + }, + []( leaf::match<leaf::condition<cond_x>, cond_x::x00> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(ec.matched, make_error_code(errc_a::a0)); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } +#endif + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + // int, try_handle_all (failure), match cond_x (wrapped std::error_code) + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f_errc_wrapped<int>(errc_a::a0)); + return answer; + }, + []( leaf::match<leaf::condition<cond_x>, cond_x::x11> ) + { + return 1; + }, + []( leaf::match_value<leaf::condition<e_std_error_code, cond_x>, cond_x::x00> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(ec.matched.value, make_error_code(errc_a::a0)); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } +#endif + + // int, try_handle_all (failure), match enum (single enum value) + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match<my_error_code, my_error_code::error2> ) + { + return 1; + }, + []( leaf::match<my_error_code, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + + // int, try_handle_all (failure), match enum (multiple enum values) + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match<my_error_code, my_error_code::error2> ) + { + return 1; + }, + []( leaf::match<my_error_code, my_error_code::error2, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + + // int, try_handle_all (failure), match value (single value) + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match_value<e_my_error_code, my_error_code::error2> ) + { + return 1; + }, + []( leaf::match_value<e_my_error_code, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + + // int, try_handle_all (failure), match value (multiple values) + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match_value<e_my_error_code, my_error_code::error2> ) + { + return 1; + }, + []( leaf::match_value<e_my_error_code, my_error_code::error2, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + + ////////////////////////////////////// + + // move_only, try_handle_all (success) + { + move_only r = leaf::try_handle_all( + []() -> leaf::result<move_only> + { + BOOST_LEAF_AUTO(answer, f<move_only>(my_error_code::ok)); + return std::move(answer); + }, + [] + { + return move_only(1); + } ); + BOOST_TEST_EQ(r.value, 42); + } + + // move_only, try_handle_all (failure) + { + move_only r = leaf::try_handle_all( + []() -> leaf::result<move_only> + { + BOOST_LEAF_AUTO(answer, f<move_only>(my_error_code::error1)); + return std::move(answer); + }, + []( my_error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return move_only(1); + }, + [] + { + return move_only(2); + } ); + BOOST_TEST_EQ(r.value, 1); + } + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + // move_only, try_handle_all (failure), match cond_x (single enum value) + { + move_only r = leaf::try_handle_all( + []() -> leaf::result<move_only> + { + BOOST_LEAF_AUTO(answer, f_errc<move_only>(errc_a::a0)); + return std::move(answer); + }, + []( leaf::match<leaf::condition<cond_x>, cond_x::x11> ) + { + return move_only(1); + }, + []( leaf::match<leaf::condition<cond_x>, cond_x::x00> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(ec.matched, make_error_code(errc_a::a0)); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return move_only(2); + }, + [] + { + return move_only(3); + } ); + BOOST_TEST_EQ(r.value, 2); + } +#endif + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + // move_only, try_handle_all (failure), match cond_x (wrapped std::error_code) + { + move_only r = leaf::try_handle_all( + []() -> leaf::result<move_only> + { + BOOST_LEAF_AUTO(answer, f_errc_wrapped<move_only>(errc_a::a0)); + return std::move(answer); + }, + []( leaf::match<leaf::condition<cond_x>, cond_x::x11> ) + { + return move_only(1); + }, + []( leaf::match_value<leaf::condition<e_std_error_code, cond_x>, cond_x::x00> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(ec.matched.value, make_error_code(errc_a::a0)); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return move_only(2); + }, + [] + { + return move_only(3); + } ); + BOOST_TEST_EQ(r.value, 2); + } +#endif + + // move_only, try_handle_all (failure), match enum (single enum value) + { + move_only r = leaf::try_handle_all( + []() -> leaf::result<move_only> + { + BOOST_LEAF_AUTO(answer, f<move_only>(my_error_code::error1)); + return std::move(answer); + }, + []( leaf::match<my_error_code, my_error_code::error2> ) + { + return move_only(1); + }, + []( leaf::match<my_error_code, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return move_only(2); + }, + [] + { + return move_only(3); + } ); + BOOST_TEST_EQ(r.value, 2); + } + + // move_only, try_handle_all (failure), match enum (multiple enum values) + { + move_only r = leaf::try_handle_all( + []() -> leaf::result<move_only> + { + BOOST_LEAF_AUTO(answer, f<move_only>(my_error_code::error1)); + return std::move(answer); + }, + []( leaf::match<my_error_code, my_error_code::error2> ) + { + return move_only(1); + }, + []( leaf::match<my_error_code, my_error_code::error2, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return move_only(2); + }, + [] + { + return move_only(3); + } ); + BOOST_TEST_EQ(r.value, 2); + } + + // move_only, try_handle_all (failure), match value (single value) + { + move_only r = leaf::try_handle_all( + []() -> leaf::result<move_only> + { + BOOST_LEAF_AUTO(answer, f<move_only>(my_error_code::error1)); + return std::move(answer); + }, + []( leaf::match_value<e_my_error_code, my_error_code::error2> ) + { + return move_only(1); + }, + []( leaf::match_value<e_my_error_code, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return move_only(2); + }, + [] + { + return move_only(3); + } ); + BOOST_TEST_EQ(r.value, 2); + } + + // move_only, try_handle_all (failure), match value (multiple values) + { + move_only r = leaf::try_handle_all( + []() -> leaf::result<move_only> + { + BOOST_LEAF_AUTO(answer, f<move_only>(my_error_code::error1)); + return std::move(answer); + }, + []( leaf::match_value<e_my_error_code, my_error_code::error2> ) + { + return move_only(1); + }, + []( leaf::match_value<e_my_error_code, my_error_code::error2, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return move_only(2); + }, + [] + { + return move_only(3); + } ); + BOOST_TEST_EQ(r.value, 2); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/handle_basic_test.cpp b/src/boost/libs/leaf/test/handle_basic_test.cpp new file mode 100644 index 000000000..053c6f0bd --- /dev/null +++ b/src/boost/libs/leaf/test/handle_basic_test.cpp @@ -0,0 +1,401 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/config.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/pred.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int Tag> struct info { int value;}; + +enum class error_code +{ + error1=1, + error2, + error3 +}; + +struct error1_tag { }; +struct error2_tag { }; +struct error3_tag { }; + +leaf::result<int> compute_answer( int what_to_do ) noexcept +{ + switch( what_to_do ) + { + case 0: + return 42; + case 1: + return leaf::new_error(error_code::error1); + case 2: + return leaf::new_error(error_code::error2); + case 3: + return leaf::new_error(error_code::error3); + case 4: + return leaf::new_error(error1_tag{}, error_code::error1); + case 5: + return leaf::new_error(error2_tag{}, error_code::error2); + default: + BOOST_LEAF_ASSERT(what_to_do==6); + return leaf::new_error(error3_tag{}, error_code::error3); + } +} + +leaf::result<int> handle_some_errors( int what_to_do ) +{ + return leaf::try_handle_some( + [=] + { + return compute_answer(what_to_do); + }, + []( error1_tag, leaf::match<error_code, error_code::error1> ) + { + return -1; + }, + []( leaf::match<error_code, error_code::error1> ) + { + return -2; + } ); +} + +leaf::result<float> handle_some_errors_float( int what_to_do ) +{ + return leaf::try_handle_some( + [=]() -> leaf::result<float> + { + return compute_answer(what_to_do); + }, + []( error2_tag, leaf::match<error_code, error_code::error2> ) + { + return -1.0f; + }, + []( leaf::match<error_code, error_code::error2> ) + { + return -2.0f; + } ); +} + +leaf::result<void> handle_some_errors_void( int what_to_do ) +{ + return leaf::try_handle_some( + [=]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, compute_answer(what_to_do)); + (void) answer; + return { }; + }, + []( leaf::match<error_code, error_code::error3> ) + { + } ); +} + +int main() +{ + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::try_handle_some( + []() -> leaf::result<int> + { + return leaf::try_handle_some( + []() -> leaf::result<int> + { + return leaf::new_error(40); + }, + []( leaf::error_info const & ei, int & v ) + { + ++v; + return ei.error(); + }); + }, + []( leaf::error_info const & ei, int & v ) + { + ++v; + return ei.error(); + }); + }, + []( int v ) + { + BOOST_TEST_EQ(v, 42); + return 1; + }, + [] + { + return 2; + }); + BOOST_TEST_EQ(r, 1); + } + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::try_handle_some( + []() -> leaf::result<int> + { + return leaf::try_handle_some( + []() -> leaf::result<int> + { + return leaf::new_error(40); + }, + []( leaf::error_info const & ei, int * v ) + { + ++*v; + return ei.error(); + }); + }, + []( leaf::error_info const & ei, int * v ) + { + ++*v; + return ei.error(); + }); + }, + []( int v ) + { + BOOST_TEST_EQ(v, 42); + return 1; + }, + [] + { + return 2; + }); + BOOST_TEST_EQ(r, 1); + } + + /////////////////////////// + + BOOST_TEST_EQ(handle_some_errors(0).value(), 42); + BOOST_TEST_EQ(handle_some_errors(1).value(), -2); + BOOST_TEST_EQ(handle_some_errors(4).value(), -1); + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, handle_some_errors(3)); + (void) answer; + return 0; + }, + []( leaf::match<error_code, error_code::error3> ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + + /////////////////////////// + + BOOST_TEST_EQ(handle_some_errors_float(0).value(), 42.0f); + BOOST_TEST_EQ(handle_some_errors_float(2).value(), -2.0f); + BOOST_TEST_EQ(handle_some_errors_float(5).value(), -1.0f); + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, handle_some_errors_float(1)); + (void) answer; + return 0; + }, + []( leaf::match<error_code, error_code::error1> ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + + /////////////////////////// + + BOOST_TEST(handle_some_errors_void(0)); + BOOST_TEST(handle_some_errors_void(3)); + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_CHECK(handle_some_errors_void(2)); + return 0; + }, + []( leaf::match<error_code, error_code::error2> ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + + /////////////////////////// + +#ifndef BOOST_LEAF_NO_EXCEPTIONS + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_CHECK(handle_some_errors_void(2)); + return 0; + }, + []( std::exception const & ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 2); + } +#endif + + /////////////////////////// + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{42} ); + }, + []( info<1> const & i1 ) + { + BOOST_TEST_EQ(i1.value, 42); + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{43} ); + }, + []() + { + return -1; + } ); + BOOST_TEST_EQ(r, -1); + BOOST_TEST_EQ(i1.value, 42); + return 0; + }, + []() + { + return -1; + } ); + BOOST_TEST_EQ(r, 0); + } + + /////////////////////////// + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{42} ); + }, + []( info<1> const & i1 ) + { + BOOST_TEST_EQ(i1.value, 42); + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{43} ); + }, + []( info<1> const & i1 ) + { + BOOST_TEST_EQ(i1.value, 43); + return -1; + }, + []() + { + return -2; + } ); + BOOST_TEST_EQ(r, -1); + BOOST_TEST_EQ(i1.value, 42); + return 0; + }, + []() + { + return -1; + } ); + BOOST_TEST_EQ(r, 0); + } + + /////////////////////////// + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::try_handle_some( + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{1} ); + }, + []( leaf::error_info const & err, info<1> const & i1, info<2> const * i2 ) + { + //We have space for info<2> in the context but i2 is null. + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST(!i2); + return err.error().load(info<2>{2}); + } ); + }, + []( info<1> const & i1, info<2> const & i2 ) + { + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, 2); + return 0; + }, + []() + { + return -1; + } ); + BOOST_TEST_EQ(r, 0); + } + + /////////////////////////// + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::try_handle_some( + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{1}, info<2>{-2} ); + }, + []( leaf::error_info const & err, info<1> const & i1, info<2> & i2 ) + { + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, -2); + i2 = info<2>{2}; + return err.error(); + } ); + }, + []( info<1> const & i1, info<2> const & i2 ) + { + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, 2); + return 0; + }, + []() + { + return -1; + } ); + BOOST_TEST_EQ(r, 0); + } + + /////////////////////////// + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/handle_some_other_result_test.cpp b/src/boost/libs/leaf/test/handle_some_other_result_test.cpp new file mode 100644 index 000000000..d42e0292d --- /dev/null +++ b/src/boost/libs/leaf/test/handle_some_other_result_test.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) + +#include <boost/leaf/config.hpp> + +#if !BOOST_LEAF_CFG_STD_SYSTEM_ERROR + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/pred.hpp> +#endif + +#include "_test_res.hpp" +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> struct info { int value; }; + +template <class ResType> +ResType f( bool succeed ) +{ + if( succeed ) + return 42; + else + return make_error_code(errc_a::a0); +} + +template <class ResType> +ResType g( bool succeed ) +{ + if( auto r = f<ResType>(succeed) ) + return r; + else + return leaf::error_id(r.error()).load(info<42>{42}).to_error_code(); +} + +template <class ResType> +void test() +{ + { + ResType r = leaf::try_handle_some( + [] + { + return g<ResType>(true); + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(r.value(), 42); + } + { + int called = 0; + ResType r = leaf::try_handle_some( + [&] + { + auto r = g<ResType>(false); + BOOST_TEST(!r); + auto ec = r.error(); + BOOST_TEST_EQ(ec.message(), "LEAF error"); + BOOST_TEST(!std::strcmp(ec.category().name(),"LEAF error")); + return r; + }, + [&]( info<42> const & x, leaf::match<leaf::condition<cond_x>, cond_x::x00> ec ) + { + called = 1; + BOOST_TEST_EQ(x.value, 42); + return ec.matched; + } ); + BOOST_TEST(!r); + BOOST_TEST_EQ(r.error(), make_error_code(errc_a::a0)); + BOOST_TEST(called); + } +} + +int main() +{ + test<test_res<int, std::error_code>>(); + test<test_res<int const, std::error_code>>(); + test<test_res<int, std::error_code> const>(); + test<test_res<int const, std::error_code> const>(); + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/handle_some_test.cpp b/src/boost/libs/leaf/test/handle_some_test.cpp new file mode 100644 index 000000000..4801f16a7 --- /dev/null +++ b/src/boost/libs/leaf/test/handle_some_test.cpp @@ -0,0 +1,1306 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/pred.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "_test_ec.hpp" +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> struct info { int value; }; + +enum class my_error_code +{ + ok, + error1, + error2, + error3 +}; + +struct e_my_error_code { my_error_code value; }; + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR +struct e_std_error_code { std::error_code value; }; +#endif + +template <class R> +leaf::result<R> f( my_error_code ec ) +{ + if( ec==my_error_code::ok ) + return R(42); + else + return leaf::new_error(ec, e_my_error_code{ec}, info<1>{1}, info<2>{2}, info<3>{3}); +} + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR +template <class R, class Errc> +leaf::result<R> f_errc( Errc ec ) +{ + return leaf::new_error(make_error_code(ec), info<1>{1}, info<2>{2}, info<3>{3}); +} + +template <class R, class Errc> +leaf::result<R> f_errc_wrapped( Errc ec ) +{ + return leaf::new_error(e_std_error_code{make_error_code(ec)}, info<1>{1}, info<2>{2}, info<3>{3}); +} +#endif + +int main() +{ + // void, try_handle_some (success) + { + int c=0; + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::ok)); + c = answer; + return { }; + }, + [&c]( leaf::error_info const & unmatched ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + return unmatched.error(); + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(c, 42); + } + + // void, try_handle_some (failure, matched) + { + int c=0; + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( my_error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST_EQ(c, 1); + BOOST_TEST(r); + } + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + // void, try_handle_some (failure, matched), match cond_x (single enum value) + { + int c=0; + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f_errc<int>(errc_a::a0)); + c = answer; + return { }; + }, + [&c]( leaf::match<leaf::condition<cond_x>, cond_x::x00> ec, info<1> const & x, info<2> const & y ) + { + BOOST_TEST_EQ(ec.matched, make_error_code(errc_a::a0)); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST_EQ(c, 1); + BOOST_TEST(r); + } +#endif + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + // void, try_handle_some (failure, matched), match cond_x (wrapped std::error_code) + { + int c=0; + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f_errc_wrapped<int>(errc_a::a0)); + c = answer; + return { }; + }, + [&c]( leaf::match_value<leaf::condition<e_std_error_code, cond_x>, cond_x::x00> ec, info<1> const & x, info<2> const & y ) + { + BOOST_TEST_EQ(ec.matched.value, make_error_code(errc_a::a0)); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST_EQ(c, 1); + BOOST_TEST(r); + } +#endif + + // void, try_handle_some (failure, matched), match enum (single enum value) + { + int c=0; + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match<my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]( leaf::match<my_error_code, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + } ); + BOOST_TEST_EQ(c, 2); + BOOST_TEST(r); + } + + // void, try_handle_some (failure, matched), match enum (multiple enum values) + { + int c=0; + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match<my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]( leaf::match<my_error_code, my_error_code::error2, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + } ); + BOOST_TEST_EQ(c, 2); + BOOST_TEST(r); + } + + // void, try_handle_some (failure, matched), match value (single value) + { + int c=0; + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + } ); + BOOST_TEST_EQ(c, 2); + BOOST_TEST(r); + } + + // void, try_handle_some (failure, matched), match value (multiple values) + { + int c=0; + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error2, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + } ); + BOOST_TEST_EQ(c, 2); + BOOST_TEST(r); + } + + // void, try_handle_some (failure, initially not matched) + { + int c=0; + leaf::try_handle_all( + [&c] + { + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( info<4> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST_EQ(c, 0); + return r; + }, + [&c]( my_error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + // void, try_handle_some (failure, initially not matched), match cond_x (single enum value) + { + int c=0; + leaf::try_handle_all( + [&c] + { + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f_errc<int>(errc_a::a0)); + c = answer; + return { }; + }, + [&c]( leaf::match<leaf::condition<cond_x>, cond_x::x11> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST_EQ(c, 0); + return r; + }, + [&c]( leaf::match<leaf::condition<cond_x>, cond_x::x00> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(ec.matched, make_error_code(errc_a::a0)); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } +#endif + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + // void, try_handle_some (failure, initially not matched), match cond_x (wrapped std::error_code) + { + int c=0; + leaf::try_handle_all( + [&c] + { + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f_errc_wrapped<int>(errc_a::a0)); + c = answer; + return { }; + }, + [&c]( leaf::match_value<leaf::condition<e_std_error_code, cond_x>, cond_x::x11> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST_EQ(c, 0); + return r; + }, + [&c]( leaf::match_value<leaf::condition<e_std_error_code, cond_x>, cond_x::x00> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(ec.matched.value, make_error_code(errc_a::a0)); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } +#endif + + // void, try_handle_some (failure, initially not matched), match enum (single enum value) + { + int c=0; + leaf::try_handle_all( + [&c] + { + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match<my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST_EQ(c, 0); + return r; + }, + [&c]( leaf::match<my_error_code, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + + // void, try_handle_some (failure, initially not matched), match enum (multiple enum values) + { + int c=0; + leaf::try_handle_all( + [&c] + { + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match<my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST_EQ(c, 0); + return r; + }, + [&c]( leaf::match<my_error_code, my_error_code::error2, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + + // void, try_handle_some (failure, initially not matched), match value (single value) + { + int c=0; + leaf::try_handle_all( + [&c] + { + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST_EQ(c, 0); + return r; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + + // void, try_handle_some (failure, initially not matched), match value (multiple values) + { + int c=0; + leaf::try_handle_all( + [&c] + { + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST_EQ(c, 0); + return r; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error2, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + + // void, try_handle_some (failure, initially matched) + { + int c=0; + leaf::try_handle_all( + [&c] + { + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( my_error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(c, 1); + return r; + }, + [&c]( info<4> ) + { + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 1); + } + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + // void, try_handle_some (failure, initially matched), match cond_x (single enum value) + { + int c=0; + leaf::try_handle_all( + [&c] + { + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f_errc<int>(errc_a::a0)); + c = answer; + return { }; + }, + [&c]( leaf::match<leaf::condition<cond_x>, cond_x::x00> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(ec.matched, make_error_code(errc_a::a0)); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(c, 1); + return r; + }, + [&c]( leaf::match<leaf::condition<cond_x>, cond_x::x11> ) + { + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 1); + } +#endif + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + // void, try_handle_some (failure, initially matched), match cond_x (wrapped std::error_code) + { + int c=0; + leaf::try_handle_all( + [&c] + { + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f_errc_wrapped<int>(errc_a::a0)); + c = answer; + return { }; + }, + [&c]( leaf::match_value<leaf::condition<e_std_error_code, cond_x>, cond_x::x00> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(ec.matched.value, make_error_code(errc_a::a0)); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(c, 1); + return r; + }, + [&c]( leaf::match_value<leaf::condition<e_std_error_code, cond_x>, cond_x::x11> ) + { + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 1); + } +#endif + + // void, try_handle_some (failure, initially matched), match enum (single enum value) + { + int c=0; + leaf::try_handle_all( + [&c] + { + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match<my_error_code, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(c, 1); + return r; + }, + [&c]( leaf::match<my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 1); + } + + // void, try_handle_some (failure, initially matched), match enum (multiple enum values) + { + int c=0; + leaf::try_handle_all( + [&c] + { + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match<my_error_code, my_error_code::error2, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(c, 1); + return r; + }, + [&c]( leaf::match<my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 1); + } + + // void, try_handle_some (failure, initially matched), match value (single value) + { + int c=0; + leaf::try_handle_all( + [&c] + { + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(c, 1); + return r; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 1); + } + + // void, try_handle_some (failure, initially matched), match value (multiple values) + { + int c=0; + leaf::try_handle_all( + [&c] + { + leaf::result<void> r = leaf::try_handle_some( + [&c]() -> leaf::result<void> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error2,my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(c, 1); + return r; + }, + [&c]( leaf::match_value<e_my_error_code, my_error_code::error2> ) + { + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c]() + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 1); + } + + ////////////////////////////////////// + + // int, try_handle_some (success) + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::ok)); + return answer; + }, + []( leaf::error_info const & unmatched ) + { + return unmatched.error(); + } ); + BOOST_TEST_EQ(*r, 42); + } + + // int, try_handle_some (failure, matched) + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( my_error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 1; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(*r, 1); + } + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + // int, try_handle_some (failure, matched), match cond_x (single enum value) + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f_errc<int>(errc_a::a0)); + return answer; + }, + []( leaf::match<leaf::condition<cond_x>, cond_x::x11> ) + { + return 1; + }, + []( leaf::match<leaf::condition<cond_x>, cond_x::x00> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(ec.matched, make_error_code(errc_a::a0)); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(*r, 2); + } +#endif + + // int, try_handle_some (failure, matched), match enum (single enum value) + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match<my_error_code,my_error_code::error2> ) + { + return 1; + }, + []( leaf::match<my_error_code,my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(*r, 2); + } + + // int, try_handle_some (failure, matched), match enum (multiple enum values) + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match<my_error_code,my_error_code::error2> ) + { + return 1; + }, + []( leaf::match<my_error_code,my_error_code::error2,my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(*r, 2); + } + + // int, try_handle_some (failure, matched), match value (single value) + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match_value<e_my_error_code,my_error_code::error2> ) + { + return 1; + }, + []( leaf::match_value<e_my_error_code,my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(*r, 2); + } + + // int, try_handle_some (failure, matched), match value (multiple values) + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match_value<e_my_error_code,my_error_code::error2> ) + { + return 1; + }, + []( leaf::match_value<e_my_error_code,my_error_code::error2,my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(*r, 2); + } + + // int, try_handle_some (failure, initially not matched) + { + int r = leaf::try_handle_all( + [] + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( info<4> ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + []( my_error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + // int, try_handle_some (failure, initially not matched), match cond_x (single enum value) + { + int r = leaf::try_handle_all( + [] + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f_errc<int>(errc_a::a0)); + return answer; + }, + []( leaf::match<leaf::condition<cond_x>, cond_x::x11> ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + []( leaf::match<leaf::condition<cond_x>, cond_x::x00> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(ec.matched, make_error_code(errc_a::a0)); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } +#endif + + // int, try_handle_some (failure, initially not matched), match enum (single enum value) + { + int r = leaf::try_handle_all( + [] + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match<my_error_code,my_error_code::error2> ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + []( leaf::match<my_error_code,my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + + // int, try_handle_some (failure, initially not matched), match enum (multiple enum values) + { + int r = leaf::try_handle_all( + [] + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match<my_error_code,my_error_code::error2> ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + []( leaf::match<my_error_code,my_error_code::error2,my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + + // int, try_handle_some (failure, initially not matched), match value (single value) + { + int r = leaf::try_handle_all( + [] + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match_value<e_my_error_code,my_error_code::error2> ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + []( leaf::match_value<e_my_error_code,my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + + // int, try_handle_some (failure, initially not matched), match value (multiple values) + { + int r = leaf::try_handle_all( + [] + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match_value<e_my_error_code,my_error_code::error2> ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + []( leaf::match_value<e_my_error_code,my_error_code::error2,my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + + // int, try_handle_some (failure, initially matched) + { + int r = leaf::try_handle_all( + [] + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( my_error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + []( info<4> ) + { + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 1); + } + + // int, try_handle_some (failure, initially matched), match enum (single enum value) + { + int r = leaf::try_handle_all( + [] + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match<my_error_code,my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + []( leaf::match<my_error_code,my_error_code::error2> ) + { + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 1); + } + + // int, try_handle_some (failure, initially matched), match enum (multiple enum values) + { + int r = leaf::try_handle_all( + [] + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match<my_error_code,my_error_code::error2,my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + []( leaf::match<my_error_code,my_error_code::error2> ) + { + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 1); + } + + // int, try_handle_some (failure, initially matched), match value (single value) + { + int r = leaf::try_handle_all( + [] + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match_value<e_my_error_code,my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + []( leaf::match_value<e_my_error_code,my_error_code::error2> ) + { + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 1); + } + + // int, try_handle_some (failure, initially matched), match value (multiple values) + { + int r = leaf::try_handle_all( + [] + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + BOOST_LEAF_AUTO(answer, f<int>(my_error_code::error1)); + return answer; + }, + []( leaf::match_value<e_my_error_code,my_error_code::error2,my_error_code::error1> ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.matched.value==my_error_code::error1); + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + []( leaf::match_value<e_my_error_code,my_error_code::error2> ) + { + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 1); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/lightweight_test.hpp b/src/boost/libs/leaf/test/lightweight_test.hpp new file mode 100644 index 000000000..7b7ffd473 --- /dev/null +++ b/src/boost/libs/leaf/test/lightweight_test.hpp @@ -0,0 +1,19 @@ +// 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) + +#include <boost/leaf/config.hpp> +#include "boost/core/lightweight_test.hpp" +#include <exception> + +#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(); + } +} +#endif diff --git a/src/boost/libs/leaf/test/match_member_test.cpp b/src/boost/libs/leaf/test/match_member_test.cpp new file mode 100644 index 000000000..350c3d6f5 --- /dev/null +++ b/src/boost/libs/leaf/test/match_member_test.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) + +#if __cplusplus < 201703L + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/pred.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "_test_ec.hpp" +#include "lightweight_test.hpp" +#include <exception> + +namespace leaf = boost::leaf; + +enum class my_error { e1=1, e2, e3 }; + +struct e_my_error { my_error value; }; + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR +struct e_error_code { std::error_code value; }; +#endif + +struct my_exception: std::exception +{ + int value; +}; + +template <class M, class E> +bool test(E const & e ) +{ + if( M::evaluate(e) ) + { + M m{e}; + BOOST_TEST_EQ(&e, &m.matched); + return true; + } + else + return false; +} + +int main() +{ + { + e_my_error e = { my_error::e1 }; + + BOOST_TEST(( test<leaf::match_member<&e_my_error::value, my_error::e1>>(e) )); + BOOST_TEST(( !test<leaf::match_member<&e_my_error::value, my_error::e2>>(e) )); + BOOST_TEST(( test<leaf::match_member<&e_my_error::value, my_error::e2, my_error::e1>>(e) )); + } + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + { + e_error_code e = { errc_a::a0 }; + + BOOST_TEST(( test<leaf::match_member<&e_error_code::value, errc_a::a0>>(e) )); + BOOST_TEST(( !test<leaf::match_member<&e_error_code::value, errc_a::a2>>(e) )); + BOOST_TEST(( test<leaf::match_member<&e_error_code::value, errc_a::a2, errc_a::a0>>(e) )); + } +#endif + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error(e_my_error{my_error::e1}); + }, + + []( leaf::match_member<&e_my_error::value, my_error::e1> ) + { + return 1; + }, + + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error(e_my_error{my_error::e1}); + }, + + []( leaf::match_member<&e_my_error::value, my_error::e2> ) + { + return 1; + }, + + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 2); + } + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/match_test.cpp b/src/boost/libs/leaf/test/match_test.cpp new file mode 100644 index 000000000..3592d9828 --- /dev/null +++ b/src/boost/libs/leaf/test/match_test.cpp @@ -0,0 +1,282 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/pred.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "_test_ec.hpp" +#include "lightweight_test.hpp" +#include <exception> + +namespace leaf = boost::leaf; + +enum class my_error { e1=1, e2, e3 }; + +struct e_my_error { int value; }; + +#if __cplusplus >= 201703L +template <my_error value> +constexpr bool cmp_my_error( my_error const & e ) noexcept +{ + return e == value; +}; + +template <int S> +constexpr bool e_my_error_gt( e_my_error const & e ) noexcept +{ + return e.value > S; +} +#endif + +struct my_exception: std::exception +{ + int value; + bool operator==(int); +}; + +template <class M, class E> +bool test(E const & e ) +{ + if( M::evaluate(e) ) + { + M m{e}; + BOOST_TEST(e == m.matched); + return true; + } + else + return false; +} + +int main() +{ + { + int e = 42; + + BOOST_TEST(( test<leaf::match<int, 42>>(e) )); + BOOST_TEST(( !test<leaf::match<int, 41>>(e) )); + BOOST_TEST(( test<leaf::match<int, 42, 41>>(e) )); + + BOOST_TEST(( !test<leaf::if_not<leaf::match<int, 42>>>(e) )); + BOOST_TEST(( test<leaf::if_not<leaf::match<int, 41>>>(e) )); + BOOST_TEST(( !test<leaf::if_not<leaf::match<int, 42, 41>>>(e) )); + } + + { + my_error e = my_error::e1; + + BOOST_TEST(( test<leaf::match<my_error, my_error::e1>>(e) )); + BOOST_TEST(( !test<leaf::match<my_error, my_error::e2>>(e) )); + BOOST_TEST(( test<leaf::match<my_error, my_error::e2, my_error::e1>>(e) )); + + BOOST_TEST(( !test<leaf::if_not<leaf::match<my_error, my_error::e1>>>(e) )); + BOOST_TEST(( test<leaf::if_not<leaf::match<my_error, my_error::e2>>>(e) )); + BOOST_TEST(( !test<leaf::if_not<leaf::match<my_error, my_error::e2, my_error::e1>>>(e) )); + } + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + { + std::error_code e = errc_a::a0; + + BOOST_TEST(( test<leaf::match<leaf::condition<cond_x>, cond_x::x00>>(e) )); + BOOST_TEST(( !test<leaf::match<leaf::condition<cond_x>, cond_x::x11>>(e) )); + BOOST_TEST(( test<leaf::match<leaf::condition<cond_x>, cond_x::x11, cond_x::x00>>(e) )); + + + BOOST_TEST(( !test<leaf::if_not<leaf::match<leaf::condition<cond_x>, cond_x::x00>>>(e) )); + BOOST_TEST(( test<leaf::if_not<leaf::match<leaf::condition<cond_x>, cond_x::x11>>>(e) )); + BOOST_TEST(( !test<leaf::if_not<leaf::match<leaf::condition<cond_x>, cond_x::x11, cond_x::x00>>>(e) )); + +#if __cplusplus >= 201703L + BOOST_TEST(( test<leaf::match<std::error_code, errc_a::a0>>(e) )); + BOOST_TEST(( !test<leaf::match<std::error_code, errc_a::a2>>(e) )); + BOOST_TEST(( test<leaf::match<std::error_code, errc_a::a2, errc_a::a0>>(e) )); + + BOOST_TEST(( !test<leaf::if_not<leaf::match<std::error_code, errc_a::a0>>>(e) )); + BOOST_TEST(( test<leaf::if_not<leaf::match<std::error_code, errc_a::a2>>>(e) )); + BOOST_TEST(( !test<leaf::if_not<leaf::match<std::error_code, errc_a::a2, errc_a::a0>>>(e) )); +#endif + } +#endif + +#if __cplusplus >= 201703L + { + my_error e = my_error::e1; + + BOOST_TEST(( test<leaf::match<my_error, cmp_my_error<my_error::e1>>>(e) )); + BOOST_TEST(( !test<leaf::match<my_error, cmp_my_error<my_error::e2>>>(e) )); + } +#endif + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error(my_error::e1); + }, + + []( leaf::match<my_error, my_error::e1> ) + { + return 1; + }, + + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error(my_error::e1); + }, + + []( leaf::match<my_error, my_error::e2> ) + { + return 1; + }, + + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 2); + } + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error(my_error::e1); + }, + + []( leaf::if_not<leaf::match<my_error, my_error::e1>> ) + { + return 1; + }, + + []( my_error e ) + { + return 2; + }, + + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error(); + }, + + []( leaf::if_not<leaf::match<my_error, my_error::e1>> ) + { + return 1; + }, + + []( my_error e ) + { + return 2; + }, + + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 3); + } + +#if __cplusplus >= 201703L + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error(my_error::e1); + }, + + []( leaf::match<my_error, cmp_my_error<my_error::e1>> ) + { + return 1; + }, + + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error(my_error::e1); + }, + + []( leaf::match<my_error, cmp_my_error<my_error::e2>> ) + { + return 1; + }, + + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 2); + } + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error(e_my_error{42}); + }, + + []( leaf::match<e_my_error, e_my_error_gt<41>> m ) + { + return 1; + }, + + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error(e_my_error{42}); + }, + + []( leaf::match<e_my_error, e_my_error_gt<42>> m ) + { + return 1; + }, + + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 2); + } +#endif + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/match_value_test.cpp b/src/boost/libs/leaf/test/match_value_test.cpp new file mode 100644 index 000000000..41054235f --- /dev/null +++ b/src/boost/libs/leaf/test/match_value_test.cpp @@ -0,0 +1,111 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/pred.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "_test_ec.hpp" +#include "lightweight_test.hpp" +#include <exception> + +namespace leaf = boost::leaf; + +enum class my_error { e1=1, e2, e3 }; + +struct e_my_error { my_error value; }; + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR +struct e_error_code { std::error_code value; }; +#endif + +struct my_exception: std::exception +{ + int value; +}; + +template <class M, class E> +bool test(E const & e ) +{ + if( M::evaluate(e) ) + { + M m{e}; + BOOST_TEST_EQ(&e, &m.matched); + return true; + } + else + return false; +} + +int main() +{ + { + e_my_error e = { my_error::e1 }; + + BOOST_TEST(( test<leaf::match_value<e_my_error, my_error::e1>>(e) )); + BOOST_TEST(( !test<leaf::match_value<e_my_error, my_error::e2>>(e) )); + BOOST_TEST(( test<leaf::match_value<e_my_error, my_error::e2, my_error::e1>>(e) )); + } + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + { + e_error_code e = { errc_a::a0 }; + + BOOST_TEST(( test<leaf::match_value<leaf::condition<e_error_code, cond_x>, cond_x::x00>>(e) )); + BOOST_TEST(( !test<leaf::match_value<leaf::condition<e_error_code, cond_x>, cond_x::x11>>(e) )); + BOOST_TEST(( test<leaf::match_value<leaf::condition<e_error_code, cond_x>, cond_x::x11, cond_x::x00>>(e) )); + +#if __cplusplus >= 201703L + BOOST_TEST(( test<leaf::match_value<e_error_code, errc_a::a0>>(e) )); + BOOST_TEST(( !test<leaf::match_value<e_error_code, errc_a::a2>>(e) )); + BOOST_TEST(( test<leaf::match_value<e_error_code, errc_a::a2, errc_a::a0>>(e) )); +#endif + } +#endif + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error(e_my_error{my_error::e1}); + }, + + []( leaf::match_value<e_my_error, my_error::e1> ) + { + return 1; + }, + + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error(e_my_error{my_error::e1}); + }, + + []( leaf::match_value<e_my_error, my_error::e2> ) + { + return 1; + }, + + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 2); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/multiple_errors_test.cpp b/src/boost/libs/leaf/test/multiple_errors_test.cpp new file mode 100644 index 000000000..da1e74ea0 --- /dev/null +++ b/src/boost/libs/leaf/test/multiple_errors_test.cpp @@ -0,0 +1,91 @@ +// 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) + +#include <boost/leaf/config.hpp> +#include <boost/leaf/handle_errors.hpp> + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" +#include <exception> + +namespace leaf = boost::leaf; + +template <int A> +struct info +{ + int value; +}; + +leaf::result<void> f12() +{ + return leaf::new_error( info<1>{1}, info<2>{2} ); +} + +leaf::result<void> f23() +{ + return leaf::new_error( info<2>{2}, info<3>{3} ); +} + +int main() +{ + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + leaf::result<void> r1 = f12(); + (void) r1; + leaf::result<void> r2 = f23(); + return r2.error(); + }, + []( info<1> ) + { + return 1; + }, + []( info<2> const & x, info<3> const & y ) + { + BOOST_TEST_EQ(x.value, 2); + BOOST_TEST_EQ(y.value, 3); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + +#ifndef BOOST_LEAF_NO_EXCEPTIONS + { + int r = leaf::try_catch( + []() -> int + { + try + { + throw leaf::exception(info<4>{4}); + } + catch(...) + { + } + throw std::exception{}; + }, + []( std::exception const &, info<4> ) + { + return 1; + }, + []( std::exception const & ) + { + return 2; + } ); + BOOST_TEST_EQ(r, 2); + } +#endif + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/optional_test.cpp b/src/boost/libs/leaf/test/optional_test.cpp new file mode 100644 index 000000000..f3178c86e --- /dev/null +++ b/src/boost/libs/leaf/test/optional_test.cpp @@ -0,0 +1,390 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/detail/optional.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +int object_count=0; +int value_count=0; + +class my_info +{ + my_info & operator=( my_info const & ) = delete; + +public: + + int value; + + explicit my_info( int value ): + value(value) + { + BOOST_TEST(++object_count>0); + BOOST_TEST(++value_count>0); + } + + my_info( my_info const & x ): + value(x.value) + { + BOOST_TEST(++object_count>0); + BOOST_TEST(++value_count>0); + } + + my_info( my_info && x ): + value(x.value) + { + x.value=-1; + BOOST_TEST(++object_count>0); + } + ~my_info() + { + BOOST_TEST(--object_count>=0); + if( value!=-1 ) + BOOST_TEST(--value_count>=0); + } +}; + +#ifndef BOOST_LEAF_NO_EXCEPTIONS +class throws_on_copy +{ + throws_on_copy & operator=( throws_on_copy const & )=delete; + +public: + + int value; + + throws_on_copy() + { + BOOST_TEST(++object_count>0); + } + + throws_on_copy( throws_on_copy const & ) + { + throw std::exception(); + } + + throws_on_copy( throws_on_copy && ) + { + BOOST_TEST(++object_count>0); + } + + ~throws_on_copy() + { + BOOST_TEST(--object_count>=0); + } +}; +#endif + +void run_tests() +{ + using leaf::leaf_detail::optional; + { + optional<my_info> x; + BOOST_TEST(x.empty()); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); + { + my_info a(42); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + optional<my_info> x(10, a); + BOOST_TEST_EQ(object_count, 2); + BOOST_TEST_EQ(value_count, 2); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST_EQ(x.value(10).value, 42); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); +#ifndef BOOST_LEAF_NO_EXCEPTIONS + { + throws_on_copy a; + BOOST_TEST_EQ(object_count, 1); + try + { + optional<throws_on_copy> x(10, a); + BOOST_TEST(false); + } + catch( std::exception & ) + { + } + } +#endif + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); + { + my_info a(42); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + optional<my_info> x(10, std::move(a)); + BOOST_TEST_EQ(object_count, 2); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST_EQ(x.value(10).value, 42); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); + { + optional<my_info> x(10, my_info(42)); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST_EQ(x.value(10).value, 42); + optional<my_info> y(x); + BOOST_TEST_EQ(object_count, 2); + BOOST_TEST_EQ(value_count, 2); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST_EQ(x.value(10).value, 42); + BOOST_TEST(!y.empty()); + BOOST_TEST(y.has_value(10)); + BOOST_TEST(!y.has_value(11)); + BOOST_TEST_EQ(y.value(10).value, 42); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); + { + optional<my_info> x(10, my_info(42)); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST_EQ(x.value(10).value, 42); + optional<my_info> y(std::move(x)); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(x.empty()); + BOOST_TEST(!x.has_value(10)); + BOOST_TEST(!y.empty()); + BOOST_TEST(y.has_value(10)); + BOOST_TEST(!y.has_value(11)); + BOOST_TEST_EQ(y.value(10).value, 42); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); + { + optional<my_info> x(10, my_info(42)); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST_EQ(x.value(10).value, 42); + optional<my_info> y; + BOOST_TEST(y.empty()); + BOOST_TEST_EQ(&(y=x), &y); + BOOST_TEST_EQ(object_count, 2); + BOOST_TEST_EQ(value_count, 2); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST_EQ(x.value(10).value, 42); + BOOST_TEST(!y.empty()); + BOOST_TEST(y.has_value(10)); + BOOST_TEST(!y.has_value(11)); + BOOST_TEST_EQ(y.value(10).value, 42); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); + { + optional<my_info> x(10, my_info(42)); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST_EQ(x.value(10).value, 42); + optional<my_info> y(11, my_info(43)); + BOOST_TEST_EQ(object_count, 2); + BOOST_TEST_EQ(value_count, 2); + BOOST_TEST(!y.empty()); + BOOST_TEST_EQ(&(y=x), &y); + BOOST_TEST_EQ(object_count, 2); + BOOST_TEST_EQ(value_count, 2); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST_EQ(x.value(10).value, 42); + BOOST_TEST(!y.empty()); + BOOST_TEST(y.has_value(10)); + BOOST_TEST(!y.has_value(11)); + BOOST_TEST_EQ(y.value(10).value, 42); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); +#ifndef BOOST_LEAF_NO_EXCEPTIONS + { + optional<throws_on_copy> x(10, throws_on_copy()); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + optional<throws_on_copy> y; + try + { + (void) (y=x); + } + catch( std::exception & ) + { + } + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST(y.empty()); + BOOST_TEST(!y.has_value(10)); + } +#endif + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); + { + optional<my_info> x(10, my_info(42)); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST_EQ(x.value(10).value, 42); + optional<my_info> y; + BOOST_TEST_EQ(&(y=std::move(x)), &y); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(x.empty()); + BOOST_TEST(!x.has_value(10)); + BOOST_TEST(!y.empty()); + BOOST_TEST(y.has_value(10)); + BOOST_TEST(!y.has_value(11)); + BOOST_TEST_EQ(y.value(10).value, 42); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); + { + optional<my_info> x(10, my_info(42)); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST_EQ(x.value(10).value, 42); + optional<my_info> y(11, my_info(43)); + BOOST_TEST_EQ(object_count, 2); + BOOST_TEST_EQ(value_count, 2); + BOOST_TEST(!y.empty()); + BOOST_TEST_EQ(&(y=std::move(x)), &y); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(x.empty()); + BOOST_TEST(!x.has_value(10)); + BOOST_TEST(!y.empty()); + BOOST_TEST(y.has_value(10)); + BOOST_TEST(!y.has_value(11)); + BOOST_TEST_EQ(y.value(10).value, 42); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); + { + optional<my_info> x; + my_info a(42); + x.put(10, a); + BOOST_TEST_EQ(object_count, 2); + BOOST_TEST_EQ(value_count, 2); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST_EQ(x.value(10).value, 42); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); + { + optional<my_info> x(10, my_info(43)); + BOOST_TEST(!x.empty()); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + my_info a(42); + x.put(10, a); + BOOST_TEST_EQ(object_count, 2); + BOOST_TEST_EQ(value_count, 2); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST_EQ(x.value(10).value, 42); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); + { + optional<my_info> x; + BOOST_TEST(x.empty()); + x.put(10, my_info(42)); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST_EQ(x.value(10).value, 42); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); + { + optional<my_info> x(11, my_info(43)); + BOOST_TEST(!x.empty()); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + x.put(10, my_info(42)); + BOOST_TEST(!x.empty()); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + BOOST_TEST_EQ(x.value(10).value, 42); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); + { + optional<my_info> x(10, my_info(42)); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(!x.empty()); + my_info a = std::move(x).value(10); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(x.empty()); + BOOST_TEST(!x.has_value(10)); + BOOST_TEST_EQ(a.value, 42); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); + { + optional<my_info> x(10, my_info(42)); + BOOST_TEST_EQ(object_count, 1); + BOOST_TEST_EQ(value_count, 1); + BOOST_TEST(!x.empty()); + BOOST_TEST(x.has_value(10)); + BOOST_TEST(!x.has_value(11)); + x.reset(); + BOOST_TEST(x.empty()); + BOOST_TEST(!x.has_value(10)); + } + BOOST_TEST(!object_count); + BOOST_TEST(!value_count); +} + +int main() +{ + run_tests(); + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/preload_basic_test.cpp b/src/boost/libs/leaf/test/preload_basic_test.cpp new file mode 100644 index 000000000..e374083d4 --- /dev/null +++ b/src/boost/libs/leaf/test/preload_basic_test.cpp @@ -0,0 +1,85 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" +#include <sstream> + +namespace leaf = boost::leaf; + +template <int> +struct info +{ + int value; +}; + +template <class G> +leaf::error_id f( G && g ) +{ + return std::forward<G>(g)(); +} + +template <class G> +void test( G && g ) +{ + int r = leaf::try_handle_all( + [&]() -> leaf::result<int> + { + return f(std::move(g)); + }, + []( info<42> const & i42, leaf::diagnostic_info const & di ) + { + BOOST_TEST_EQ(i42.value, 42); +#if BOOST_LEAF_CFG_STD_STRING + std::stringstream ss; ss << di; + std::string s = ss.str(); + std::cout << s; +#if BOOST_LEAF_CFG_DIAGNOSTICS + BOOST_TEST(s.find("info<-42>")!=s.npos); +#else + BOOST_TEST(s.find("BOOST_LEAF_CFG_DIAGNOSTICS")!=s.npos); +#endif +#endif + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); +} + +int main() +{ + test( + [] + { + auto load = leaf::on_error( info<42>{42}, info<-42>{-42} ); + return leaf::new_error(); + }); + test( + [] + { + info<42> inf1{42}; + info<-42> const inf2{-42}; + auto load = leaf::on_error( inf1, inf2 ); + return leaf::new_error(); + }); + test( + [] + { + info<42> inf1{42}; + auto load = leaf::on_error( inf1, info<-42>{-42} ); + return leaf::new_error(); + }); + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/preload_exception_test.cpp b/src/boost/libs/leaf/test/preload_exception_test.cpp new file mode 100644 index 000000000..8008d1407 --- /dev/null +++ b/src/boost/libs/leaf/test/preload_exception_test.cpp @@ -0,0 +1,153 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#if defined(BOOST_LEAF_NO_EXCEPTIONS) || defined(BOOST_LEAF_NO_THREADS) + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> +struct info +{ + int value; +}; + +template <class Thrower> +void g1( Thrower th ) +{ + auto load = leaf::on_error( info<1>{} ); + th(); +} + +template <class Thrower> +void g2( Thrower th ) +{ + auto load = leaf::on_error(info<3>{}, info<2>{} ); + th(); +} + +template <class Thrower> +void f1( Thrower th ) +{ + return g1(th); +} + +template <class Thrower> +void f2( Thrower th ) +{ + return g2(th); +} + +int main() +{ + BOOST_TEST_EQ(1, + leaf::try_catch( + [] + { + f1( [] { throw leaf::exception(); } ); + return 0; + }, + []( leaf::error_info const & err, info<1> ) + { + BOOST_TEST_EQ(err.error().value(), 1); + return 1; + }, + []( info<2> ) + { + return 2; + }, + []( info<1>, info<2> ) + { + return 3; + } )); + + BOOST_TEST_EQ(2, + leaf::try_catch( + [] + { + f2( [] { throw leaf::exception(); } ); + return 0; + }, + []( info<1> ) + { + return 1; + }, + []( leaf::error_info const & err, info<2>, info<3> ) + { + BOOST_TEST_EQ(err.error().value(), 9); + return 2; + }, + []( info<1>, info<2> ) + { + return 3; + } )); + + BOOST_TEST_EQ(1, + leaf::try_catch( + [] + { + f1( [] { throw std::exception(); } ); + return 0; + }, + []( leaf::error_info const & err, info<1> ) + { + BOOST_TEST_EQ(err.error().value(), 17); + return 1; + }, + []( info<2> ) + { + return 2; + }, + []( info<1>, info<2> ) + { + return 3; + } ) ); + + BOOST_TEST_EQ(2, + leaf::try_catch( + [] + { + f2( [] { throw std::exception(); } ); + return 0; + }, + []( info<1> ) + { + return 1; + }, + []( leaf::error_info const & err, info<2>, info<3> ) + { + BOOST_TEST_EQ(err.error().value(), 21); + return 2; + }, + []( info<1>, info<2> ) + { + return 3; + } )); + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/preload_nested_error_exception_test.cpp b/src/boost/libs/leaf/test/preload_nested_error_exception_test.cpp new file mode 100644 index 000000000..301daae81 --- /dev/null +++ b/src/boost/libs/leaf/test/preload_nested_error_exception_test.cpp @@ -0,0 +1,88 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/exception.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> +struct info +{ + int value; +}; + +void f0() +{ + auto load = leaf::on_error( info<0>{0} ); + throw leaf::exception(info<2>{2}); +} + +void f1() +{ + auto load = leaf::on_error( info<0>{-1}, info<1>{1}, info<2>{-1} ); + f0(); +} + +void f2() +{ + try + { + f1(); + } + catch( leaf::error_id const & err ) + { + err.load( info<3>{3} ); + throw; + } +} + +int main() +{ + int r = leaf::try_catch( + [] + { + f2(); + return 0; + }, + []( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST_EQ(i0.value, 0); + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, 2); + BOOST_TEST_EQ(i3.value, 3); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/preload_nested_error_result_test.cpp b/src/boost/libs/leaf/test/preload_nested_error_result_test.cpp new file mode 100644 index 000000000..1689e9214 --- /dev/null +++ b/src/boost/libs/leaf/test/preload_nested_error_result_test.cpp @@ -0,0 +1,63 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> +struct info +{ + int value; +}; + +leaf::error_id f0() +{ + auto load = leaf::on_error(info<0>{}); + return leaf::new_error( info<2>{2} ); +} + +leaf::error_id f1() +{ + auto load = leaf::on_error( info<0>{-1}, info<1>{1}, info<2>{-1} ); + return f0(); +} + +leaf::error_id f2() +{ + return f1().load( info<3>{3} ); +} + +int main() +{ + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return f2(); + }, + []( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST_EQ(i0.value, 0); + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, 2); + BOOST_TEST_EQ(i3.value, 3); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/preload_nested_new_error_exception_test.cpp b/src/boost/libs/leaf/test/preload_nested_new_error_exception_test.cpp new file mode 100644 index 000000000..4a593e868 --- /dev/null +++ b/src/boost/libs/leaf/test/preload_nested_new_error_exception_test.cpp @@ -0,0 +1,95 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/exception.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int A> +struct info +{ + int value; +}; + +void f0() +{ + auto load = leaf::on_error( info<0>{-1} ); + throw leaf::exception(info<1>{-1}); +} + +void f1() +{ + auto load = leaf::on_error( info<0>{}, info<1>{1}, info<2>{2} ); + try { f0(); } catch(...) { } + throw leaf::exception(); +} + +leaf::error_id f2() +{ + try + { + f1(); + BOOST_TEST(false); + } + catch( leaf::error_id const & err ) + { + err.load( info<3>{3} ); + throw; + } + catch(...) + { + BOOST_TEST(false); + } + return leaf::new_error(); +} + +int main() +{ + int r = leaf::try_catch( + [] + { + f2(); + return 0; + }, + []( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST_EQ(i0.value, 0); + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, 2); + BOOST_TEST_EQ(i3.value, 3); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/preload_nested_new_error_result_test.cpp b/src/boost/libs/leaf/test/preload_nested_new_error_result_test.cpp new file mode 100644 index 000000000..3066c349f --- /dev/null +++ b/src/boost/libs/leaf/test/preload_nested_new_error_result_test.cpp @@ -0,0 +1,64 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int A> +struct info +{ + int value; +}; + +leaf::error_id f0() +{ + auto load = leaf::on_error( info<0>{-1} ); + return leaf::new_error( info<1>{-1} ); +} + +leaf::error_id f1() +{ + auto load = leaf::on_error(info<0>{}, info<1>{1}, info<2>{2} ); + (void) f0(); + return leaf::new_error(); +} + +leaf::error_id f2() +{ + return f1().load( info<3>{3} ); +} + +int main() +{ + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return f2(); + }, + []( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST_EQ(i0.value, 0); + BOOST_TEST_EQ(i1.value, 1); + BOOST_TEST_EQ(i2.value, 2); + BOOST_TEST_EQ(i3.value, 3); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/preload_nested_success_exception_test.cpp b/src/boost/libs/leaf/test/preload_nested_success_exception_test.cpp new file mode 100644 index 000000000..86f509e62 --- /dev/null +++ b/src/boost/libs/leaf/test/preload_nested_success_exception_test.cpp @@ -0,0 +1,72 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +void g1() +{ + auto load = leaf::on_error( info{1} ); +} + +void g2() +{ + throw std::exception(); +} + +void f() +{ + auto load = leaf::on_error( info{2} ); + g1(); + g2(); +} + +int main() +{ + int r = leaf::try_catch( + [] + { + f(); + return 0; + }, + []( info x ) + { + BOOST_TEST_EQ(x.value, 2); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/preload_nested_success_result_test.cpp b/src/boost/libs/leaf/test/preload_nested_success_result_test.cpp new file mode 100644 index 000000000..f5026872d --- /dev/null +++ b/src/boost/libs/leaf/test/preload_nested_success_result_test.cpp @@ -0,0 +1,58 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/on_error.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +leaf::result<void> g1() +{ + auto load = leaf::on_error( info{1} ); + return { }; +} + +leaf::result<void> g2() +{ + return leaf::new_error(); +} + +leaf::result<void> f() +{ + auto load = leaf::on_error( info{2} ); + BOOST_LEAF_CHECK(g1()); + return g2(); +} + +int main() +{ + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_CHECK(f()); + return 1; + }, + []( info x ) + { + BOOST_TEST_EQ(x.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/print_test.cpp b/src/boost/libs/leaf/test/print_test.cpp new file mode 100644 index 000000000..e9ed0e357 --- /dev/null +++ b/src/boost/libs/leaf/test/print_test.cpp @@ -0,0 +1,126 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#if !BOOST_LEAF_CFG_DIAGNOSTICS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/detail/print.hpp> +#endif + +#include "lightweight_test.hpp" +#include <sstream> + +namespace leaf = boost::leaf; + +struct c0 +{ + friend std::ostream & operator<<( std::ostream & os, c0 const & ) + { + return os << "c0"; + } +}; + +struct c1 +{ + int value; + + friend std::ostream & operator<<( std::ostream & os, c1 const & ) + { + return os << "c1"; + } +}; + +struct c2 +{ + int value; +}; + +std::ostream & operator<<( std::ostream & os, c2 const & ) +{ + return os << "c2"; +} + +struct c3 +{ + int value; +}; + +struct c4 +{ + struct unprintable { }; + unprintable value;; +}; + +template <class T> +bool check( T const & x, char const * sub ) +{ + using namespace leaf::leaf_detail; + std::ostringstream s; + diagnostic<T>::print(s,x); + std::string q = s.str(); + return q.find(sub)!=q.npos; +} + +struct my_exception: std::exception +{ + char const * what() const noexcept override { return "my_exception_what"; } +}; + +int main() +{ + BOOST_TEST(check(c0{ },"c0")); + BOOST_TEST(check(c1{42},"c1")); + { + c1 x; + c1 & y = x; + BOOST_TEST(check(x,"c1")); + BOOST_TEST(check(y,"c1")); + } + BOOST_TEST(check(c2{42},"c2")); + { + c2 x = {42}; + c2 & y = x; + BOOST_TEST(check(x,"c2")); + BOOST_TEST(check(y,"c2")); + } + BOOST_TEST(check(c3{42},"c3")); + BOOST_TEST(check(c3{42},"42")); + { + c3 x = {42}; + c3 & y = x; + BOOST_TEST(check(x,"c3")); + BOOST_TEST(check(x,"42")); + BOOST_TEST(check(y,"c3")); + BOOST_TEST(check(y,"42")); + } + BOOST_TEST(check(c4(),"c4")); + BOOST_TEST(check(c4(),"{Non-Printable}")); + { + c4 x; + c4 & y = x; + BOOST_TEST(check(x,"c4")); + BOOST_TEST(check(x,"{Non-Printable}")); + BOOST_TEST(check(y,"c4")); + BOOST_TEST(check(y,"{Non-Printable}")); + } + BOOST_TEST(check(my_exception{}, "std::exception::what(): my_exception_what")); + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/result_bad_result_test.cpp b/src/boost/libs/leaf/test/result_bad_result_test.cpp new file mode 100644 index 000000000..519c61ca6 --- /dev/null +++ b/src/boost/libs/leaf/test/result_bad_result_test.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) + +#include <boost/leaf/config.hpp> + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/pred.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" +#ifdef BOOST_LEAF_BOOST_AVAILABLE +# include <boost/config/workaround.hpp> +#else +# define BOOST_WORKAROUND(a,b) 0 +#endif + +namespace leaf = boost::leaf; + +struct e_test { int value; }; + +int check( leaf::bad_result const &, leaf::match_value<e_test, 42> ) +{ + return 1; +} + +struct res { int val; }; + +int main() +{ + { + int r = leaf::try_catch( + [] + { + leaf::result<int> r = leaf::new_error(e_test{42}); + (void) r.value(); + return 0; + }, + check ); + BOOST_TEST_EQ(r, 1); + } +#if !BOOST_WORKAROUND( BOOST_GCC, < 50000 ) + { + int r = leaf::try_catch( + [] + { + leaf::result<int> const r = leaf::new_error(e_test{42}); + (void) r.value(); + return 0; + }, + check ); + BOOST_TEST_EQ(r, 1); + } +#endif + { + leaf::result<res> r = leaf::new_error(e_test{42}); + BOOST_TEST(r.operator->()==0); + } +#if !BOOST_WORKAROUND( BOOST_GCC, < 50000 ) + { + leaf::result<res> const r = leaf::new_error(e_test{42}); + BOOST_TEST(r.operator->()==0); + } +#endif + { + int r = leaf::try_catch( + [] + { + leaf::result<void> r = leaf::new_error(e_test{42}); + (void) r.value(); + return 0; + }, + check ); + BOOST_TEST_EQ(r, 1); + } +#if !BOOST_WORKAROUND( BOOST_GCC, < 50000 ) + { + int r = leaf::try_catch( + [] + { + leaf::result<void> const r = leaf::new_error(e_test{42}); + (void) r.value(); + return 0; + }, + check ); + BOOST_TEST_EQ(r, 1); + } +#endif + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/result_implicit_conversion_test.cpp b/src/boost/libs/leaf/test/result_implicit_conversion_test.cpp new file mode 100644 index 000000000..7ba7077cb --- /dev/null +++ b/src/boost/libs/leaf/test/result_implicit_conversion_test.cpp @@ -0,0 +1,55 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct A +{ + int x; + A() noexcept: + x(0) + { + } + + A( int x ) noexcept: + x(x) + { + } +}; + +leaf::result<int> f() +{ + return 42; +} + +leaf::result<A> g() +{ + return f(); +} + +int main() +{ + BOOST_TEST_EQ(g().value().x, 42); + { + leaf::result<int> r1(42); + leaf::result<A> r2(std::move(r1)); + BOOST_TEST_EQ(r2.value().x, 42); + } + { + leaf::result<int> r1(42); + leaf::result<A> r2; + r2 = std::move(r1); + BOOST_TEST_EQ(r2.value().x, 42); + } + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/result_load_test.cpp b/src/boost/libs/leaf/test/result_load_test.cpp new file mode 100644 index 000000000..45c8555b1 --- /dev/null +++ b/src/boost/libs/leaf/test/result_load_test.cpp @@ -0,0 +1,67 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/result.hpp> +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/pred.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> struct info { int value; }; + +template <class T> +leaf::result<T> test() +{ + leaf::result<T> r1 = leaf::new_error(info<42>{40}); + leaf::result<T> r2 = r1.load(info<1>{}); + leaf::result<T> r3 = r2.load(info<2>{2}, []{ return info<3>{3}; }); + leaf::result<T> r4 = r3.load([](info<42> & x){ ++x.value; }); + leaf::result<T> r5 = r4.load([](info<42> & x){ ++x.value; }, [](info<1> & x){ ++x.value; }); + return r5; +} + +int main() +{ + { + int r = leaf::try_handle_all( + [] + { + return test<int>(); + }, + []( leaf::match_value<info<42>, 42>, leaf::match_value<info<1>, 1>, leaf::match_value<info<2>, 2>, leaf::match_value<info<3>, 3> ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_CHECK(test<void>()); + return 0; + }, + []( leaf::match_value<info<42>, 42>, leaf::match_value<info<1>, 1>, leaf::match_value<info<2>, 2>, leaf::match_value<info<3>, 3> ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/result_ref_test.cpp b/src/boost/libs/leaf/test/result_ref_test.cpp new file mode 100644 index 000000000..819ddef99 --- /dev/null +++ b/src/boost/libs/leaf/test/result_ref_test.cpp @@ -0,0 +1,244 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/result.hpp> +# include <boost/leaf/capture.hpp> +# include <boost/leaf/handle_errors.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct val +{ + int id; + + friend bool operator==( val const & a, val const & b ) + { + return a.id==b.id; + } + + friend std::ostream & operator<<( std::ostream & os, val const & v ) + { + return os << v.id; + } +}; + +struct base +{ +}; + +struct derived: base +{ +}; + +template <class T, class R, class RetType> +void test_case_lvref() +{ + val x = { 42 }; + R r(x); + static_assert(std::is_same<RetType &, decltype(r.value())>::value, + "Bad return type for .value()" ); + static_assert(std::is_same<RetType &, decltype(*r)>::value, + "Bad return type for operator*()" ); + static_assert(std::is_same<typename std::remove_reference<RetType>::type *, decltype(r.operator->())>::value, + "Bad return type for operator->()" ); + BOOST_TEST(r); + BOOST_TEST_EQ(&r->id, &r.value().id); + T a = r.value(); + BOOST_TEST_EQ(a, x); + T b = *r; + BOOST_TEST_EQ(b, x); +} + +void test_lvref_access() +{ + test_case_lvref<val, leaf::result<val>, val &>(); + test_case_lvref<val &, leaf::result<val>, val &>(); + test_case_lvref<val const &, leaf::result<val>, val &>(); + test_case_lvref<val, leaf::result<val &>, val &>(); + test_case_lvref<val &, leaf::result<val &>, val &>(); + test_case_lvref<val const &, leaf::result<val &>, val &>(); + test_case_lvref<val, leaf::result<val const &>, val const &>(); + test_case_lvref<val const &, leaf::result<val const &>, val const &>(); + + test_case_lvref<val, leaf::result<val> const, val const &>(); + test_case_lvref<val const &, leaf::result<val> const, val const &>(); + test_case_lvref<val, leaf::result<val &> const, val &>(); + test_case_lvref<val &, leaf::result<val &> const, val &>(); + test_case_lvref<val const &, leaf::result<val &> const, val &>(); + test_case_lvref<val, leaf::result<val const &> const, val const &>(); + test_case_lvref<val const &, leaf::result<val const &> const, val const &>(); +} + +#ifndef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS + +template <class T, class R, class RetType> +void test_case_rvref() +{ + val x = { 42 }; + R r1(x); + static_assert(std::is_same<RetType, decltype(std::move(r1).value())>::value, + "Bad return type for .value()" ); + static_assert(std::is_same<RetType, decltype(*std::move(r1))>::value, + "Bad return type for operator*()" ); + static_assert(std::is_same<typename std::remove_reference<RetType>::type *, decltype(std::move(r1).operator->())>::value, + "Bad return type for operator->()" ); + BOOST_TEST(r1); + BOOST_TEST_EQ(&r1->id, &r1.value().id); + T a = std::move(r1).value(); + BOOST_TEST_EQ(a, x); + R r2(x); + T b = *std::move(r2); + BOOST_TEST_EQ(b, x); +} + +void test_rvref_access() +{ + test_case_rvref<val, leaf::result<val>, val &&>(); + test_case_rvref<val &&, leaf::result<val>, val &&>(); + test_case_rvref<val const &, leaf::result<val>, val &&>(); + test_case_rvref<val, leaf::result<val &>, val &>(); + test_case_rvref<val &, leaf::result<val &>, val &>(); + test_case_rvref<val const &, leaf::result<val &>, val &>(); + test_case_rvref<val, leaf::result<val const &>, val const &>(); + test_case_rvref<val const &, leaf::result<val const &>, val const &>(); + + test_case_rvref<val, leaf::result<val> const, val const &&>(); + test_case_rvref<val const &, leaf::result<val> const, val const &&>(); + test_case_rvref<val, leaf::result<val &> const, val &>(); + test_case_rvref<val &, leaf::result<val &> const, val &>(); + test_case_rvref<val const &, leaf::result<val &> const, val &>(); + test_case_rvref<val, leaf::result<val const &> const, val const &>(); + test_case_rvref<val const &, leaf::result<val const &> const, val const &>(); +} + +#endif + +int main() +{ + { + leaf::result<val const> r1, r2; + leaf::result<val const> & ref = r1; + leaf::result<val const> const & cref = r1; + leaf::result<val const> && rvref = std::move(r1); + leaf::result<val const> const && rvcref = std::move(r2); + + static_assert(std::is_same<decltype(ref.value()), val const &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(cref.value()), val const &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(rvref.value())), val const &&>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(rvcref.value())), val const &&>::value, "result type deduction bug"); + + static_assert(std::is_same<decltype(*ref), val const &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(*cref), val const &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(*rvref)), val const &&>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(*rvcref)), val const &&>::value, "result type deduction bug"); + + auto & ref_id = ref->id; static_assert(std::is_same<decltype(ref_id), int const &>::value, "result type deduction bug"); + auto & cref_id = cref->id; static_assert(std::is_same<decltype(cref_id), int const &>::value, "result type deduction bug"); + auto & rvref_id = rvref->id; static_assert(std::is_same<decltype(rvref_id), int const &>::value, "result type deduction bug"); + auto & rvcref_id = rvcref->id; static_assert(std::is_same<decltype(rvcref_id), int const &>::value, "result type deduction bug"); + } + + { + leaf::result<val> r1, r2; + leaf::result<val> & ref = r1; + leaf::result<val> const & cref = r1; + leaf::result<val> && rvref = std::move(r1); + leaf::result<val> const && rvcref = std::move(r2); + + static_assert(std::is_same<decltype(ref.value()), val &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(cref.value()), val const &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(rvref.value())), val &&>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(rvcref.value())), val const &&>::value, "result type deduction bug"); + + static_assert(std::is_same<decltype(*ref), val &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(*cref), val const &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(*rvref)), val &&>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(*rvcref)), val const &&>::value, "result type deduction bug"); + + auto & ref_id = ref->id; static_assert(std::is_same<decltype(ref_id), int &>::value, "result type deduction bug"); + auto & cref_id = cref->id; static_assert(std::is_same<decltype(cref_id), int const &>::value, "result type deduction bug"); + auto & rvref_id = rvref->id; static_assert(std::is_same<decltype(rvref_id), int &>::value, "result type deduction bug"); + auto & rvcref_id = rvcref->id; static_assert(std::is_same<decltype(rvcref_id), int const &>::value, "result type deduction bug"); + } + + { + val v; + leaf::result<val const &> r1(v), r2(v); + leaf::result<val const &> & ref = r1; + leaf::result<val const &> const & cref = r1; + leaf::result<val const &> && rvref = std::move(r1); + leaf::result<val const &> const && rvcref = std::move(r2); + + static_assert(std::is_same<decltype(ref.value()), val const &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(cref.value()), val const &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(rvref.value())), val const &&>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(rvcref.value())), val const &&>::value, "result type deduction bug"); + + static_assert(std::is_same<decltype(*ref), val const &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(*cref), val const &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(*rvref)), val const &&>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(*rvcref)), val const &&>::value, "result type deduction bug"); + + auto & ref_id = ref->id; static_assert(std::is_same<decltype(ref_id), int const &>::value, "result type deduction bug"); + auto & cref_id = cref->id; static_assert(std::is_same<decltype(cref_id), int const &>::value, "result type deduction bug"); + auto & rvref_id = rvref->id; static_assert(std::is_same<decltype(rvref_id), int const &>::value, "result type deduction bug"); + auto & rvcref_id = rvcref->id; static_assert(std::is_same<decltype(rvcref_id), int const &>::value, "result type deduction bug"); + } + + { + val v; + leaf::result<val &> r1(v), r2(v); + leaf::result<val &> & ref = r1; + leaf::result<val &> const & cref = r1; + leaf::result<val &> && rvref = std::move(r1); + leaf::result<val &> const && rvcref = std::move(r2); + + static_assert(std::is_same<decltype(ref.value()), val &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(cref.value()), val &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(rvref.value())), val &&>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(rvcref.value())), val &&>::value, "result type deduction bug"); + + static_assert(std::is_same<decltype(*ref), val &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(*cref), val &>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(*rvref)), val &&>::value, "result type deduction bug"); + static_assert(std::is_same<decltype(std::move(*rvcref)), val &&>::value, "result type deduction bug"); + + auto & ref_id = ref->id; static_assert(std::is_same<decltype(ref_id), int &>::value, "result type deduction bug"); + auto & cref_id = cref->id; static_assert(std::is_same<decltype(cref_id), int &>::value, "result type deduction bug"); + auto & rvref_id = rvref->id; static_assert(std::is_same<decltype(rvref_id), int &>::value, "result type deduction bug"); + auto & rvcref_id = rvcref->id; static_assert(std::is_same<decltype(rvcref_id), int &>::value, "result type deduction bug"); + } + + test_lvref_access(); +#ifndef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS + test_rvref_access(); +#endif + + // Hierarchy + + { + derived d; + leaf::result<base &> r = d; + BOOST_TEST_EQ(&r.value(), &d); + BOOST_TEST_EQ(&*r, &d); + BOOST_TEST_EQ(r.operator->(), &d); + } + + { + derived d; + leaf::result<base *> r = &d; + BOOST_TEST_EQ(r.value(), &d); + BOOST_TEST_EQ(*r, &d); + BOOST_TEST_EQ(*r.operator->(), &d); + } + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/result_state_test.cpp b/src/boost/libs/leaf/test/result_state_test.cpp new file mode 100644 index 000000000..591721f50 --- /dev/null +++ b/src/boost/libs/leaf/test/result_state_test.cpp @@ -0,0 +1,647 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/result.hpp> +# include <boost/leaf/capture.hpp> +# include <boost/leaf/handle_errors.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct val +{ + static int id_count; + static int count; + int id; + + val(): + id(++id_count) + { + ++count; + } + + val( val const & x ): + id(x.id) + { + ++count; + } + + val( val && x ): + id(x.id) + { + ++count; + } + + ~val() + { + --count; + } + + friend bool operator==( val const & a, val const & b ) + { + return a.id==b.id; + } + + friend std::ostream & operator<<( std::ostream & os, val const & v ) + { + return os << v.id; + } +}; +int val::count = 0; +int val::id_count = 0; + +struct err +{ + static int count; + + err() + { + ++count; + } + + err( err const & ) + { + ++count; + } + + err( err && ) + { + ++count; + } + + ~err() + { + --count; + } +}; +int err::count = 0; +struct e_err { err value; }; + +bool eq_value( leaf::result<val> & r, val v ) +{ + leaf::result<val> const & cr = r; + val const & cv = v; + return + r.value()==v && + cr.value()==cv && + *r.operator->()==v && + *cr.operator->()==cv && + *r==v && + *cr==cv; +} + +int main() +{ + { // value default -> move + leaf::result<val> r1; + BOOST_TEST(r1); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 1); + leaf::result<val> r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 2); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + { // value move -> move + leaf::result<val> r1 = val(); + BOOST_TEST(r1); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 1); + leaf::result<val> r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 2); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + { // value copy -> move + val v; + leaf::result<val> r1 = v; + BOOST_TEST(r1); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 2); + BOOST_TEST(eq_value(r1, v)); + leaf::result<val> r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 3); + BOOST_TEST(eq_value(r2, v)); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + + { // value default -> assign-move + leaf::result<val> r1; + BOOST_TEST(r1); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 1); + leaf::result<val> r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 2); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + { // value move -> assign-move + leaf::result<val> r1 = val(); + BOOST_TEST(r1); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 1); + leaf::result<val> r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 2); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + { // value copy -> assign-move + val v; + leaf::result<val> r1 = v; + BOOST_TEST(r1); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 2); + BOOST_TEST(eq_value(r1, v)); + leaf::result<val> r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 3); + BOOST_TEST(eq_value(r2, v)); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + + using context_type = leaf::leaf_detail::polymorphic_context_impl<leaf::context<e_err>>; + +#if BOOST_LEAF_CFG_CAPTURE + { // value default -> capture -> move + leaf::result<val> r1 = leaf::capture( std::make_shared<context_type>(), []{ return leaf::result<val>(); } ); + BOOST_TEST(r1); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 1); + leaf::result<val> r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 2); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + { // value move -> capture -> move + leaf::result<val> r1 = leaf::capture( std::make_shared<context_type>(), []{ return leaf::result<val>(val()); } ); + BOOST_TEST(r1); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 1); + leaf::result<val> r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 2); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + { // value copy -> capture -> move + leaf::result<val> r1 = leaf::capture( std::make_shared<context_type>(), []{ val v; return leaf::result<val>(v); } ); + BOOST_TEST(r1); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 1); + leaf::result<val> r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 2); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + + { // value default -> capture -> assign-move + leaf::result<val> r1 = leaf::capture( std::make_shared<context_type>(), []{ return leaf::result<val>(); } ); + BOOST_TEST(r1); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 1); + leaf::result<val> r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 2); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + { // value move -> capture -> assign-move + leaf::result<val> r1 = leaf::capture( std::make_shared<context_type>(), []{ return leaf::result<val>(val()); } ); + BOOST_TEST(r1); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 1); + leaf::result<val> r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 2); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + { // value copy -> capture -> assign-move + leaf::result<val> r1 = leaf::capture( std::make_shared<context_type>(), []{ val v; return leaf::result<val>(v); } ); + BOOST_TEST(r1); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 1); + leaf::result<val> r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 2); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); +#endif + + // ^^ value ^^ + // vv error vv + + { // error move -> move + context_type ctx; + auto active_context = activate_context(ctx); + leaf::result<val> r1 = leaf::new_error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST_EQ(err::count, 1); + BOOST_TEST_EQ(val::count, 0); + leaf::error_id r1e = r1.error(); + leaf::result<val> r2 = std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + { // error copy -> move + context_type ctx; + auto active_context = activate_context(ctx); + leaf::error_id err = leaf::new_error( e_err{ } ); + leaf::result<val> r1 = err; + BOOST_TEST(!r1); + BOOST_TEST_EQ(err::count, 1); + BOOST_TEST_EQ(val::count, 0); + leaf::error_id r1e = r1.error(); + leaf::result<val> r2 = std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + + { // error move -> assign move + context_type ctx; + ctx.activate(); + leaf::result<val> r1 = leaf::new_error( e_err { } ); + ctx.deactivate(); + BOOST_TEST(!r1); + BOOST_TEST_EQ(err::count, 1); + BOOST_TEST_EQ(val::count, 0); + leaf::error_id r1e = r1.error(); + leaf::result<val> r2; r2=std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + { + val x; + BOOST_TEST(ctx.handle_error<val>(r2.error(), [&]{ return x; }) == x); + } + BOOST_TEST_EQ(err::count, 1); + BOOST_TEST_EQ(val::count, 0); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + { // error copy -> assign move + context_type ctx; + auto active_context = activate_context(ctx); + leaf::error_id err = leaf::new_error( e_err{ } ); + leaf::result<val> r1 = err; + BOOST_TEST(!r1); + BOOST_TEST_EQ(err::count, 1); + BOOST_TEST_EQ(val::count, 0); + leaf::error_id r1e = r1.error(); + leaf::result<val> r2; r2=std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + +#if BOOST_LEAF_CFG_CAPTURE + { // error move -> capture -> move + leaf::result<val> r1 = leaf::capture( std::make_shared<context_type>(), []{ return leaf::result<val>( leaf::new_error( e_err { } ) ); } ); + BOOST_TEST(!r1); + BOOST_TEST_EQ(err::count, 1); + BOOST_TEST_EQ(val::count, 0); + leaf::error_id r1e = r1.error(); + leaf::result<val> r2 = std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + BOOST_TEST(!r1); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + { // error copy -> capture -> move + leaf::result<val> r1 = leaf::capture( std::make_shared<context_type>(), []{ leaf::error_id err = leaf::new_error( e_err{ } ); return leaf::result<val>(err); } ); + BOOST_TEST(!r1); + BOOST_TEST_EQ(err::count, 1); + BOOST_TEST_EQ(val::count, 0); + leaf::error_id r1e = r1.error(); + leaf::result<val> r2 = std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + BOOST_TEST(!r1); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + + { // error move -> capture -> assign-move + leaf::result<val> r1 = leaf::capture( std::make_shared<context_type>(), []{ return leaf::result<val>( leaf::new_error( e_err { } ) ); } ); + BOOST_TEST(!r1); + BOOST_TEST_EQ(err::count, 1); + BOOST_TEST_EQ(val::count, 0); + leaf::error_id r1e = r1.error(); + leaf::result<val> r2; r2=std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + BOOST_TEST(!r1); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); + { // error copy -> capture -> assign-move + leaf::result<val> r1 = leaf::capture( std::make_shared<context_type>(), []{ leaf::error_id err = leaf::new_error( e_err{ } ); return leaf::result<val>(err); } ); + BOOST_TEST(!r1); + BOOST_TEST_EQ(err::count, 1); + BOOST_TEST_EQ(val::count, 0); + leaf::error_id r1e = r1.error(); + leaf::result<val> r2; r2=std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + BOOST_TEST(!r1); + } + BOOST_TEST_EQ(err::count, 0); + BOOST_TEST_EQ(val::count, 0); +#endif + + // ^^ result<T> ^^ + + ///////////////////////////////////////////////////////////// + + // vv result<void> vv + + { // void default -> move + leaf::result<void> r1; + BOOST_TEST(r1); + r1.value(); + BOOST_TEST(r1.operator->() != 0); + *r1; + leaf::result<void> r2 = std::move(r1); + BOOST_TEST(r2); + r2.value(); + BOOST_TEST(r2.operator->() != 0); + *r2; + } + + { // void default -> assign-move + leaf::result<void> r1; + BOOST_TEST(r1); + r1.value(); + BOOST_TEST(r1.operator->() != 0); + *r1; + leaf::result<void> r2; r2=std::move(r1); + BOOST_TEST(r2); + r2.value(); + BOOST_TEST(r2.operator->() != 0); + *r2; + } + +#if BOOST_LEAF_CFG_CAPTURE + { // void default -> capture -> move + leaf::result<void> r1 = leaf::capture( std::make_shared<context_type>(), []{ return leaf::result<void>(); } ); + BOOST_TEST(r1); + r1.value(); + BOOST_TEST(r1.operator->() != 0); + *r1; + leaf::result<void> r2 = std::move(r1); + BOOST_TEST(r2); + r2.value(); + BOOST_TEST(r2.operator->() != 0); + *r2; + } + { // void default -> capture -> assign-move + leaf::result<void> r1 = leaf::capture( std::make_shared<context_type>(), []{ return leaf::result<void>(); } ); + BOOST_TEST(r1); + r1.value(); + BOOST_TEST(r1.operator->() != 0); + *r1; + leaf::result<void> r2; r2=std::move(r1); + BOOST_TEST(r2); + r2.value(); + BOOST_TEST(r2.operator->() != 0); + *r2; + } +#endif + + // ^^ void default ^^ + // vv void error vv + + { // void error move -> move + context_type ctx; + auto active_context = activate_context(ctx); + leaf::result<void> r1 = leaf::new_error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(r1.operator->() == 0); + BOOST_TEST_EQ(err::count, 1); + leaf::error_id r1e = r1.error(); + leaf::result<void> r2 = std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + BOOST_TEST(r2.operator->() == 0); + } + BOOST_TEST_EQ(err::count, 0); + { // void error copy -> move + context_type ctx; + auto active_context = activate_context(ctx); + leaf::error_id err = leaf::new_error( e_err{ } ); + leaf::result<void> r1 = err; + BOOST_TEST(!r1); + BOOST_TEST(r1.operator->() == 0); + BOOST_TEST_EQ(err::count, 1); + leaf::error_id r1e = r1.error(); + leaf::result<void> r2 = std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + BOOST_TEST(r2.operator->() == 0); + } + BOOST_TEST_EQ(err::count, 0); + + { // void error move -> assign move + context_type ctx; + ctx.activate(); + leaf::result<void> r1 = leaf::new_error( e_err { } ); + ctx.deactivate(); + BOOST_TEST(!r1); + BOOST_TEST(r1.operator->() == 0); + BOOST_TEST_EQ(err::count, 1); + leaf::error_id r1e = r1.error(); + leaf::result<void> r2; r2=std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + BOOST_TEST(r2.operator->() == 0); + ctx.handle_error<void>(r2.error(), []{ }); + BOOST_TEST_EQ(err::count, 1); + } + BOOST_TEST_EQ(err::count, 0); + { // void error copy -> assign move + context_type ctx; + auto active_context = activate_context(ctx); + leaf::error_id err = leaf::new_error( e_err{ } ); + leaf::result<void> r1 = err; + BOOST_TEST(!r1); + BOOST_TEST(r1.operator->() == 0); + BOOST_TEST_EQ(err::count, 1); + leaf::error_id r1e = r1.error(); + leaf::result<void> r2; r2=std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + BOOST_TEST(r2.operator->() == 0); + } + BOOST_TEST_EQ(err::count, 0); + +#if BOOST_LEAF_CFG_CAPTURE + { // void error move -> capture -> move + leaf::result<void> r1 = leaf::capture( std::make_shared<context_type>(), []{ return leaf::result<void>( leaf::new_error( e_err { } ) ); } ); + BOOST_TEST(!r1); + BOOST_TEST(r1.operator->() == 0); + BOOST_TEST_EQ(err::count, 1); + leaf::error_id r1e = r1.error(); + leaf::result<void> r2 = std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + BOOST_TEST(r2.operator->() == 0); + } + BOOST_TEST_EQ(err::count, 0); + { // void error copy -> capture -> move + leaf::result<void> r1 = leaf::capture( std::make_shared<context_type>(), []{ leaf::error_id err = leaf::new_error( e_err{ } ); return leaf::result<void>(err); } ); + BOOST_TEST(!r1); + BOOST_TEST(r1.operator->() == 0); + BOOST_TEST_EQ(err::count, 1); + leaf::error_id r1e = r1.error(); + leaf::result<void> r2 = std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + BOOST_TEST(r2.operator->() == 0); + } + BOOST_TEST_EQ(err::count, 0); + + { // void error move -> capture -> assign-move + leaf::result<void> r1 = leaf::capture( std::make_shared<context_type>(), []{ return leaf::result<void>( leaf::new_error( e_err { } ) ); } ); + BOOST_TEST(!r1); + BOOST_TEST(r1.operator->() == 0); + BOOST_TEST_EQ(err::count, 1); + leaf::error_id r1e = r1.error(); + leaf::result<void> r2; r2=std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + BOOST_TEST(r2.operator->() == 0); + } + BOOST_TEST_EQ(err::count, 0); + { // void error copy -> capture -> assign-move + leaf::result<void> r1 = leaf::capture( std::make_shared<context_type>(), []{ leaf::error_id err = leaf::new_error( e_err{ } ); return leaf::result<void>(err); } ); + BOOST_TEST(!r1); + BOOST_TEST(r1.operator->() == 0); + BOOST_TEST_EQ(err::count, 1); + leaf::error_id r1e = r1.error(); + leaf::result<void> r2; r2=std::move(r1); + leaf::error_id r2e = r2.error(); + BOOST_TEST_EQ(r1e, r2e); + BOOST_TEST(!r2); + BOOST_TEST(r2.operator->() == 0); + } + BOOST_TEST_EQ(err::count, 0); +#endif + + { + leaf::result<int> r = leaf::error_id(); + BOOST_TEST(!r); + BOOST_TEST_EQ(val::count, 0); + BOOST_TEST_EQ(err::count, 0); + } + BOOST_TEST_EQ(val::count, 0); + BOOST_TEST_EQ(err::count, 0); + + { + leaf::result<void> r = leaf::error_id(); + BOOST_TEST(!r); + BOOST_TEST_EQ(val::count, 0); + BOOST_TEST_EQ(err::count, 0); + } + BOOST_TEST_EQ(val::count, 0); + BOOST_TEST_EQ(err::count, 0); + + { + leaf::result<void> r; + BOOST_TEST(r); + leaf::result<val> r1 = r.error(); + BOOST_TEST_EQ(val::count, 0); + BOOST_TEST(!r1); + leaf::error_id id = r.error(); + BOOST_TEST(!id); + } + BOOST_TEST_EQ(val::count, 0); + + { + leaf::result<val> r; + BOOST_TEST(r); + leaf::result<void> r1 = r.error(); + BOOST_TEST(!r1); + leaf::error_id id = r.error(); + BOOST_TEST(!id); + BOOST_TEST_EQ(val::count, 1); + } + BOOST_TEST_EQ(val::count, 0); + + { + leaf::result<val> r; + BOOST_TEST(r); + leaf::result<float> r1 = r.error(); + BOOST_TEST(!r1); + leaf::error_id id = r.error(); + BOOST_TEST(!id); + BOOST_TEST_EQ(val::count, 1); + } + BOOST_TEST_EQ(val::count, 0); + +#if BOOST_LEAF_CFG_STD_STRING + { // Initialization forwarding constructor + leaf::result<std::string> r = "hello"; + BOOST_TEST(r); + BOOST_TEST_EQ(r.value(), "hello"); + } +#endif + +#if BOOST_LEAF_CFG_STD_STRING + { // Initialization forwarding constructor + leaf::result<std::string> r; r = "hello"; + BOOST_TEST(r); + BOOST_TEST_EQ(r.value(), "hello"); + } +#endif + + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/tls_array_alloc_test1.cpp b/src/boost/libs/leaf/test/tls_array_alloc_test1.cpp new file mode 100644 index 000000000..d550bd19a --- /dev/null +++ b/src/boost/libs/leaf/test/tls_array_alloc_test1.cpp @@ -0,0 +1,81 @@ +// 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) + +#define BOOST_LEAF_NO_EXCEPTIONS +#define BOOST_LEAF_CFG_STD_SYSTEM_ERROR 0 +#define BOOST_LEAF_CFG_STD_STRING 0 +#ifdef BOOST_LEAF_CFG_DIAGNOSTICS +# undef BOOST_LEAF_CFG_DIAGNOSTICS +#endif +#define BOOST_LEAF_CFG_DIAGNOSTICS 0 +#define BOOST_LEAF_USE_TLS_ARRAY +#define BOOST_LEAF_CFG_TLS_ARRAY_SIZE 64 +#define BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX 10 + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include <limits> +#include <vector> +#include <algorithm> +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +static void * tls_storage[BOOST_LEAF_CFG_TLS_ARRAY_SIZE]; +static int min_tls_index = std::numeric_limits<int>::max(); +static int max_tls_index = std::numeric_limits<int>::min(); + +namespace boost { namespace leaf { + +namespace tls +{ + void * read_void_ptr( int tls_index ) noexcept + { + BOOST_TEST_GE(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); + BOOST_TEST_LT(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_SIZE)); + min_tls_index = std::min(min_tls_index, tls_index); + max_tls_index = std::max(max_tls_index, tls_index); + return tls_storage[tls_index]; + } + + void write_void_ptr( int tls_index, void * p ) noexcept + { + BOOST_TEST_GE(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); + BOOST_TEST_LT(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_SIZE)); + min_tls_index = std::min(min_tls_index, tls_index); + max_tls_index = std::max(max_tls_index, tls_index); + tls_storage[tls_index] = p; + } +} + +} } + +template <int> +struct my_error_info +{ +}; + +int main() +{ + std::vector<void const *> used_ptrs; + int r = leaf::try_handle_all( + [&]() -> leaf::result<int> + { + return leaf::new_error( my_error_info<1>{}, my_error_info<2>{} ); + }, + [] + { + return 1; + } ); + BOOST_TEST_EQ(r, 1); + BOOST_TEST_EQ(min_tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); + BOOST_TEST_EQ(max_tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/tls_array_alloc_test2.cpp b/src/boost/libs/leaf/test/tls_array_alloc_test2.cpp new file mode 100644 index 000000000..870d6229f --- /dev/null +++ b/src/boost/libs/leaf/test/tls_array_alloc_test2.cpp @@ -0,0 +1,85 @@ +// 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) + +#define BOOST_LEAF_NO_EXCEPTIONS +#define BOOST_LEAF_CFG_STD_SYSTEM_ERROR 0 +#define BOOST_LEAF_CFG_STD_STRING 0 +#ifdef BOOST_LEAF_CFG_DIAGNOSTICS +# undef BOOST_LEAF_CFG_DIAGNOSTICS +#endif +#define BOOST_LEAF_CFG_DIAGNOSTICS 0 +#define BOOST_LEAF_USE_TLS_ARRAY +#define BOOST_LEAF_CFG_TLS_ARRAY_SIZE 64 +#define BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX 10 + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include <limits> +#include <vector> +#include <algorithm> +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +static void * tls_storage[BOOST_LEAF_CFG_TLS_ARRAY_SIZE]; +static int min_tls_index = std::numeric_limits<int>::max(); +static int max_tls_index = std::numeric_limits<int>::min(); + +namespace boost { namespace leaf { + +namespace tls +{ + void * read_void_ptr( int tls_index ) noexcept + { + BOOST_TEST_GE(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); + BOOST_TEST_LT(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_SIZE)); + min_tls_index = std::min(min_tls_index, tls_index); + max_tls_index = std::max(max_tls_index, tls_index); + return tls_storage[tls_index]; + } + + void write_void_ptr( int tls_index, void * p ) noexcept + { + BOOST_TEST_GE(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); + BOOST_TEST_LT(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_SIZE)); + min_tls_index = std::min(min_tls_index, tls_index); + max_tls_index = std::max(max_tls_index, tls_index); + tls_storage[tls_index] = p; + } +} + +} } + +template <int> +struct my_error_info +{ +}; + +int main() +{ + std::vector<void const *> used_ptrs; + int r = leaf::try_handle_all( + [&]() -> leaf::result<int> + { + return leaf::new_error( my_error_info<1>{}, my_error_info<2>{} ); + }, + []( my_error_info<2> ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + BOOST_TEST_EQ(min_tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); + BOOST_TEST_EQ(max_tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)+1); + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/tls_array_alloc_test3.cpp b/src/boost/libs/leaf/test/tls_array_alloc_test3.cpp new file mode 100644 index 000000000..cfcafbfe6 --- /dev/null +++ b/src/boost/libs/leaf/test/tls_array_alloc_test3.cpp @@ -0,0 +1,89 @@ +// 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) + +#define BOOST_LEAF_NO_EXCEPTIONS +#define BOOST_LEAF_CFG_STD_SYSTEM_ERROR 0 +#define BOOST_LEAF_CFG_STD_STRING 0 +#ifdef BOOST_LEAF_CFG_DIAGNOSTICS +# undef BOOST_LEAF_CFG_DIAGNOSTICS +#endif +#define BOOST_LEAF_CFG_DIAGNOSTICS 0 +#define BOOST_LEAF_USE_TLS_ARRAY +#define BOOST_LEAF_CFG_TLS_ARRAY_SIZE 64 +#define BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX 10 + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include <limits> +#include <vector> +#include <algorithm> +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +static void * tls_storage[BOOST_LEAF_CFG_TLS_ARRAY_SIZE]; +static int min_tls_index = std::numeric_limits<int>::max(); +static int max_tls_index = std::numeric_limits<int>::min(); + +namespace boost { namespace leaf { + +namespace tls +{ + void * read_void_ptr( int tls_index ) noexcept + { + BOOST_TEST_GE(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); + BOOST_TEST_LT(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_SIZE)); + min_tls_index = std::min(min_tls_index, tls_index); + max_tls_index = std::max(max_tls_index, tls_index); + return tls_storage[tls_index]; + } + + void write_void_ptr( int tls_index, void * p ) noexcept + { + BOOST_TEST_GE(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); + BOOST_TEST_LT(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_SIZE)); + min_tls_index = std::min(min_tls_index, tls_index); + max_tls_index = std::max(max_tls_index, tls_index); + tls_storage[tls_index] = p; + } +} + +} } + +template <int> +struct my_error_info +{ +}; + +int main() +{ + std::vector<void const *> used_ptrs; + int r = leaf::try_handle_all( + [&]() -> leaf::result<int> + { + return leaf::new_error( my_error_info<1>{}, my_error_info<2>{}, my_error_info<3>{}, my_error_info<4>{} ); + }, + []( my_error_info<2> ) + { + return 1; + }, + []( my_error_info<4> ) + { + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 1); + BOOST_TEST_EQ(min_tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); + BOOST_TEST_EQ(max_tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)+2); + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/tls_array_test.cpp b/src/boost/libs/leaf/test/tls_array_test.cpp new file mode 100644 index 000000000..5e6e650fe --- /dev/null +++ b/src/boost/libs/leaf/test/tls_array_test.cpp @@ -0,0 +1,101 @@ +// 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) + +#define BOOST_LEAF_NO_EXCEPTIONS +#define BOOST_LEAF_CFG_STD_SYSTEM_ERROR 0 +#define BOOST_LEAF_CFG_STD_STRING 0 +#ifdef BOOST_LEAF_CFG_DIAGNOSTICS +# undef BOOST_LEAF_CFG_DIAGNOSTICS +#endif +#define BOOST_LEAF_CFG_DIAGNOSTICS 0 +#define BOOST_LEAF_USE_TLS_ARRAY +#define BOOST_LEAF_CFG_TLS_ARRAY_SIZE 64 +#define BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX 10 + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include <limits> +#include <vector> +#include <algorithm> +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +static void * tls_storage[BOOST_LEAF_CFG_TLS_ARRAY_SIZE]; +static int min_tls_index = std::numeric_limits<int>::max(); +static int max_tls_index = std::numeric_limits<int>::min(); + +namespace boost { namespace leaf { + +namespace tls +{ + void * read_void_ptr( int tls_index ) noexcept + { + BOOST_TEST_GE(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); + BOOST_TEST_LT(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_SIZE)); + min_tls_index = std::min(min_tls_index, tls_index); + max_tls_index = std::max(max_tls_index, tls_index); + return tls_storage[tls_index]; + } + + void write_void_ptr( int tls_index, void * p ) noexcept + { + BOOST_TEST_GE(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); + BOOST_TEST_LT(tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_SIZE)); + min_tls_index = std::min(min_tls_index, tls_index); + max_tls_index = std::max(max_tls_index, tls_index); + tls_storage[tls_index] = p; + } +} + +} } + +template <int> +struct my_error_info +{ +}; + +// Mirroring boost::leaf::leaf_detail::optional<T>, to verify correctness of TLS pointers +template <class T> +struct optional +{ + int key_; + union { T value_; }; +}; +int const offset = offsetof(optional<my_error_info<1>>, value_); + +int main() +{ + std::vector<void const *> used_ptrs; + int r = leaf::try_handle_all( + [&]() -> leaf::result<int> + { + BOOST_TEST_GE(min_tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); + BOOST_TEST_GT(max_tls_index, min_tls_index); + BOOST_TEST_LT(max_tls_index, (BOOST_LEAF_CFG_TLS_ARRAY_SIZE)); + for( int i=min_tls_index; i<=max_tls_index; ++i ) + used_ptrs.push_back((char const *)tls_storage[i] + offset); + return leaf::new_error( my_error_info<1>{}, my_error_info<2>{} ); + }, + + [&]( my_error_info<1> const & a, my_error_info<2> const & b ) + { + BOOST_TEST(std::find(used_ptrs.begin(), used_ptrs.end(), &a) != used_ptrs.end()); + BOOST_TEST(std::find(used_ptrs.begin(), used_ptrs.end(), &b) != used_ptrs.end()); + return 1; + }, + + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/to_variant_test.cpp b/src/boost/libs/leaf/test/to_variant_test.cpp new file mode 100644 index 000000000..f2d6fc29b --- /dev/null +++ b/src/boost/libs/leaf/test/to_variant_test.cpp @@ -0,0 +1,67 @@ +// 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) + +#if __cplusplus < 201703L + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/to_variant.hpp> +#endif + +#include "_test_ec.hpp" +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +enum class E1 { e11, e12, e13 }; +enum class E2 { e21, e22, e23 }; +enum class E3 { e31, e32, e33 }; + +int main() +{ +#if !defined(__clang__) || __clang_major__ < 5 || __clang_major__ > 7 // See https://github.com/llvm/llvm-project/issues/32569 + { + auto v = leaf::to_variant<E1, E2, E3>( + [ ]() -> leaf::result<int> + { + return 42; + } ); + BOOST_TEST(v.index() == 0); + BOOST_TEST(std::get<0>(v) == 42); + } + + { + auto v = leaf::to_variant<E1, E2, E3>( + [ ]() -> leaf::result<int> + { + return leaf::new_error(E1::e12, E3::e31); + } ); + BOOST_TEST(v.index() == 1); + auto t = std::get<1>(v); + + BOOST_TEST(std::get<0>(t).has_value()); + BOOST_TEST(!std::get<1>(t).has_value()); + BOOST_TEST(std::get<2>(t).has_value()); + + BOOST_TEST(std::get<0>(t).value() == E1::e12); + BOOST_TEST(std::get<2>(t).value() == E3::e31); + } +#endif + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/try_catch_error_id_test.cpp b/src/boost/libs/leaf/test/try_catch_error_id_test.cpp new file mode 100644 index 000000000..5b32875c5 --- /dev/null +++ b/src/boost/libs/leaf/test/try_catch_error_id_test.cpp @@ -0,0 +1,56 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/exception.hpp> +# include <boost/leaf/pred.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +struct my_error: std::exception { }; + +int main() +{ + int r = leaf::try_catch( + []() -> int + { + throw leaf::exception( my_error(), info{42} ); + }, + []( my_error const & x, leaf::catch_<leaf::error_id> id ) + { + BOOST_TEST(dynamic_cast<leaf::error_id const *>(&id.matched)!=0 && dynamic_cast<leaf::error_id const *>(&id.matched)->value()==1); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/try_catch_system_error_test.cpp b/src/boost/libs/leaf/test/try_catch_system_error_test.cpp new file mode 100644 index 000000000..2ece5fd99 --- /dev/null +++ b/src/boost/libs/leaf/test/try_catch_system_error_test.cpp @@ -0,0 +1,150 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#if defined(BOOST_LEAF_NO_EXCEPTIONS) || !BOOST_LEAF_CFG_STD_SYSTEM_ERROR + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/exception.hpp> +# include <boost/leaf/pred.hpp> +#endif + +#include "_test_ec.hpp" +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +int main() +{ + { + int r = leaf::try_catch( + []() -> int + { + throw leaf::exception( std::system_error(make_error_code(errc_a::a0)), info{42} ); + }, + []( std::system_error const & se, leaf::match_value<info, 42> ) + { + BOOST_TEST_EQ(se.code(), errc_a::a0); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + { + int r = leaf::try_catch( + []() -> int + { + auto load = leaf::on_error(info{42}); + throw std::system_error(make_error_code(errc_a::a0)); + }, + []( std::system_error const & se, leaf::match_value<info, 42> ) + { + BOOST_TEST_EQ(se.code(), errc_a::a0); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + //////////////////////////////////////// + { + int r = leaf::try_catch( + []() -> int + { + throw leaf::exception( std::system_error(make_error_code(errc_a::a0)), info{42} ); + }, + []( leaf::match<leaf::condition<errc_a>, errc_a::a0> code, leaf::match_value<info, 42> ) + { + std::error_code const & ec = code.matched; + BOOST_TEST_EQ(ec, errc_a::a0); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + { + int r = leaf::try_catch( + []() -> int + { + auto load = leaf::on_error(info{42}); + throw std::system_error(make_error_code(errc_a::a0)); + }, + []( leaf::match<leaf::condition<errc_a>, errc_a::a0> code, leaf::match_value<info, 42> ) + { + std::error_code const & ec = code.matched; + BOOST_TEST_EQ(ec, errc_a::a0); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + //////////////////////////////////////// + { + int r = leaf::try_catch( + []() -> int + { + throw leaf::exception( std::system_error(make_error_code(errc_a::a0)), info{42} ); + }, + []( std::error_code const & ec, leaf::match_value<info, 42> ) + { + BOOST_TEST_EQ(ec, errc_a::a0); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + { + int r = leaf::try_catch( + []() -> int + { + auto load = leaf::on_error(info{42}); + throw std::system_error(make_error_code(errc_a::a0)); + }, + []( std::error_code const & ec, leaf::match_value<info, 42> ) + { + BOOST_TEST_EQ(ec, errc_a::a0); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + //////////////////////////////////////// + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/try_catch_test.cpp b/src/boost/libs/leaf/test/try_catch_test.cpp new file mode 100644 index 000000000..d14740f4c --- /dev/null +++ b/src/boost/libs/leaf/test/try_catch_test.cpp @@ -0,0 +1,622 @@ +// 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) + +#include <boost/leaf/handle_errors.hpp> +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +int main() +{ + int r = leaf::try_catch( + [] + { + return 42; + }, + [] + { + return 1; + } ); + BOOST_TEST_EQ(r, 42); + + return boost::report_errors(); +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/pred.hpp> +#endif + +template <int> struct info { int value; }; + +struct error1: std::exception { }; +struct error2: std::exception { }; +struct error3: std::exception { }; + +struct exc_val: std::exception { int value; explicit exc_val(int v): value(v) { } }; + +template <class R,class Ex> +R failing( Ex && ex ) +{ + throw leaf::exception(std::move(ex), info<1>{1}, info<2>{2}, info<3>{3}); +} + +template <class R> +R succeeding() +{ + return R(42); +} + +int main() +{ + // void, try_catch (success) + { + int c=0; + leaf::try_catch( + [&c] + { + c = succeeding<int>(); + }, + [&c] + { + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST_EQ(c, 42); + } + + // void, try_catch (failure), match_enum (single enum value) + { + int c=0; + leaf::try_catch( + [&c] + { + c = failing<int>(error1()); + }, + [&c]( error2 const & ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]( error1 const &, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c] + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + + // void, try_catch (failure), match_enum (multiple enum values) + { + int c=0; + leaf::try_catch( + [&c] + { + c = failing<int>(error1()); + }, + [&c]( error2 const & ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]( leaf::catch_<error2,error1>, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c] + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + + // void, try_catch (failure), match_value (single value) + { + int c=0; + leaf::try_catch( + [&c] + { + c = failing<int>(error1()); + }, + [&c]( error2 const & ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]( error1 const &, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c] + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + + // void, try_catch (failure), match_value (multiple values) + { + int c=0; + leaf::try_catch( + [&c] + { + c = failing<int>(error1()); + }, + [&c]( error2 const & ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + }, + [&c]( leaf::catch_<error2,error1>, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c] + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + + ////////////////////////////////////// + + // void, handle_some (failure, initially not matched), match_enum (single enum value) + { + int c=0; + leaf::try_catch( + [&c] + { + leaf::try_catch( + [&c] + { + c = failing<int>(error1()); + }, + [&c]( error2 const & ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(false); + }, + [&c]( error1 const &, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c] + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + + // void, handle_some (failure, initially not matched), match_enum (multiple enum values) + { + int c=0; + leaf::try_catch( + [&c] + { + leaf::try_catch( + [&c] + { + c = failing<int>(error1()); + }, + [&c]( error2 const & ) + { + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + BOOST_TEST(false); + }, + [&c]( leaf::catch_<error2,error1>, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c] + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 2); + } + + // void, handle_some (failure, initially matched), match_enum (single enum value) + { + int c=0; + leaf::try_catch( + [&c] + { + leaf::try_catch( + [&c] + { + c = failing<int>(error1()); + }, + [&c]( error1 const &, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + }, + [&c]( error2 const & ) + { + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c] + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 1); + } + + // void, handle_some (failure, initially matched), match_enum (multiple enum values) + { + int c=0; + leaf::try_catch( + [&c] + { + leaf::try_catch( + [&c] + { + c = failing<int>(error1()); + }, + [&c]( leaf::catch_<error2,error1>, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + BOOST_TEST_EQ(c, 0); + c = 1; + } ); + }, + [&c]( error2 const & ) + { + BOOST_TEST_EQ(c, 0); + c = 2; + }, + [&c] + { + BOOST_TEST_EQ(c, 0); + c = 3; + } ); + BOOST_TEST_EQ(c, 1); + } + + ////////////////////////////////////// + + // int, try_catch (success) + { + int r = leaf::try_catch( + [] + { + return succeeding<int>(); + }, + [] + { + return 1; + } ); + BOOST_TEST_EQ(r, 42); + } + + // int, try_catch (failure), match_enum (single enum value) + { + int r = leaf::try_catch( + [] + { + return failing<int>(error1()); + }, + []( error2 const & ) + { + return 1; + }, + []( error1 const &, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + + // int, try_catch (failure), match_enum (multiple enum values) + { + int r = leaf::try_catch( + [] + { + return failing<int>(error1()); + }, + []( error2 const & ) + { + return 1; + }, + []( leaf::catch_<error2,error1>, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + + ////////////////////////////////////// + + // int, handle_some (failure, matched), match_enum (single enum value) + { + int r = leaf::try_catch( + [] + { + return failing<int>(error1()); + }, + []( error2 const & ) + { + return 1; + }, + []( error1 const &, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + } ); + BOOST_TEST_EQ(r, 2); + } + + // int, handle_some (failure, matched), match_enum (multiple enum values) + { + int r = leaf::try_catch( + [] + { + return failing<int>(error1()); + }, + []( error2 const & ) + { + return 1; + }, + []( leaf::catch_<error2,error1>, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + } ); + BOOST_TEST_EQ(r, 2); + } + + // int, handle_some (failure, initially not matched), match_enum (single enum value) + { + int r = leaf::try_catch( + [] + { + int r = leaf::try_catch( + [] + { + return failing<int>(error1()); + }, + []( error2 const & ) + { + return 1; + } ); + BOOST_TEST(false); + return r; + }, + []( error1 const &, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + + // int, handle_some (failure, initially not matched), match_enum (multiple enum values) + { + int r = leaf::try_catch( + [] + { + int r = leaf::try_catch( + [] + { + return failing<int>(error1()); + }, + []( error2 const & ) + { + return 1; + } ); + BOOST_TEST(false); + return r; + }, + []( leaf::catch_<error2,error1>, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 2); + } + + // int, handle_some (failure, initially matched), match_enum (single enum value) + { + int r = leaf::try_catch( + [] + { + int r = leaf::try_catch( + [] + { + return failing<int>(error1()); + }, + []( error1 const &, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 1; + } ); + BOOST_TEST_EQ(r, 1); + return r; + }, + []( error1 const & ) + { + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 1); + } + + // int, handle_some (failure, initially matched), match_enum (multiple enum values) + { + int r = leaf::try_catch( + [] + { + int r = leaf::try_catch( + [] + { + return failing<int>(error1()); + }, + []( leaf::catch_<error2,error1>, info<1> const & x, info<2> y ) + { + BOOST_TEST_EQ(x.value, 1); + BOOST_TEST_EQ(y.value, 2); + return 1; + } ); + BOOST_TEST_EQ(r, 1); + return r; + }, + []( error1 const & ) + { + return 2; + }, + [] + { + return 3; + } ); + BOOST_TEST_EQ(r, 1); + } + + ////////////////////////////////////// + + // match<> with exceptions + { + int r = leaf::try_catch( + [] + { + throw leaf::exception(exc_val{42}); + return 0; + }, + []( leaf::match_value<exc_val, 42> ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + { + int r = leaf::try_catch( + [] + { + throw leaf::exception(exc_val{42}); + return 0; + }, + []( leaf::match_value<exc_val, 41> ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 2); + } + { + int r = leaf::try_catch( + [] + { + throw exc_val{42}; + return 0; + }, + []( leaf::match_value<exc_val, 42> ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + { + int r = leaf::try_catch( + [] + { + throw exc_val{42}; + return 0; + }, + []( leaf::match_value<exc_val, 41> ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 2); + } + + ////////////////////////////////////// + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/try_exception_and_result_test.cpp b/src/boost/libs/leaf/test/try_exception_and_result_test.cpp new file mode 100644 index 000000000..954ab091f --- /dev/null +++ b/src/boost/libs/leaf/test/try_exception_and_result_test.cpp @@ -0,0 +1,709 @@ +// 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) + +#include <boost/leaf/config.hpp> + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +#include <iostream> + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/pred.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template <int> struct info { int value; }; + +struct my_exception: std::exception +{ + int value; + + my_exception(): + value(0) + { + } + + my_exception(int v): + value(v) + { + } +}; + +int main() +{ + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + return 42; + }, + [] + { + return 1; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(r.value(), 42); + } + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + throw leaf::exception( my_exception(), info<1>{1} ); + }, + []( my_exception const &, info<1> const & x ) + { + BOOST_TEST_EQ(x.value, 1); + return 1; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(r.value(), 1); + } + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + throw leaf::exception( info<1>{1} ); + }, + []( info<1> const & x ) + { + BOOST_TEST_EQ(x.value, 1); + return 1; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(r.value(), 1); + } + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{1} ); + }, + []( info<1> const & x ) + { + BOOST_TEST_EQ(x.value, 1); + return 1; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(r.value(), 1); + } + + /////////////////////////// + + { + auto error_handlers = std::make_tuple( + []( my_exception const &, info<1> const & x ) -> leaf::result<int> + { + BOOST_TEST_EQ(x.value, 1); + return 1; + }, + []( info<1> const & x ) -> leaf::result<int> + { + BOOST_TEST_EQ(x.value, 1); + return 2; + } ); + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + return 42; + }, + error_handlers ); + BOOST_TEST(r); + BOOST_TEST_EQ(r.value(), 42); + } + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + throw leaf::exception( my_exception(), info<1>{1} ); + }, + error_handlers ); + BOOST_TEST(r); + BOOST_TEST_EQ(r.value(), 1); + } + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + throw leaf::exception( info<1>{1} ); + }, + error_handlers ); + BOOST_TEST(r); + BOOST_TEST_EQ(r.value(), 2); + } + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{1} ); + }, + error_handlers ); + BOOST_TEST(r); + BOOST_TEST_EQ(r.value(), 2); + } + } + + /////////////////////////// + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return 42; + }, + [] + { + return 1; + } ); + BOOST_TEST_EQ(r, 42); + } + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + throw leaf::exception( my_exception(), info<1>{1} ); + }, + []( my_exception const &, info<1> const & x ) + { + BOOST_TEST_EQ(x.value, 1); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + throw leaf::exception( info<1>{1} ); + }, + []( info<1> const & x ) + { + BOOST_TEST_EQ(x.value, 1); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{1} ); + }, + []( info<1> const & x ) + { + BOOST_TEST_EQ(x.value, 1); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + + /////////////////////////// + + { + auto error_handlers = std::make_tuple( + []( my_exception const &, info<1> const & x ) + { + BOOST_TEST_EQ(x.value, 1); + return 1; + }, + []( info<1> const & x ) + { + BOOST_TEST_EQ(x.value, 1); + return 2; + }, + [] + { + return 1; + } ); + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return 42; + }, + error_handlers ); + BOOST_TEST_EQ(r, 42); + } + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + throw leaf::exception( my_exception(), info<1>{1} ); + }, + error_handlers ); + BOOST_TEST_EQ(r, 1); + } + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + throw leaf::exception( info<1>{1} ); + }, + error_handlers ); + BOOST_TEST_EQ(r, 2); + } + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{1} ); + }, + error_handlers ); + BOOST_TEST_EQ(r, 2); + } + } + + /////////////////////////// + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{1} ); + }, + []( info<1> const & ) -> int + { + BOOST_LEAF_THROW_EXCEPTION(my_exception()); + }, + [] + { + return 1; + } ); + }, + []( my_exception const &, info<1> ) + { + return 2; + }, + []( my_exception const & ) + { + return 3; + }, + [] + { + return 4; + } ); + + BOOST_TEST_EQ(r, 3); + } + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{1} ); + }, + []( info<1> const & x ) -> int + { + BOOST_TEST_EQ(x.value, 1); + BOOST_LEAF_THROW_EXCEPTION(); + }, + [] + { + return 1; + } ); + }, + []( my_exception const &, info<1> ) + { + return 2; + }, + []( my_exception const & ) + { + return 3; + }, + [] + { + return 4; + } ); + + BOOST_TEST_EQ(r, 4); + } + + /////////////////////////// + + { + auto error_handlers = std::make_tuple( + []( info<1> const & x ) -> int + { + BOOST_TEST_EQ(x.value, 1); + BOOST_LEAF_THROW_EXCEPTION(my_exception()); + }, + [] + { + return 1; + } ); + int r = leaf::try_handle_all( + [&]() -> leaf::result<int> + { + return leaf::try_handle_all( + [&]() -> leaf::result<int> + { + return leaf::new_error( info<1>{1} ); + }, + error_handlers ); + }, + []( my_exception const &, info<1> ) + { + return 2; + }, + []( my_exception const & ) + { + return 3; + }, + [] + { + return 4; + } ); + + BOOST_TEST_EQ(r, 3); + } + { + auto error_handlers = std::make_tuple( + []( info<1> const & x ) -> int + { + BOOST_TEST_EQ(x.value, 1); + BOOST_LEAF_THROW_EXCEPTION(); + }, + [] + { + return 1; + } ); + int r = leaf::try_handle_all( + [&]() -> leaf::result<int> + { + return leaf::try_handle_all( + [&]() -> leaf::result<int> + { + return leaf::new_error( info<1>{1} ); + }, + error_handlers ); + }, + []( my_exception const &, info<1> ) + { + return 2; + }, + []( my_exception const & ) + { + return 3; + }, + [] + { + return 4; + } ); + + BOOST_TEST_EQ(r, 4); + } + + /////////////////////////// + + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::try_handle_some( + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{1} ); + }, + []( info<1> const & x ) -> int + { + BOOST_TEST_EQ(x.value, 1); + BOOST_LEAF_THROW_EXCEPTION(my_exception()); + }, + [] + { + return 1; + } ); + }, + []( my_exception const &, info<1> ) + { + return 3; + }, + []( my_exception const & ) + { + return 4; + }, + [] + { + return 5; + } ); + + BOOST_TEST_EQ(r, 4); + } + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + return leaf::try_handle_some( + []() -> leaf::result<int> + { + return leaf::new_error( info<1>{1} ); + }, + []( info<1> const & x ) -> int + { + BOOST_TEST_EQ(x.value, 1); + BOOST_LEAF_THROW_EXCEPTION(); + }, + [] + { + return 1; + } ); + }, + []( my_exception const &, info<1> ) + { + return 3; + }, + []( my_exception const & ) + { + return 4; + }, + [] + { + return 5; + } ); + + BOOST_TEST_EQ(r, 5); + } + + /////////////////////////// + + { + auto error_handlers = std::make_tuple( + []( info<1> const & x ) -> leaf::result<int> + { + BOOST_TEST_EQ(x.value, 1); + BOOST_LEAF_THROW_EXCEPTION(my_exception()); + }, + []() -> leaf::result<int> + { + return 1; + } ); + int r = leaf::try_handle_all( + [&]() -> leaf::result<int> + { + return leaf::try_handle_some( + [&]() -> leaf::result<int> + { + return leaf::new_error( info<1>{1} ); + }, + error_handlers ); + }, + []( my_exception const &, info<1> ) + { + return 3; + }, + []( my_exception const & ) + { + return 4; + }, + [] + { + return 5; + } ); + + BOOST_TEST_EQ(r, 4); + } + { + auto error_handlers = std::make_tuple( + []( info<1> const & x ) -> leaf::result<int> + { + BOOST_TEST_EQ(x.value, 1); + BOOST_LEAF_THROW_EXCEPTION(); + }, + []() -> leaf::result<int> + { + return 1; + } ); + int r = leaf::try_handle_all( + [&]() -> leaf::result<int> + { + return leaf::try_handle_some( + [&]() -> leaf::result<int> + { + return leaf::new_error( info<1>{1} ); + }, + error_handlers ); + }, + []( my_exception const &, info<1> ) + { + return 3; + }, + []( my_exception const & ) + { + return 4; + }, + [] + { + return 5; + } ); + + BOOST_TEST_EQ(r, 5); + } + + ////////////////////////////////////// + + // match_value<> with exceptions, try_handle_some + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + throw leaf::exception( my_exception(42) ); + }, + []( leaf::match_value<my_exception, 42> m ) + { + return m.matched.value; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(r.value(), 42); + } + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + throw my_exception(42); + }, + []( leaf::match_value<my_exception, 42> m ) + { + return m.matched.value; + } ); + BOOST_TEST(r); + BOOST_TEST_EQ(r.value(), 42); + } + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + throw leaf::exception( my_exception(42) ); + }, + []( leaf::match_value<my_exception, 41> m ) + { + return m.matched.value; + }, + []( leaf::error_info const & unmatched ) + { + return unmatched.error(); + } ); + BOOST_TEST(!r); + } + { + leaf::result<int> r = leaf::try_handle_some( + []() -> leaf::result<int> + { + throw my_exception(42); + }, + []( leaf::match_value<my_exception, 41> m ) + { + return m.matched.value; + }, + []( leaf::error_info const & unmatched ) + { + return unmatched.error(); + } ); + BOOST_TEST(!r); + } + + ////////////////////////////////////// + + // match_value<> with exceptions, try_handle_all + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + throw leaf::exception( my_exception(42) ); + }, + []( leaf::match_value<my_exception, 42> m ) + { + return m.matched.value; + }, + [] + { + return -1; + } ); + BOOST_TEST_EQ(r, 42); + } + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + throw my_exception(42); + }, + []( leaf::match_value<my_exception, 42> m ) + { + return m.matched.value; + }, + [] + { + return -1; + } ); + BOOST_TEST_EQ(r, 42); + } + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + throw leaf::exception( my_exception(42) ); + }, + []( leaf::match_value<my_exception, 41> m ) + { + return m.matched.value; + }, + [] + { + return -1; + } ); + BOOST_TEST_EQ(r, -1); + } + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + throw my_exception(42); + }, + []( leaf::match_value<my_exception, 41> m ) + { + return m.matched.value; + }, + [] + { + return -1; + } ); + BOOST_TEST_EQ(r, -1); + } + + return boost::report_errors(); +} + +#endif diff --git a/src/boost/libs/leaf/test/visibility_test.cpp b/src/boost/libs/leaf/test/visibility_test.cpp new file mode 100644 index 000000000..d3d58fe9e --- /dev/null +++ b/src/boost/libs/leaf/test/visibility_test.cpp @@ -0,0 +1,109 @@ +// 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) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include <boost/leaf/handle_errors.hpp> +# include <boost/leaf/result.hpp> +#endif + +#include "visibility_test_lib.hpp" +#include "lightweight_test.hpp" +#include <sstream> + +namespace leaf = boost::leaf; + +leaf::result<void> hidden_result(); +void hidden_throw(); + +int main() +{ + { + int r = leaf::try_handle_all( + []() -> leaf::result<int> + { + BOOST_LEAF_CHECK(hidden_result()); + return 0; + }, + []( my_info<1> x1, my_info<2> x2, leaf::diagnostic_info const & info, leaf::verbose_diagnostic_info const & vinfo ) + { + BOOST_TEST_EQ(x1.value, 1); + BOOST_TEST_EQ(x2.value, 2); + if( BOOST_LEAF_CFG_DIAGNOSTICS ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::stringstream ss; ss << info; + BOOST_TEST_NE(ss.str().find("1 attempt to communicate an unexpected error object"), std::string::npos); +#endif + } + if( BOOST_LEAF_CFG_DIAGNOSTICS ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::stringstream ss; ss << vinfo; + BOOST_TEST_NE(ss.str().find("Test my_info<3>::value = 3"), std::string::npos); +#endif + } + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + +#ifndef BOOST_LEAF_NO_EXCEPTIONS + { + int r = leaf::try_catch( + [] + { + hidden_throw(); + return 0; + }, + []( my_info<1> x1, my_info<2> x2, leaf::diagnostic_info const & info, leaf::verbose_diagnostic_info const & vinfo ) + { + BOOST_TEST_EQ(x1.value, 1); + BOOST_TEST_EQ(x2.value, 2); + if( BOOST_LEAF_CFG_DIAGNOSTICS ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::stringstream ss; ss << info; + BOOST_TEST_NE(ss.str().find("1 attempt to communicate an unexpected error object"), std::string::npos); +#endif + } + if( BOOST_LEAF_CFG_DIAGNOSTICS ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::stringstream ss; ss << vinfo; + BOOST_TEST_NE(ss.str().find("Test my_info<3>::value = 3"), std::string::npos); +#endif + } + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + { + try + { + hidden_throw(); + BOOST_ERROR("hidden_throw() failed to throw"); + } + catch( leaf::error_id const & ) + { + } + catch(...) + { + BOOST_ERROR("Failed to catch leaf::error_id"); + } + } +#endif + +return boost::report_errors(); +} diff --git a/src/boost/libs/leaf/test/visibility_test_lib.cpp b/src/boost/libs/leaf/test/visibility_test_lib.cpp new file mode 100644 index 000000000..2c4a0ac31 --- /dev/null +++ b/src/boost/libs/leaf/test/visibility_test_lib.cpp @@ -0,0 +1,27 @@ +// 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) + +#include "visibility_test_lib.hpp" +#include <boost/leaf/exception.hpp> +#include <boost/leaf/result.hpp> +#include <boost/leaf/on_error.hpp> + +namespace leaf = boost::leaf; + +leaf::result<void> BOOST_SYMBOL_VISIBLE hidden_result() +{ + auto load = leaf::on_error( my_info<1>{1}, my_info<3>{3} ); + return leaf::new_error( my_info<2>{2} ); +} + +#ifndef BOOST_NO_EXCEPTIONS + +void BOOST_SYMBOL_VISIBLE hidden_throw() +{ + auto load = leaf::on_error( my_info<1>{1}, my_info<3>{3} ); + throw leaf::exception( my_info<2>{2} ); +} + +#endif diff --git a/src/boost/libs/leaf/test/visibility_test_lib.hpp b/src/boost/libs/leaf/test/visibility_test_lib.hpp new file mode 100644 index 000000000..bd924c3f4 --- /dev/null +++ b/src/boost/libs/leaf/test/visibility_test_lib.hpp @@ -0,0 +1,24 @@ +#ifndef VISIBILITY_TEST_LIB_HPP_INCLUDED +#define VISIBILITY_TEST_LIB_HPP_INCLUDED + +// 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) + +#include <boost/config.hpp> +#include <iosfwd> + +template <int Tag> +struct BOOST_SYMBOL_VISIBLE my_info +{ + int value; + + template <class CharT, class Traits> + friend std::basic_ostream<CharT, Traits> & operator<<( std::basic_ostream<CharT, Traits> & os, my_info const & x ) + { + return os << "Test my_info<" << Tag << ">::value = " << x.value; + } +}; + +#endif diff --git a/src/boost/libs/leaf/wasm.txt b/src/boost/libs/leaf/wasm.txt new file mode 100644 index 000000000..82e568fe6 --- /dev/null +++ b/src/boost/libs/leaf/wasm.txt @@ -0,0 +1,18 @@ +# 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) + +# Crossfile for Meson / emscripten + +[binaries] +c = 'emcc' +cpp = 'em++' +ar = 'emar' +# exe_wrapper = ['node', '--experimental-wasm-threads', '--experimental-wasm-bulk-memory', '--max-old-space-size=4096'] + +[host_machine] +system = 'emscripten' +cpu_family = 'wasm32' +cpu = 'wasm32' +endian = 'little' |