From 53daeb8ac38d75fd1e85ac0b09291309f60507d9 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Sun, 16 Sep 2018 15:56:34 -0700 Subject: [PATCH] initial commit --- .gitignore | 10 + .travis.yml | 220 ++++++++++++++++ .vscode/c_cpp_properties.json | 21 ++ .vscode/launch.json | 19 ++ .vscode/tasks.json | 21 ++ example/print_file.cpp | 146 +++++++++++ example/transport_between_threads.cpp | 79 ++++++ gh-pages.sh | 9 + include/boost/leaf/capture.hpp | 45 ++++ include/boost/leaf/common.hpp | 58 +++++ ...rrent_exception_diagnostic_information.hpp | 55 ++++ .../boost/leaf/detail/diagnostic_print.hpp | 65 +++++ include/boost/leaf/detail/optional.hpp | 104 ++++++++ include/boost/leaf/detail/tl_slot.hpp | 226 +++++++++++++++++ include/boost/leaf/diagnostic_information.hpp | 43 ++++ include/boost/leaf/expected.hpp | 186 ++++++++++++++ include/boost/leaf/has_current_error.hpp | 48 ++++ include/boost/leaf/put.hpp | 123 +++++++++ include/boost/leaf/transport.hpp | 99 ++++++++ meson.build | 41 +++ test/Jamfile.v2 | 21 ++ test/diagnostic_information_test.cpp | 103 ++++++++ test/diagnostic_print_test.cpp | 77 ++++++ test/expected_test.cpp | 167 ++++++++++++ test/tl_slot_test.cpp | 240 ++++++++++++++++++ test/transport_test.cpp | 80 ++++++ 26 files changed, 2306 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 example/print_file.cpp create mode 100644 example/transport_between_threads.cpp create mode 100755 gh-pages.sh create mode 100644 include/boost/leaf/capture.hpp create mode 100644 include/boost/leaf/common.hpp create mode 100644 include/boost/leaf/current_exception_diagnostic_information.hpp create mode 100644 include/boost/leaf/detail/diagnostic_print.hpp create mode 100644 include/boost/leaf/detail/optional.hpp create mode 100644 include/boost/leaf/detail/tl_slot.hpp create mode 100644 include/boost/leaf/diagnostic_information.hpp create mode 100644 include/boost/leaf/expected.hpp create mode 100644 include/boost/leaf/has_current_error.hpp create mode 100644 include/boost/leaf/put.hpp create mode 100644 include/boost/leaf/transport.hpp create mode 100644 meson.build create mode 100644 test/Jamfile.v2 create mode 100644 test/diagnostic_information_test.cpp create mode 100644 test/diagnostic_print_test.cpp create mode 100644 test/expected_test.cpp create mode 100644 test/tl_slot_test.cpp create mode 100644 test/transport_test.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..842d8d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +.Trashes +*.suo +*.user +*.log +*.sdf +*.opensdf +*.db +*.opendb +bld/* \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..be3b532 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,220 @@ +# Copyright 2017 Emil Dotchevski +# Copyright 2016 Peter Dimov +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) + +language: cpp + +sudo: false + +python: "2.7" + +os: + - linux + - osx + +branches: + only: + - master + - develop + +env: + matrix: + - BOGUS_JOB=true + +matrix: + + exclude: + - env: BOGUS_JOB=true + + include: + - os: osx + compiler: clang++ + env: TOOLSET=clang COMPILER=clang++ CXXSTD=11,14,1z + + - os: linux + compiler: g++-4.8 + env: TOOLSET=gcc COMPILER=g++-4.8 CXXSTD=11 + addons: + apt: + packages: + - g++-4.8 + sources: + - ubuntu-toolchain-r-test + + - os: linux + compiler: g++-4.9 + env: TOOLSET=gcc COMPILER=g++-4.9 CXXSTD=11 + addons: + apt: + packages: + - g++-4.9 + sources: + - ubuntu-toolchain-r-test + + - os: linux + compiler: g++-5 + env: TOOLSET=gcc COMPILER=g++-5 CXXSTD=11,14,1z + addons: + apt: + packages: + - g++-5 + sources: + - ubuntu-toolchain-r-test + + - os: linux + compiler: g++-6 + env: TOOLSET=gcc COMPILER=g++-6 CXXSTD=11,14,1z + addons: + apt: + packages: + - g++-6 + sources: + - ubuntu-toolchain-r-test + + - os: linux + compiler: g++-7 + env: TOOLSET=gcc COMPILER=g++-7 CXXSTD=11,14,1z + addons: + apt: + packages: + - g++-7 + sources: + - ubuntu-toolchain-r-test + + - os: linux + compiler: g++-8 + env: TOOLSET=gcc COMPILER=g++-8 CXXSTD=11,14,17 + addons: + apt: + packages: + - g++-8 + sources: + - ubuntu-toolchain-r-test + + - os: linux + compiler: /usr/bin/clang++ + env: TOOLSET=clang COMPILER=/usr/bin/clang++ CXXSTD=11 + addons: + apt: + packages: + - clang-3.3 + + - os: linux + compiler: /usr/bin/clang++ + env: TOOLSET=clang COMPILER=/usr/bin/clang++ CXXSTD=11 + addons: + apt: + packages: + - clang-3.4 + + - os: linux + compiler: clang++-3.5 + env: TOOLSET=clang COMPILER=clang++-3.5 CXXSTD=11,14,1z + addons: + apt: + packages: + - clang-3.5 + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.5 + + - os: linux + compiler: clang++-3.6 + env: TOOLSET=clang COMPILER=clang++-3.6 CXXSTD=11,14,1z + addons: + apt: + packages: + - clang-3.6 + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.6 + + - os: linux + compiler: clang++-3.7 + env: TOOLSET=clang COMPILER=clang++-3.7 CXXSTD=11,14,1z + addons: + apt: + packages: + - clang-3.7 + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.7 + + - os: linux + compiler: clang++-3.8 + env: TOOLSET=clang COMPILER=clang++-3.8 CXXSTD=11,14,1z + addons: + apt: + packages: + - clang-3.8 + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 + + - os: linux + compiler: clang++-3.9 + env: TOOLSET=clang COMPILER=clang++-3.9 CXXSTD=11,14,1z + addons: + apt: + packages: + - clang-3.9 + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.9 + + - os: linux + compiler: clang++-4.0 + env: TOOLSET=clang COMPILER=clang++-4.0 CXXSTD=11,14,1z + addons: + apt: + packages: + - clang-4.0 + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-4.0 + + - os: linux + compiler: clang++-5.0 + env: TOOLSET=clang COMPILER=clang++-5.0 CXXSTD=11,14,1z + addons: + apt: + packages: + - clang-5.0 + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + + - os: linux + compiler: clang++-6.0 + env: TOOLSET=clang COMPILER=clang++-6.0 CXXSTD=11,14,17 + addons: + apt: + packages: + - clang-6.0 + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-6.0 + +install: + - cd .. + - git clone -b $TRAVIS_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root + - cd boost-root + - git submodule update --init tools/build + - git submodule update --init tools/inspect + - git submodule update --init libs/config + - git submodule update --init tools/boostdep + - mkdir libs/leaf + - cp -r $TRAVIS_BUILD_DIR/* libs/leaf + - python tools/boostdep/depinst/depinst.py leaf + - ./bootstrap.sh + - ./b2 headers + +script: + - |- + echo "using $TOOLSET : : $COMPILER ;" > ~/user-config.jam + - ./b2 variant=release libs/leaf/build/test toolset=$TOOLSET cxxstd=$CXXSTD cxxflags=-w ${CXXFLAGS:+cxxflags="$CXXFLAGS"} + +notifications: + email: + on_success: always diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..ac665c5 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,21 @@ +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "macFrameworkPath": [ + "/System/Library/Frameworks", + "/Library/Frameworks" + ], + "compilerPath": "/usr/bin/clang", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "clang-x64", + "compileCommands": "${workspaceFolder}/bld/debug/compile_commands.json" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..f6551d0 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(lldb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/bld/debug/basic_info", + "args": [ ], + "cwd": "${workspaceFolder}/bld/debug", + "stopAtEntry": false, + "externalConsole": false, + "MIMode": "lldb" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..2b3a6a0 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,21 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "group": { "kind": "build", "isDefault": true }, + "label": "Build all unit tests and examples", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && ninja", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] } + }, + { + "group": { "kind": "test", "isDefault": true }, + "label": "Run all unit tests", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] } + } + ] +} \ No newline at end of file diff --git a/example/print_file.cpp b/example/print_file.cpp new file mode 100644 index 0000000..16ff9ab --- /dev/null +++ b/example/print_file.cpp @@ -0,0 +1,146 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 +#include +#include +#include +#include +#include + +namespace leaf = boost::leaf; + +//We could define our own exception info types, but for this example the ones +//defined in are a perfect match. +using leaf::xi_file_name; +using leaf::xi_errno; + +//Exception type hierarchy. +struct print_file_error : virtual std::exception { }; +struct command_line_error : virtual print_file_error { }; +struct bad_command_line : virtual command_line_error { }; +struct io_error : virtual print_file_error { }; +struct file_error : virtual io_error { }; +struct fopen_error : virtual file_error { }; +struct fread_error : virtual file_error { }; +struct ftell_error : virtual file_error { }; +struct fseek_error : virtual file_error { }; + +std::shared_ptr +file_open( char const * file_name ) + { + if( FILE * f = fopen(file_name,"rb") ) + return std::shared_ptr(f,&fclose); + else + leaf::throw_with_info( fopen_error(), xi_file_name{file_name}, xi_errno{errno} ); + } + +int +file_size( FILE & f ) + { + auto put = leaf::preload(&leaf::get_errno); + + if( fseek(&f,0,SEEK_END) ) + throw fseek_error(); + int s = ftell(&f); + if( s==-1L ) + throw ftell_error(); + if( fseek(&f,0,SEEK_SET) ) + throw fseek_error(); + return s; + } + +int +file_read( FILE & f, void * buf, int size ) + { + int n = fread(buf,1,size,&f); + if( ferror(&f) ) + leaf::throw_with_info( fread_error(), xi_errno{errno} ); + if( n!=size ) + throw fread_error(); + return n; + } + +void +print_file( char const * file_name ) + { + auto put = leaf::preload( xi_file_name{file_name} ); + + std::shared_ptr f = file_open( file_name ); + std::string buffer( 1+file_size(*f), '\0' ); + file_read(*f,&buffer[0],buffer.size()-1); + std::cout << buffer; + } + +char const * +parse_command_line( int argc, char const * argv[ ] ) + { + if( argc!=2 ) + throw bad_command_line(); + return argv[1]; + } + +int +main( int argc, char const * argv[ ] ) + { + //We expect xi_file_name and xi_errno info to arrive with exceptions handled in this function. + leaf::expected info; + + try + { + print_file(parse_command_line(argc,argv)); + } + catch( + bad_command_line & ) + { + std::cout << "Bad command line argument" << std::endl; + return 1; + } + catch( + fopen_error const & ) + { + //unwrap is given a list of match objects (in this case only one), which it attempts to match (in order) to + //available exception info (if none can be matched, it throws leaf::mismatch_error). + unwrap( info.match( [ ] ( std::string const & fn, int errn ) + { + if( errn==ENOENT ) + std::cerr << "File not found: " << fn << std::endl; + else + std::cerr << "Failed to open " << fn << ", errno=" << errn << std::endl; + } ) ); + return 2; + } + catch( + io_error const & ) + { + //unwrap is given a list of match objects, which it attempts to match (in order) to available exception info. + //In this case it will first check if both xi_file_name and xi_errno are avialable; if not, it will next check + //if just xi_errno is available; and if not, the last match will match even if no exception info is available, + //to print a generic error message. + unwrap( + info.match( [ ] ( std::string const & fn, int errn ) + { + std::cerr << "Failed to access " << fn << ", errno=" << errn << std::endl; + } ), + info.match( [ ] ( int errn ) + { + std::cerr << "I/O error, errno=" << errn << std::endl; + } ), + info.match<>( [ ] + { + std::cerr << "I/O error" << std::endl; + } ) ); + return 3; + } + catch(...) + { + std::cerr << + "Unknown error, cryptic information follows." << std::endl << + leaf::current_exception_diagnostic_information(); + return 6; + } + return 0; + } diff --git a/example/transport_between_threads.cpp b/example/transport_between_threads.cpp new file mode 100644 index 0000000..211b7da --- /dev/null +++ b/example/transport_between_threads.cpp @@ -0,0 +1,79 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 +#include +#include +#include +#include +#include +#include +#include +#include + +namespace leaf = boost::leaf; + +struct failure : virtual std::exception { }; + +struct failed_thread_id { std::thread::id value; }; +struct failure_info1 { std::string value; }; +struct failure_info2 { int value; }; + +struct task_result { }; + +task_result +task( bool succeed ) + { + auto put = leaf::preload( failed_thread_id{std::this_thread::get_id()} ); //Report this thread's id (if expected). + + if( succeed ) + return task_result { }; + else + leaf::throw_with_info( failure(), failure_info1{"info"}, failure_info2{42} ); //Also report both failure_info1 and failure_info2 (if expected). + } + +std::vector> +launch_async_tasks( int thread_count ) + { + std::vector> fut; + std::generate_n( std::inserter(fut,fut.end()), thread_count, [ ] + { + return std::async( std::launch::async, + leaf::transport( [ ] { return task(rand()%4); } ) ); + } ); + return fut; + } + +int +main() + { + //Launch asynchronous tasks. + auto fut = launch_async_tasks(42); + + //Collect results or deal with failures. + for( auto & f : fut ) + { + f.wait(); + try + { + //Get the result, or setup any exception info from the task thread into this thread. + task_result r = leaf::get( [&f] { return f.get(); } ); + + //No exception, consume the result. + std::cout << "Success!" << std::endl; + } + catch( failure const & ) + { + //Caught exception from the task. Inspect and print the info. + leaf::available info; + + unwrap( info.match( [ ] ( std::string const & v1, int v2, std::thread::id tid ) + { + std::cerr << "Error in thread " << tid << "! failure_info1: " << v1 << ", failure_info2: " << v2 << std::endl; + } ) ); + } + } + } diff --git a/gh-pages.sh b/gh-pages.sh new file mode 100755 index 0000000..0b130b7 --- /dev/null +++ b/gh-pages.sh @@ -0,0 +1,9 @@ +set -e +asciidoctor README.adoc -o index1.html +git checkout gh-pages +rm index.html +mv index1.html index.html +git add -u index.html +git commit -m "documentation update" +git push +git checkout master diff --git a/include/boost/leaf/capture.hpp b/include/boost/leaf/capture.hpp new file mode 100644 index 0000000..1636538 --- /dev/null +++ b/include/boost/leaf/capture.hpp @@ -0,0 +1,45 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 UUID_42603B6CB97111E893DFE71A0D39171A +#define UUID_42603B6CB97111E893DFE71A0D39171A + +#include +#include + +namespace +boost + { + namespace + leaf + { + class + capture + { + capture( capture const & ) = delete; + capture & operator=( capture const & ) = delete; + typedef std::vector > container_t; + container_t const info_; + public: + capture() noexcept: + info_( [ ] + { + using namespace leaf_detail; + container_t v; + tl_slot_base::enumerate_put( [&v]( tl_slot_base & p ) + { + if( std::shared_ptr c = p.capture_if_has_value() ) + v.push_back(c); + } ); + return v; + } () ) + { + } + }; + } + } + +#endif diff --git a/include/boost/leaf/common.hpp b/include/boost/leaf/common.hpp new file mode 100644 index 0000000..cb9c273 --- /dev/null +++ b/include/boost/leaf/common.hpp @@ -0,0 +1,58 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 EBA7EF10B6F311E8AAD493990D39171A +#define EBA7EF10B6F311E8AAD493990D39171A + +#include +#include +#include + +namespace +boost + { + namespace + leaf + { + struct xi_api_function { char const * value; }; + struct xi_file_name { std::string value; }; + + struct + xi_errno + { + int value; + friend + inline + std::ostream & + operator<<( std::ostream & os, xi_errno const & err ) + { + using namespace std; + os << strerror(err.value); + return os; + } + }; + inline + xi_errno + get_errno() + { + using namespace std; + return xi_errno{errno}; + } + + struct throw_function; + struct throw_file; + struct throw_line; + template struct xi_source_location { char const * value; }; + template <> struct xi_source_location { int value; }; + } + } + +#define xi_THROW_LOCATION\ + ::boost::leaf::xi_source_location<::boost::leaf::throw_function> {__FUNCTION__},\ + ::boost::leaf::xi_source_location<::boost::leaf::throw_file> {__FILE__},\ + ::boost::leaf::xi_source_location<::boost::leaf::throw_line> {__LINE__} + +#endif diff --git a/include/boost/leaf/current_exception_diagnostic_information.hpp b/include/boost/leaf/current_exception_diagnostic_information.hpp new file mode 100644 index 0000000..3bccfc1 --- /dev/null +++ b/include/boost/leaf/current_exception_diagnostic_information.hpp @@ -0,0 +1,55 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 UUID_95126C26B8B411E8B5E01E350D39171A +#define UUID_95126C26B8B411E8B5E01E350D39171A + +#include +#include +#include + +namespace +boost + { + namespace + leaf + { + class + current_exception_diagnostic_information: + diagnostic_information + { + current_exception_diagnostic_information( current_exception_diagnostic_information const & ); + current_exception_diagnostic_information & operator=( current_exception_diagnostic_information const & ); + public: + current_exception_diagnostic_information() + { + } + friend + std::ostream & + operator<<( std::ostream & os, current_exception_diagnostic_information const & di ) + { + os << "Current exception diagnostic Information:" << std::endl; + try + { + throw; + } + catch( std::exception & ex ) + { + os << + "std::exception::what(): " << ex.what() << std::endl << + "Dynamic typeid: " << typeid(ex).name() << std::endl; + } + catch( ... ) + { + os << "Not a std::exception" << std::endl; + } + return os << static_cast(di); + } + }; + } + } + +#endif diff --git a/include/boost/leaf/detail/diagnostic_print.hpp b/include/boost/leaf/detail/diagnostic_print.hpp new file mode 100644 index 0000000..7117c00 --- /dev/null +++ b/include/boost/leaf/detail/diagnostic_print.hpp @@ -0,0 +1,65 @@ +//Copyright (c) 2006-2009 Emil Dotchevski and Reverge Studios, Inc. +//Copyright (c) 2018 Second Spectrum, 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 UUID_3BAB50A2B87E11E89EEB30600C39171A +#define UUID_3BAB50A2B87E11E89EEB30600C39171A + +#include + +namespace +boost + { + namespace + leaf + { + template + char const * + type() + { +#ifdef __FUNCSIG__ + return __FUNCSIG__; +#else + return __PRETTY_FUNCTION__; +#endif + } + namespace + leaf_detail + { + template struct is_printable { static constexpr bool value=false; }; + template struct is_printable()<(), void())> { static constexpr bool value=true; }; + template ::value> + struct + diagnostic + { + template + static + void + print( std::ostream & os, T const & x ) + { + if( PrintType ) + os << type() << " = "; + os << x; + } + }; + template + struct + diagnostic + { + template + static + void + print( std::ostream & os, T const & ) + { + if( PrintType ) + os << type() << " = "; + os << "N/A"; + } + }; + } + } + } + +#endif diff --git a/include/boost/leaf/detail/optional.hpp b/include/boost/leaf/detail/optional.hpp new file mode 100644 index 0000000..5434a70 --- /dev/null +++ b/include/boost/leaf/detail/optional.hpp @@ -0,0 +1,104 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 UUID_47258FCCB6B411E8A1F35AA00C39171A +#define UUID_47258FCCB6B411E8A1F35AA00C39171A + +#include +#include +#include + +namespace +boost + { + namespace + leaf + { + namespace + leaf_detail + { + template + class + optional + { + optional & operator=( optional const & ) = delete; + union { T value_; }; + bool has_value_; + public: + optional() noexcept: + has_value_(false) + { + } + optional( optional && x ) noexcept: + has_value_(false) + { + if( x.has_value() ) + put(std::move(x.value())); + } + optional( T && v ): + has_value_(false) + { + put(std::move(v)); + } + ~optional() noexcept + { + reset(); + } + virtual + void + reset() noexcept + { + if( has_value() ) + { + value_.~T(); + has_value_=false; + } + } + void + put( T const & v ) + { + reset(); + (void) new(&value_) T(v); + has_value_=true; + } + void + put( T && v ) noexcept + { + reset(); + (void) new(&value_) T(std::move(v)); + has_value_=true; + } + bool + has_value() const noexcept + { + return has_value_; + } + T const & + value() const noexcept + { + assert(has_value()); + return value_; + } + T & + value() noexcept + { + assert(has_value()); + return value_; + } + T + extract_value() noexcept + { + assert(has_value()); + T tmp(std::move(value_)); + reset(); + return tmp; + } + }; + } + } + } + +#endif diff --git a/include/boost/leaf/detail/tl_slot.hpp b/include/boost/leaf/detail/tl_slot.hpp new file mode 100644 index 0000000..f7dae61 --- /dev/null +++ b/include/boost/leaf/detail/tl_slot.hpp @@ -0,0 +1,226 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 UUID_BF995DE0B2DF11E8B0E10DFDAD35F1A2 +#define UUID_BF995DE0B2DF11E8B0E10DFDAD35F1A2 + +#include +#include +#include + +namespace +boost + { + namespace + leaf + { + class dynamic_storage; + namespace + leaf_detail + { + class tl_slot_base; + struct + tl_slot_state + { + tl_slot_base * put_list; + static + tl_slot_state & + tl_instance() noexcept + { + static thread_local tl_slot_state state = { }; + return state; + } + }; + class + tl_slot_base + { + int open_count_; + tl_slot_base * next_put_; + public: + virtual std::shared_ptr capture_if_has_value() = 0; + virtual void reset() noexcept = 0; + virtual bool diagnostic_print( std::ostream & ) const = 0; + protected: + tl_slot_base() noexcept: + open_count_(0), + next_put_(0) + { + } + ~tl_slot_base() noexcept + { + } + void + notify_put() noexcept + { + if( !next_put_ ) + { + auto & s = tl_slot_state::tl_instance(); + next_put_ = s.put_list ? s.put_list : this; + s.put_list = this; + } + } + public: + int + open() noexcept + { + assert(open_count_>=0); + return ++open_count_; + } + int + close() noexcept + { + assert(is_open()); + if( --open_count_==0 ) + reset(); + return open_count_; + } + int + is_open() const noexcept + { + assert(open_count_>=0); + return open_count_; + } + static + void + reset_all() noexcept + { + tl_slot_state & s = tl_slot_state::tl_instance(); + if( tl_slot_base * p = s.put_list ) + for(;;) + { + p->reset(); + tl_slot_base * n = p->next_put_; + assert(n!=0); + p->next_put_ = 0; + if( p==n ) + break; + p = n; + } + s.put_list = 0; + } + template + static + void + enumerate_put( F f ) noexcept + { + tl_slot_state & s = tl_slot_state::tl_instance(); + if( tl_slot_base * p = s.put_list ) + for(;;) + { + tl_slot_base * n = p->next_put_; + assert(n!=0); + f(*p); + if( p==n ) + break; + p = n; + } + } + }; + template + class + tl_slot: + optional, + public tl_slot_base + { + tl_slot( tl_slot const & ) = delete; + tl_slot & operator=( tl_slot const & ) = delete; + typedef optional base; + class + captured_value + { + captured_value( captured_value const & ) = delete; + captured_value & operator=( captured_value const & ) = delete; + T v_; + public: + explicit + captured_value( T && v ) noexcept: + v_(std::move(v)) + { + } + ~captured_value() noexcept + { + tl_slot::tl_instance().put_(std::move(v_)); + } + }; + std::shared_ptr + capture_if_has_value() + { + if( has_value() ) + return std::make_shared(extract_value()); + else + return std::shared_ptr(); + } + tl_slot() noexcept + { + } + ~tl_slot() noexcept + { + } + void + put_( T && x ) noexcept + { + base::put(std::move(x)); + notify_put(); + } + bool + diagnostic_print( std::ostream & os ) const + { + if( has_value() ) + { + if( is_printable::value ) + diagnostic::template print(os,value()); + else + diagnostic::template print(os,value().value); + return true; + } + else + return false; + } + public: + using base::has_value; + using base::value; + using base::extract_value; + bool + put( T const & x ) + { + if( is_open() ) + { + base::put(x); + notify_put(); + return true; + } + else + return false; + } + bool + put( T && x ) noexcept + { + if( is_open() ) + { + put_(std::move(x)); + return true; + } + else + return false; + } + void + reset() noexcept + { + base::reset(); + } + static + tl_slot & + tl_instance() noexcept + { + static thread_local tl_slot x; + return x; + } + }; + } + } + } + +#endif diff --git a/include/boost/leaf/diagnostic_information.hpp b/include/boost/leaf/diagnostic_information.hpp new file mode 100644 index 0000000..a71322a --- /dev/null +++ b/include/boost/leaf/diagnostic_information.hpp @@ -0,0 +1,43 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 UUID_E4B56A60B87011E890CB55B30D39171A +#define UUID_E4B56A60B87011E890CB55B30D39171A + +#include + +namespace +boost + { + namespace + leaf + { + class + diagnostic_information + { + diagnostic_information( diagnostic_information const & ); + diagnostic_information & operator=( diagnostic_information const & ); + public: + diagnostic_information() + { + } + friend + std::ostream & + operator<<( std::ostream & os, diagnostic_information const & ) + { + using namespace leaf_detail; + tl_slot_base::enumerate_put( [&os]( tl_slot_base const & info ) + { + if( info.diagnostic_print(os) ) + os << std::endl; + } ); + return os; + } + }; + } + } + +#endif diff --git a/include/boost/leaf/expected.hpp b/include/boost/leaf/expected.hpp new file mode 100644 index 0000000..e0dec21 --- /dev/null +++ b/include/boost/leaf/expected.hpp @@ -0,0 +1,186 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 UUID_AFBBD676B2FF11E8984C7976AE35F1A2 +#define UUID_AFBBD676B2FF11E8984C7976AE35F1A2 + +#include + +namespace +boost + { + namespace + leaf + { + struct mismatch_error: virtual std::exception { }; + template + void + unwrap( Match && ... m ) + { + using namespace leaf_detail; + bool matched = false; + { using _ = int[ ]; (void) _ { 42, m.unwrap_(matched)... }; } + if( !matched ) + throw mismatch_error(); + } + class + available + { + available( available const & ) = delete; + available & operator=( available const & ) = delete; + template + class + match_ + { + friend class available; + match_( match_ && ) = default; + match_( match_ const & ) = delete; + match_ & operator=( match_ const & ) = delete; + F f_; + explicit + match_( F && f ) noexcept: + f_(std::move(f)) + { + } + public: + int + unwrap_( bool & matched ) const noexcept + { + using namespace leaf_detail; + if( !matched ) + { + bool const available[ ] = { tl_slot::tl_instance().has_value()... }; + for( auto i : available ) + if( !i ) + return 42; + (void) f_(tl_slot::tl_instance().value().value...); + matched=true; + } + return 42; + } + }; + bool reset_all_; + protected: + public: + available() noexcept: + reset_all_(true) + { + } + ~available() noexcept + { + if( reset_all_ ) + leaf_detail::tl_slot_base::reset_all(); + } + void + set_to_propagate() noexcept + { + reset_all_ = false; + } + [[noreturn]] + void + rethrow_with_info() + { + set_to_propagate(); + throw; + } + template + match_ + match( F && f ) noexcept + { + return match_(std::move(f)); + } + }; + namespace + leaf_detail + { +#ifdef _MSC_VER + template + struct + msvc_workaround_open + { + static + void + open() noexcept + { + typedef typename std::tuple_element::type ith_type; + tl_slot::tl_instance().open(); + msvc_workaround_open::open(); + } + }; + template + struct + msvc_workaround_open<0,Tuple> + { + static void open() noexcept { } + }; + template + struct + msvc_workaround_close + { + static + void + close() noexcept + { + typedef typename std::tuple_element::type ith_type; + tl_slot::tl_instance().close(); + msvc_workaround_close::close(); + } + }; + template + struct + msvc_workaround_close<0,Tuple> + { + static void close() noexcept { } + }; + template + void + open_slots() + { + msvc_workaround_open>::open(); + } + template + void + close_slots() + { + msvc_workaround_close>::close(); + } +#else + template + void + open_slots() + { + { using _ = int[ ]; (void) _ { 42, tl_slot::tl_instance().open()... }; } + } + template + void + close_slots() + { + { using _ = int[ ]; (void) _ { 42, tl_slot::tl_instance().close()... }; } + } +#endif + } + template + class + expected: + public available + { + expected( expected const & ) = delete; + expected & operator=( expected const & ) = delete; + public: + expected() noexcept + { + leaf_detail::tl_slot_base::reset_all(); + leaf_detail::open_slots(); + } + ~expected() noexcept + { + leaf_detail::close_slots(); + } + }; + } + } + +#endif diff --git a/include/boost/leaf/has_current_error.hpp b/include/boost/leaf/has_current_error.hpp new file mode 100644 index 0000000..a6203a5 --- /dev/null +++ b/include/boost/leaf/has_current_error.hpp @@ -0,0 +1,48 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 UUID_E20D9594B97411E89C6313280D39171A +#define UUID_E20D9594B97411E89C6313280D39171A + +#include + +namespace +boost + { + namespace + leaf + { + namespace + leaf_detail + { + inline + bool + uncaught_exception_fwd() noexcept + { + return std::uncaught_exception(); + } + bool (* & + has_current_error() noexcept)() + { + static thread_local bool (*f)() noexcept = &uncaught_exception_fwd; + return f; + } + } + bool + has_current_error() noexcept + { + return leaf_detail::has_current_error()(); + } + void + set_has_current_error( bool (*f)() ) noexcept + { + assert(f!=0); + leaf_detail::has_current_error() = f; + } + } + } + +#endif diff --git a/include/boost/leaf/put.hpp b/include/boost/leaf/put.hpp new file mode 100644 index 0000000..ab80d3f --- /dev/null +++ b/include/boost/leaf/put.hpp @@ -0,0 +1,123 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 UUID_8F1C53BEB39F11E8A1C5B6F3E99C4353 +#define UUID_8F1C53BEB39F11E8A1C5B6F3E99C4353 + +#include +#include +#include +#include + +namespace +boost + { + namespace + leaf + { + template + void + put( T && ... a ) noexcept + { + { using _ = bool[ ]; (void) _ { leaf_detail::tl_slot::tl_instance().put(std::forward(a))... }; } + } + template + [[noreturn]] + void + throw_with_info( Exception const & e, T && ... a ) + { + put(std::forward(a)...); + throw e; + } + namespace + leaf_detail + { + template struct can_haz_call { static constexpr bool value=false; }; + template struct can_haz_call()(), void())> { static constexpr bool value=true; }; + template ::value> + struct + defer_dispatch + { + static + void + put_( T && x ) noexcept + { + tl_slot::tl_instance().put(std::move(x)); + } + }; + template + struct + defer_dispatch + { + static + void + put_( F && x ) noexcept + { + put(x()); + } + }; + template + struct + put_meta + { + static + void + put( Tuple && t ) noexcept + { + typedef typename std::tuple_element::type ith_type; + defer_dispatch::put_(std::move(std::get(std::move(t)))); + put_meta::put(std::move(t)); + } + }; + template + struct + put_meta<0,Tuple> + { + static + void + put( Tuple && ) noexcept + { + } + }; + template + class + preloaded + { + preloaded( preloaded const & ) = delete; + preloaded & operator=( preloaded const & ) = delete; + typedef std::tuple::type>::type...> tuple_type; + optional to_put_; + public: + template + explicit + preloaded( U && ... a ): + to_put_(tuple_type(std::forward(a)...)) + { + } + preloaded( preloaded && x ) noexcept: + to_put_(std::move(x.to_put_)) + { + assert(!x.to_put_.has_value()); + } + ~preloaded() noexcept + { + if( to_put_.has_value() && has_current_error() ) + put_meta::put(to_put_.extract_value()); + } + }; + } + template + leaf_detail::preloaded + preload( T && ... a ) + { + return leaf_detail::preloaded(std::forward(a)...); + } + } + } + +#define EXCEPTION_INFO_THROW(e) ::boost::leaf::throw_with_info(e,xi_THROW_LOCATION) + +#endif diff --git a/include/boost/leaf/transport.hpp b/include/boost/leaf/transport.hpp new file mode 100644 index 0000000..629b0e6 --- /dev/null +++ b/include/boost/leaf/transport.hpp @@ -0,0 +1,99 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 UUID_BC24FB98B2DE11E884419CF5AD35F1A2 +#define UUID_BC24FB98B2DE11E884419CF5AD35F1A2 + +#include +#include +#include +#include + +namespace +boost + { + namespace + leaf + { + class + exception_wrapper + { + std::shared_ptr cap_; + std::exception_ptr original_exception_; + public: + explicit + exception_wrapper( std::exception_ptr && original_exception ): + cap_(std::make_shared()), + original_exception_(std::move(original_exception)) + { + } + [[noreturn]] + void + rethrow_original_exception() + { + cap_.reset(); + std::rethrow_exception(original_exception_); + } + }; + namespace + leaf_detail + { + template + class + transport_impl + { + transport_impl & operator=( transport_impl const & ) = delete; + void (* const op_)(); + F f_; + public: + transport_impl( transport_impl const & ) = default; + transport_impl( transport_impl && ) = default; + transport_impl( void (*op)(), F f ): + op_(op), + f_(f) + { + assert(op_!=0); + } + template + decltype(std::declval()()) + operator()( T && ... x ) + { + op_(); + try + { + return f_(std::forward(x)...); + } + catch(...) + { + throw exception_wrapper(std::current_exception()); + } + } + }; + } + template + leaf_detail::transport_impl + transport( F f ) + { + using namespace leaf_detail; + return transport_impl(&open_slots,f); + } + template + decltype(std::declval()()) + get( F f ) + { + try + { + return f(); + } + catch( exception_wrapper & ex ) + { + ex.rethrow_original_exception(); + } + } + } + } + +#endif diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..2b481dc --- /dev/null +++ b/meson.build @@ -0,0 +1,41 @@ +# Copyright (c) 2018 Emil Dotchevski +# Copyright (c) 2018 Second Spectrum, 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++11','b_pch=false'], license : 'boost') + +compiler = meson.get_compiler('cpp') +compiler_id = compiler.get_id() +if not meson.is_subproject() + if compiler_id=='clang' + add_global_arguments( + '-Wno-unused-variable', + '-Wno-non-virtual-dtor', + language:'cpp' ) + endif +endif + +includes = [ include_directories('include') ] + +leaf = declare_dependency( include_directories: includes ) + +tests = [ + 'tl_slot_test', + 'diagnostic_print_test', + 'diagnostic_information_test', + 'expected_test', + 'transport_test' +] +foreach t : tests + test(t, executable(t, 'test/'+t+'.cpp', dependencies: [ leaf ] ) ) +endforeach + +examples = [ + 'print_file', + 'transport_between_threads' +] +foreach e : examples + executable(e, 'example/'+e+'.cpp', dependencies: [ leaf ] ) +endforeach diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 new file mode 100644 index 0000000..0822c40 --- /dev/null +++ b/test/Jamfile.v2 @@ -0,0 +1,21 @@ +# Copyright (c) 2018 Emil Dotchevski +# Copyright (c) 2018 Second Spectrum, 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 ; + +project + : requirements + gcc:"-std=c++11" + darwin:"-std=c++11 -Wno-unused-variable -Wno-non-virtual-dtor" + msvc "-wd 4267" + ../../.. + ; + +run tl_slot_test.cpp ; +run diagnostic_print_test.cpp ; +run diagnostic_information_test.cpp ; +run expected_test.cpp ; +run transport_test.cpp ; diff --git a/test/diagnostic_information_test.cpp b/test/diagnostic_information_test.cpp new file mode 100644 index 0000000..48f5381 --- /dev/null +++ b/test/diagnostic_information_test.cpp @@ -0,0 +1,103 @@ +//Copyright (c) 2006-2009 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 +#include +#include +#include +#include + +namespace leaf = boost::leaf; + +struct +my_error: + virtual std::exception + { + char const * + what() const noexcept + { + return "my_error"; + } + }; + +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::expected + < + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload + > info; + try + { + leaf::throw_with_info( + my_error(), + printable_info_printable_payload(), + printable_info_non_printable_payload(), + non_printable_info_printable_payload(), + non_printable_info_non_printable_payload() ); + } + catch( my_error const & ) + { + std::ostringstream st; + st << leaf::current_exception_diagnostic_information(); + std::string s = st.str(); + BOOST_TEST(s.find("std::exception::what(): my_error")!=s.npos); + BOOST_TEST(s.find(" = N/A")!=s.npos); + BOOST_TEST(s.find(" = printed printable_payload")!=s.npos); + BOOST_TEST(s.find("*** printable_info_non_printable_payload ***")!=s.npos); + BOOST_TEST(s.find("*** printable_info_printable_payload printed printable_payload ***")!=s.npos); + std::cout << s; + } + return boost::report_errors(); + } diff --git a/test/diagnostic_print_test.cpp b/test/diagnostic_print_test.cpp new file mode 100644 index 0000000..54a8a46 --- /dev/null +++ b/test/diagnostic_print_test.cpp @@ -0,0 +1,77 @@ +//Copyright (c) 2006-2009 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 +#include +#include + +namespace leaf = boost::leaf; + +class +c1 + { + friend + std::ostream & + operator<<( std::ostream & os, c1 const & ) + { + return os << "c1"; + } + }; +class +c2 + { + }; +std::ostream & +operator<<( std::ostream & os, c2 const & ) + { + return os << "c2"; + } +class +c3 + { + }; +template +bool +check( T const & x, char const * sub ) + { + using namespace leaf::leaf_detail; + std::ostringstream s; + diagnostic::template print(s,x); + std::string q = s.str(); + return q.find(sub)!=q.npos; + } +int +main() + { + BOOST_TEST(check(42,"42")); + { + int x=42; + int & y = x; + BOOST_TEST(check(x,"42")); + BOOST_TEST(check(y,"42")); + } + BOOST_TEST(check(c1(),"c1")); + { + c1 x; + c1 & y = x; + BOOST_TEST(check(x,"c1")); + BOOST_TEST(check(y,"c1")); + } + BOOST_TEST(check(c2(),"c2")); + { + c2 x; + c2 & y = x; + BOOST_TEST(check(x,"c2")); + BOOST_TEST(check(y,"c2")); + } + BOOST_TEST(check(c3(),"N/A")); + { + c3 x; + c3 & y = x; + BOOST_TEST(check(x,"N/A")); + BOOST_TEST(check(y,"N/A")); + } + return boost::report_errors(); + } diff --git a/test/expected_test.cpp b/test/expected_test.cpp new file mode 100644 index 0000000..9fcc2fc --- /dev/null +++ b/test/expected_test.cpp @@ -0,0 +1,167 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 +#include +#include + +namespace leaf = boost::leaf; + +struct error { }; + +int & +total_count() + { + static thread_local int c=0; + return c; + } +template +int & +count() + { + static thread_local int c=0; + return c; + } +template +struct +my_info + { + my_info( my_info const & ) = delete; + my_info & operator=( my_info const & ) = delete; + public: + int value; + explicit + my_info( int value ): + value(value) + { + ++total_count(); + ++count(); + } + my_info( my_info && x ): + value(x.value) + { + ++total_count(); + ++count(); + } + ~my_info() + { + --total_count(); + --count(); + } + }; +int +main() + { + using namespace leaf::leaf_detail; + /////////////////////////////////////////////// + { + BOOST_TEST(tl_slot>::tl_instance().is_open()==0); + BOOST_TEST(tl_slot>::tl_instance().is_open()==0); + leaf::expected,my_info<3>> info; + BOOST_TEST(tl_slot>::tl_instance().is_open()==1); + BOOST_TEST(tl_slot>::tl_instance().is_open()==1); + try + { + auto put = leaf::preload( my_info<2>(42), my_info<3>(43) ); + throw error(); + } + catch( error const & ) + { + BOOST_TEST(tl_slot>::tl_instance().has_value()); + BOOST_TEST(tl_slot>::tl_instance().has_value()); + bool called = false; + unwrap( info.match,my_info<2>>( [&called]( int x3, int x2 ) + { + called = true; + BOOST_TEST(x2==42); + BOOST_TEST(x3==43); + } ) ); + BOOST_TEST(called); + } + } + BOOST_TEST(!tl_slot>::tl_instance().is_open()); + BOOST_TEST(!tl_slot>::tl_instance().is_open()); + BOOST_TEST(!tl_slot>::tl_instance().is_open()); + BOOST_TEST(!tl_slot>::tl_instance().has_value()); + BOOST_TEST(!tl_slot>::tl_instance().has_value()); + BOOST_TEST(!tl_slot>::tl_instance().has_value()); + BOOST_TEST(total_count()==0); + /////////////////////////////////////////////// + { + leaf::expected,my_info<3>> info; + BOOST_TEST(!tl_slot>::tl_instance().is_open()); + BOOST_TEST(tl_slot>::tl_instance().is_open()==1); + BOOST_TEST(tl_slot>::tl_instance().is_open()==1); + try + { + leaf::expected,my_info<2>> info; + BOOST_TEST(tl_slot>::tl_instance().is_open()==1); + BOOST_TEST(tl_slot>::tl_instance().is_open()==2); + BOOST_TEST(tl_slot>::tl_instance().is_open()==1); + try + { + auto put = leaf::preload(my_info<1>(41)); + throw error(); + } + catch(...) + { + { + bool called = false; + unwrap( info.match>( [&called]( int x1 ) + { + called = true; + BOOST_TEST(x1==41); + } ) ); + BOOST_TEST(called); + } + try + { + unwrap( info.match,my_info<2>>( [ ]( int, int ) { } ) ); + BOOST_TEST(false); + } + catch( leaf::mismatch_error & ) + { + } + leaf::put(my_info<2>(42)); + { + bool called = false; + unwrap( info.match,my_info<2>>( [&called]( int x1, int x2 ) + { + called = true; + BOOST_TEST(x1==41); + BOOST_TEST(x2==42); + } ) ); + BOOST_TEST(called); + } + info.rethrow_with_info(); + } + } + catch( error const & ) + { + BOOST_TEST(!tl_slot>::tl_instance().has_value()); + BOOST_TEST(tl_slot>::tl_instance().has_value()); + BOOST_TEST(!tl_slot>::tl_instance().has_value()); + { + bool called = false; + unwrap( info.match>( [&called]( int x2 ) + { + called = true; + BOOST_TEST(x2==42); + } ) ); + BOOST_TEST(called); + } + } + } + BOOST_TEST(!tl_slot>::tl_instance().is_open()); + BOOST_TEST(!tl_slot>::tl_instance().is_open()); + BOOST_TEST(!tl_slot>::tl_instance().is_open()); + BOOST_TEST(!tl_slot>::tl_instance().has_value()); + BOOST_TEST(!tl_slot>::tl_instance().has_value()); + BOOST_TEST(!tl_slot>::tl_instance().has_value()); + BOOST_TEST(total_count()==0); + /////////////////////////////////////////////// + return boost::report_errors(); + } diff --git a/test/tl_slot_test.cpp b/test/tl_slot_test.cpp new file mode 100644 index 0000000..69fbf8d --- /dev/null +++ b/test/tl_slot_test.cpp @@ -0,0 +1,240 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 +#include +#include +#include +#include + +namespace leaf = boost::leaf; + +struct error { }; + +template +int & +count() + { + static thread_local int c=0; + return c; + } + +template +class +my_info + { + my_info & operator=( my_info const & ) = delete; + public: + int value; + explicit + my_info( int value ): + value(value) + { + BOOST_TEST(++count()>0); + } + my_info( my_info const & x ): + value(x.value) + { + BOOST_TEST(++count()>0); + } + my_info( my_info && x ): + value(x.value) + { + x.value=-1; + BOOST_TEST(++count()>0); + } + ~my_info() + { + BOOST_TEST(--count()>=0); + } + }; + +class +throws_on_copy + { + throws_on_copy & operator=( throws_on_copy const & )=delete; + public: + int value; + throws_on_copy() + { + BOOST_TEST(++count()>0); + } + throws_on_copy( throws_on_copy const & ) + { + throw error(); + } + throws_on_copy( throws_on_copy && ) + { + BOOST_TEST(++count()>0); + } + ~throws_on_copy() + { + BOOST_TEST(--count()>=0); + } + }; + +struct A; +struct B; +struct C; + +void +run_tests() + { + using leaf::leaf_detail::tl_slot; + auto & tls = leaf::leaf_detail::tl_slot_state::tl_instance(); + { + { + throws_on_copy a; + BOOST_TEST(count()==1); + auto & x = tl_slot::tl_instance(); + BOOST_TEST(!x.is_open()); + BOOST_TEST(!x.put(a)); + BOOST_TEST(count()==1); + BOOST_TEST(!x.has_value()); + BOOST_TEST(x.open()==1); + BOOST_TEST(x.put(throws_on_copy())); + BOOST_TEST(count()==2); + BOOST_TEST(x.has_value()); + try + { + (void) x.put(a); + BOOST_TEST(false); + } + catch( error & ) + { + } + BOOST_TEST(count()==1); + BOOST_TEST(!x.has_value()); + BOOST_TEST(x.is_open()); + BOOST_TEST(x.close()==0); + } + BOOST_TEST(count()==0); + { + auto & x = tl_slot>::tl_instance(); + BOOST_TEST(&x==&tl_slot>::tl_instance()); + BOOST_TEST(!x.is_open()); + BOOST_TEST(!x.has_value()); + BOOST_TEST(!x.put(my_info(42))); + BOOST_TEST(!x.has_value()); + BOOST_TEST(x.open()==1); + BOOST_TEST(x.is_open()); + BOOST_TEST(!x.has_value()); + BOOST_TEST(x.open()==2); + BOOST_TEST(x.is_open()); + BOOST_TEST(!x.has_value()); + BOOST_TEST(count>()==0); + BOOST_TEST(x.put(my_info(42))); + BOOST_TEST(x.close()==1); + BOOST_TEST(x.is_open()); + BOOST_TEST(x.has_value()); + BOOST_TEST(count>()==1); + BOOST_TEST(x.close()==0); + BOOST_TEST(!x.is_open()); + BOOST_TEST(!x.has_value()); + BOOST_TEST(count>()==0); + BOOST_TEST(x.open()==1); + BOOST_TEST(x.is_open()); + BOOST_TEST(!x.has_value()); + BOOST_TEST(count>()==0); + BOOST_TEST(x.put(my_info(43))); + BOOST_TEST(x.is_open()); + BOOST_TEST(x.has_value()); + BOOST_TEST(x.open()==2); + BOOST_TEST(x.is_open()); + BOOST_TEST(x.close()==1); + BOOST_TEST(x.close()==0); + BOOST_TEST(!x.has_value()); + } + { + auto & x = tl_slot>::tl_instance(); + BOOST_TEST(&x==&tl_slot>::tl_instance()); + BOOST_TEST(!x.is_open()); + BOOST_TEST(!x.has_value()); + { + my_info tmp(42); + BOOST_TEST(!x.put(tmp)); + } + BOOST_TEST(!x.has_value()); + BOOST_TEST(x.open()==1); + BOOST_TEST(x.is_open()); + BOOST_TEST(!x.has_value()); + BOOST_TEST(x.open()==2); + BOOST_TEST(x.is_open()); + BOOST_TEST(!x.has_value()); + BOOST_TEST(count>()==0); + { + my_info tmp(42); + BOOST_TEST(x.put(tmp)); + } + BOOST_TEST(x.close()==1); + BOOST_TEST(x.is_open()); + BOOST_TEST(x.has_value()); + BOOST_TEST(count>()==1); + BOOST_TEST(x.close()==0); + BOOST_TEST(!x.is_open()); + BOOST_TEST(!x.has_value()); + BOOST_TEST(count>()==0); + BOOST_TEST(x.open()==1); + BOOST_TEST(x.is_open()); + BOOST_TEST(!x.has_value()); + BOOST_TEST(count>()==0); + { + my_info tmp(43); + BOOST_TEST(x.put(tmp)); + } + BOOST_TEST(x.is_open()); + BOOST_TEST(x.has_value()); + BOOST_TEST(x.open()==2); + BOOST_TEST(x.is_open()); + BOOST_TEST(x.close()==1); + BOOST_TEST(x.close()==0); + BOOST_TEST(!x.has_value()); + } + { + auto & x = tl_slot>::tl_instance(); + BOOST_TEST(x.open()==1); + BOOST_TEST(x.is_open()); + BOOST_TEST(!x.has_value()); + BOOST_TEST(x.put(my_info(42))); + BOOST_TEST(x.has_value()); + x.reset(); + BOOST_TEST(x.is_open()); + BOOST_TEST(!x.has_value()); + BOOST_TEST(x.close()==0); + } + { + auto & x = tl_slot>::tl_instance(); + BOOST_TEST(x.open()==1); + BOOST_TEST(x.is_open()); + BOOST_TEST(!x.has_value()); + BOOST_TEST(x.put(my_info(42))); + BOOST_TEST(x.has_value()); + my_info inf = x.extract_value(); + BOOST_TEST(inf.value==42); + BOOST_TEST(x.is_open()); + BOOST_TEST(!x.has_value()); + BOOST_TEST(x.close()==0); + } + } + BOOST_TEST(count>()==0); + BOOST_TEST(count>()==0); + BOOST_TEST(count>()==0); + } + +int +main() + { + std::vector> fut; + std::generate_n( std::inserter(fut,fut.end()), 1, [ ] + { + return std::async( std::launch::async, &run_tests ); + } ); + for( auto & i : fut ) + i.wait(); + for( auto & i : fut ) + i.get(); + return boost::report_errors(); + } diff --git a/test/transport_test.cpp b/test/transport_test.cpp new file mode 100644 index 0000000..f1d1b8c --- /dev/null +++ b/test/transport_test.cpp @@ -0,0 +1,80 @@ +//Copyright (c) 2018 Emil Dotchevski +//Copyright (c) 2018 Second Spectrum, 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 +#include +#include +#include +#include + +namespace leaf = boost::leaf; + +struct error { }; +template struct my_info { int value; }; + +struct +fut_info + { + int a; + int b; + std::future fut; + }; + +int +main() + { + int const thread_count = 20; + std::vector fut; + { + std::generate_n( std::inserter(fut,fut.end()), thread_count, [ ] + { + int const a=rand(); + int const b=rand(); + auto trf = leaf::transport,my_info<2>>( [a,b] + { + auto put = leaf::preload( my_info<1>{a}, my_info<2>{b} ); + throw error(); + } ); + if( rand()%2 ) + return fut_info { a, b, std::async( std::launch::async, trf ) }; + else + { + std::packaged_task task( trf ); + std::future fut = task.get_future(); + std::thread(std::move(task)).detach(); + return fut_info { a, b, std::move(fut) }; + } + } ); + } + for( auto & f : fut ) + { + using namespace leaf::leaf_detail; + f.fut.wait(); + try + { + leaf::get([&f]{f.fut.get();}); + BOOST_TEST(false); + } + catch( error const & ) + { + leaf::available info; + BOOST_TEST(!tl_slot>::tl_instance().is_open()); + BOOST_TEST(!tl_slot>::tl_instance().is_open()); + unwrap( info.match,my_info<2>>( [&f]( int x1, int x2 ) + { + BOOST_TEST(x1==f.a); + BOOST_TEST(x2==f.b); + } ) ); + BOOST_TEST(!tl_slot>::tl_instance().is_open()); + BOOST_TEST(!tl_slot>::tl_instance().is_open()); + BOOST_TEST(tl_slot>::tl_instance().has_value()); + BOOST_TEST(tl_slot>::tl_instance().has_value()); + } + BOOST_TEST(!tl_slot>::tl_instance().has_value()); + BOOST_TEST(!tl_slot>::tl_instance().has_value()); + } + return boost::report_errors(); + }