From 56b247d71aee8ae565b8a135d666fc36815e44be Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Tue, 23 Dec 2025 17:31:52 -0500 Subject: [PATCH] Significant refactoring: - Support for error objects crossing DLL boundaries on Windows via BOOST_LEAF_CFG_WIN32=2. - Internal TLS interface improvements, separating logical allocation from reading. - Dynamic allocations (if enabled) for on_error objects now happens before stack unwinding begins. - BOOST_LEAF_SYMBOL_VISIBLE declarations now separated in config/visibility.hpp. --- .github/workflows/ci.yml | 60 ++- .vscode/tasks.json | 70 ++- README.md | 2 +- doc/leaf.adoc | 80 ++- example/error_log.cpp | 2 +- example/error_trace.cpp | 2 +- example/exception_to_result.cpp | 2 +- example/lua_callback_exceptions.cpp | 2 +- example/lua_callback_result.cpp | 2 +- example/print_file/print_file_exceptions.cpp | 2 +- example/print_file/print_file_leaf_result.cpp | 2 +- .../print_file/print_file_system_result.cpp | 2 +- example/print_half.cpp | 2 +- example/try_capture_all_exceptions.cpp | 2 +- example/try_capture_all_result.cpp | 2 +- gen/generate_single_header.py | 4 +- include/boost/leaf.hpp | 4 +- include/boost/leaf/common.hpp | 27 +- include/boost/leaf/config.hpp | 52 +- include/boost/leaf/config/tls.hpp | 62 ++- include/boost/leaf/config/tls_array.hpp | 112 ++-- include/boost/leaf/config/tls_cpp11.hpp | 96 ++-- include/boost/leaf/config/tls_freertos.hpp | 17 +- include/boost/leaf/config/tls_globals.hpp | 91 ++-- include/boost/leaf/config/tls_win32.hpp | 495 ++++++++++++++++++ include/boost/leaf/config/visibility.hpp | 45 ++ include/boost/leaf/context.hpp | 22 +- include/boost/leaf/detail/all.hpp | 2 +- include/boost/leaf/detail/capture_list.hpp | 17 +- include/boost/leaf/detail/demangle.hpp | 20 +- include/boost/leaf/detail/function_traits.hpp | 8 +- include/boost/leaf/detail/mp11.hpp | 4 +- include/boost/leaf/detail/optional.hpp | 30 +- include/boost/leaf/detail/print.hpp | 10 +- include/boost/leaf/diagnostics.hpp | 28 +- include/boost/leaf/error.hpp | 468 ++++++++++------- include/boost/leaf/exception.hpp | 81 +-- include/boost/leaf/handle_errors.hpp | 60 +-- include/boost/leaf/on_error.hpp | 140 ++++- include/boost/leaf/pred.hpp | 24 +- include/boost/leaf/result.hpp | 53 +- include/boost/leaf/to_variant.hpp | 8 +- meson.build | 54 +- meson_options.txt | 5 +- subprojects/.wraplock | 0 test/Jamfile.v2 | 18 +- test/capture_exception_async_test.cpp | 2 +- test/capture_exception_result_async_test.cpp | 2 +- test/capture_exception_result_unload_test.cpp | 2 +- test/capture_exception_state_test.cpp | 4 +- test/capture_exception_unload_test.cpp | 2 +- test/capture_result_async_test.cpp | 2 +- test/capture_result_state_test.cpp | 4 +- test/capture_result_unload_test.cpp | 2 +- test/e_LastError_test.cpp | 8 +- test/error_code_test.cpp | 28 +- test/exception_test.cpp | 2 +- test/exception_to_result_test.cpp | 2 +- test/handle_all_test.cpp | 14 +- test/handle_some_test.cpp | 28 +- test/lightweight_test.hpp | 5 +- test/match_member_test.cpp | 1 - test/match_test.cpp | 9 +- test/match_value_test.cpp | 1 - test/multiple_errors_test.cpp | 1 - ...accumulate_nested_error_exception_test.cpp | 2 +- ...mulate_nested_new_error_exception_test.cpp | 2 +- ...cumulate_nested_success_exception_test.cpp | 2 +- test/on_error_alloc_fail_test.cpp | 125 +++++ ...rror_defer_nested_error_exception_test.cpp | 2 +- ..._defer_nested_new_error_exception_test.cpp | 2 +- ...or_defer_nested_success_exception_test.cpp | 2 +- test/on_error_dynamic_reserve_test1.cpp | 52 ++ test/on_error_dynamic_reserve_test2.cpp | 58 ++ test/on_error_dynamic_reserve_test3.cpp | 74 +++ test/on_error_preload_basic_test.cpp | 18 + test/on_error_preload_exception_test.cpp | 2 +- ...or_preload_nested_error_exception_test.cpp | 2 +- ...reload_nested_new_error_exception_test.cpp | 2 +- ..._preload_nested_success_exception_test.cpp | 2 +- test/result_state_test.cpp | 4 +- ...isibility_test_lib.cpp => so_dll_lib1.cpp} | 14 +- test/so_dll_lib1.hpp | 29 + test/so_dll_lib2.cpp | 35 ++ test/so_dll_lib2.hpp | 29 + test/so_dll_test.cpp | 226 ++++++++ ...isibility_test_lib.hpp => so_dll_test.hpp} | 16 +- test/to_variant_test.cpp | 4 +- test/try_capture_all_test.cpp | 4 +- test/try_catch_error_id_test.cpp | 2 +- test/try_catch_system_error_test.cpp | 2 +- test/try_catch_test.cpp | 2 +- test/try_exception_and_result_test.cpp | 2 +- test/visibility_test.cpp | 107 ---- wasm.txt | 2 +- 95 files changed, 2332 insertions(+), 903 deletions(-) create mode 100644 include/boost/leaf/config/tls_win32.hpp create mode 100644 include/boost/leaf/config/visibility.hpp create mode 100644 subprojects/.wraplock create mode 100644 test/on_error_alloc_fail_test.cpp create mode 100644 test/on_error_dynamic_reserve_test1.cpp create mode 100644 test/on_error_dynamic_reserve_test2.cpp create mode 100644 test/on_error_dynamic_reserve_test3.cpp rename test/{visibility_test_lib.cpp => so_dll_lib1.cpp} (72%) create mode 100644 test/so_dll_lib1.hpp create mode 100644 test/so_dll_lib2.cpp create mode 100644 test/so_dll_lib2.hpp create mode 100644 test/so_dll_test.cpp rename test/{visibility_test_lib.hpp => so_dll_test.hpp} (54%) delete mode 100644 test/visibility_test.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8601ed..f466d77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,17 @@ jobs: strategy: fail-fast: false matrix: - include: + config: + - variant: "debug,release" + - name: No capture + variant: "leaf_debug_capture0,leaf_release_capture0" + - name: No diagnostics + variant: "leaf_debug_diag0,leaf_release_diag0" + - name: Embedded + variant: "leaf_debug_embedded,leaf_release_embedded" + - name: Single header + variant: "leaf_debug_single_header,leaf_release_single_header" + platform: - toolset: gcc-5 cxxstd: "11,14,1z" container: ubuntu:18.04 @@ -175,23 +185,25 @@ jobs: container: ubuntu:25.04 os: ubuntu-latest install: clang-20 - - toolset: clang - cxxstd: "11,14,17,20,2b" - os: macos-13 - toolset: clang cxxstd: "11,14,17,20,2b" os: macos-14 - toolset: clang cxxstd: "11,14,17,20,2b" os: macos-15 + - toolset: clang + cxxstd: "11,14,17,20,2b" + os: macos-26 - runs-on: ${{matrix.os}} + name: ${{matrix.platform.os}} / ${{matrix.platform.compiler || matrix.platform.toolset}}${{matrix.config.name && format(' / {0}', matrix.config.name) || ''}} + + runs-on: ${{matrix.platform.os}} container: - image: ${{matrix.container}} + image: ${{matrix.platform.container}} volumes: - /node20217:/node20217:rw,rshared - - ${{ startsWith(matrix.container, 'ubuntu:1') && '/node20217:/__e/node20:ro,rshared' || ' ' }} + - ${{ startsWith(matrix.platform.container, 'ubuntu:1') && '/node20217:/__e/node20:ro,rshared' || ' ' }} defaults: run: @@ -199,13 +211,13 @@ jobs: steps: - name: Setup container environment - if: matrix.container + if: matrix.platform.container run: | apt-get update apt-get -y install sudo python3 git g++ curl xz-utils - name: Install nodejs20glibc2.17 - if: ${{ startsWith( matrix.container, 'ubuntu:1' ) }} + if: ${{ startsWith( matrix.platform.container, 'ubuntu:1' ) }} run: | curl -LO https://archives.boost.io/misc/node/node-v20.9.0-linux-x64-glibc-217.tar.xz tar -xf node-v20.9.0-linux-x64-glibc-217.tar.xz --strip-components 1 -C /node20217 @@ -214,10 +226,10 @@ jobs: - uses: actions/checkout@v4 - name: Install packages - if: matrix.install + if: matrix.platform.install run: | sudo apt-get update - sudo apt-get -y install ${{matrix.install}} + sudo apt-get -y install ${{matrix.platform.install}} - name: Setup Boost run: | @@ -242,9 +254,9 @@ jobs: ./b2 -d0 headers - name: Create user-config.jam - if: matrix.compiler + if: matrix.platform.compiler run: | - echo "using ${{matrix.toolset}} : : ${{matrix.compiler}} ;" > ~/user-config.jam + echo "using ${{matrix.platform.toolset}} : : ${{matrix.platform.compiler}} ;" > ~/user-config.jam - name: Generate headers run: | @@ -254,13 +266,24 @@ jobs: - name: Run tests run: | cd ../boost-root - ./b2 -j3 libs/$LIBRARY/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} ${ADDRMD:+address-model=$ADDRMD} link=shared,static variant=debug,release,leaf_debug_capture0,leaf_release_capture0,leaf_debug_diag0,leaf_release_diag0,leaf_debug_embedded,leaf_release_embedded,leaf_debug_single_header,leaf_release_single_header + ./b2 -j3 libs/$LIBRARY/test toolset=${{matrix.platform.toolset}} cxxstd=${{matrix.platform.cxxstd}} ${ADDRMD:+address-model=$ADDRMD} link=shared,static variant=${{matrix.config.variant}} + ./b2 -j3 libs/$LIBRARY/test toolset=${{matrix.platform.toolset}} cxxstd=${{matrix.platform.cxxstd}} ${ADDRMD:+address-model=$ADDRMD} link=shared,static variant=${{matrix.config.variant}} exception-handling=off rtti=off windows: strategy: fail-fast: false matrix: - include: + config: + - variant: "debug,release" + - name: No capture + variant: "leaf_debug_capture0,leaf_release_capture0" + - name: No diagnostics + variant: "leaf_debug_diag0,leaf_release_diag0" + - name: Embedded + variant: "leaf_debug_embedded,leaf_release_embedded" + - name: Single header + variant: "leaf_debug_single_header,leaf_release_single_header" + platform: - toolset: msvc-14.3 cxxstd: "14,17,20,latest" addrmd: 32,64 @@ -274,7 +297,9 @@ jobs: addrmd: 64 os: windows-2022 - runs-on: ${{matrix.os}} + name: ${{matrix.platform.os}} / ${{matrix.platform.compiler || matrix.platform.toolset}}${{matrix.config.name && format(' / {0}', matrix.config.name) || ''}} + + runs-on: ${{matrix.platform.os}} steps: - uses: actions/checkout@v4 @@ -310,4 +335,5 @@ jobs: shell: cmd run: | cd ../boost-root - b2 -j3 libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} ${{matrix.embedmanifest}} variant=debug,release,leaf_debug_capture0,leaf_release_capture0,leaf_debug_diag0,leaf_release_diag0,leaf_debug_embedded,leaf_release_embedded,leaf_debug_single_header,leaf_release_single_header + b2 -j3 --abbreviate-paths libs/%LIBRARY%/test toolset=${{matrix.platform.toolset}} cxxstd=${{matrix.platform.cxxstd}} address-model=${{matrix.platform.addrmd}} link=shared,static variant=${{matrix.config.variant}} + b2 -j3 --abbreviate-paths libs/%LIBRARY%/test toolset=${{matrix.platform.toolset}} cxxstd=${{matrix.platform.cxxstd}} address-model=${{matrix.platform.addrmd}} link=shared,static variant=${{matrix.config.variant}} exception-handling=off rtti=off diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f94f55c..d366525 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -6,43 +6,43 @@ { "label": "Setup Meson build directories", "type": "shell", - "command": "cd ${workspaceRoot} && meson setup -D leaf_boost_examples=true -D leaf_lua_examples=true _bld/debug && meson setup -D leaf_boost_examples=false -D single_header=true _bld/debug_single_header && meson setup -D leaf_boost_examples=true -D leaf_lua_examples=true _bld/release --buildtype release && meson setup -D single_header=true _bld/release_single_header --buildtype release", + "command": "cd ${workspaceRoot} && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true _bld/debug && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true -D leaf_single_header=true _bld/debug_single_header && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true _bld/release --buildtype release && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true -D leaf_single_header=true _bld/release_single_header --buildtype release", "problemMatcher": [] }, { "label": "Setup Meson build directories (no exceptions)", "type": "shell", - "command": "cd ${workspaceRoot} && meson setup -D cpp_eh=none -D leaf_boost_examples=true -D leaf_lua_examples=true _bld/debug && meson setup -D cpp_eh=none -D leaf_boost_examples=true -D single_header=true _bld/debug_single_header && meson setup -D cpp_eh=none -D leaf_boost_examples=true -D leaf_lua_examples=true _bld/release --buildtype release && meson setup -D cpp_eh=none -D leaf_boost_examples=true -D single_header=true _bld/release_single_header --buildtype release", + "command": "cd ${workspaceRoot} && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true -D cpp_eh=none _bld/debug && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true -D leaf_single_header=true -D cpp_eh=none _bld/debug_single_header && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true -D cpp_eh=none _bld/release --buildtype release && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true -D leaf_single_header=true -D cpp_eh=none _bld/release_single_header --buildtype release", "problemMatcher": [] }, { "label": "Setup Meson build directories (no diagnostics)", "type": "shell", - "command": "cd ${workspaceRoot} && meson setup -D leaf_diagnostics=0 -D leaf_boost_examples=true -D leaf_lua_examples=true _bld/debug && meson setup -D leaf_diagnostics=0 -D leaf_boost_examples=true -D single_header=true _bld/debug_single_header && meson setup -D leaf_diagnostics=0 -D leaf_boost_examples=true -D leaf_lua_examples=true _bld/release --buildtype release && meson setup -D leaf_diagnostics=0 -D leaf_boost_examples=true -D single_header=true _bld/release_single_header --buildtype release", + "command": "cd ${workspaceRoot} && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true -D leaf_diagnostics=0 _bld/debug && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true -D leaf_single_header=true -D leaf_diagnostics=0 _bld/debug_single_header && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true -D leaf_diagnostics=0 _bld/release --buildtype release && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true -D leaf_single_header=true -D leaf_diagnostics=0 _bld/release_single_header --buildtype release", "problemMatcher": [] }, { "label": "Setup Meson build directories (no capture)", "type": "shell", - "command": "cd ${workspaceRoot} && meson setup -D leaf_capture=0 -D leaf_boost_examples=true -D leaf_lua_examples=true _bld/debug && meson setup -D leaf_capture=0 -D leaf_boost_examples=true -D single_header=true _bld/debug_single_header && meson setup -D leaf_capture=0 -D leaf_boost_examples=true -D leaf_lua_examples=true _bld/release --buildtype release && meson setup -D leaf_capture=0 -D leaf_boost_examples=true -D single_header=true _bld/release_single_header --buildtype release", + "command": "cd ${workspaceRoot} && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true -D leaf_capture=0 _bld/debug && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true -D leaf_single_header=true -D leaf_capture=0 _bld/debug_single_header && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true -D leaf_capture=0 _bld/release --buildtype release && meson setup -D leaf_boost_available=true -D leaf_enable_examples=true -D leaf_single_header=true -D leaf_capture=0 _bld/release_single_header --buildtype release", "problemMatcher": [] }, { "label": "Setup Meson build directories (no Boost)", "type": "shell", - "command": "cd ${workspaceRoot} && meson setup -D leaf_lua_examples=true _bld/debug && meson setup -D single_header=true _bld/debug_single_header && meson setup -D leaf_lua_examples=true _bld/release --buildtype release && meson setup -D single_header=true _bld/release_single_header --buildtype release", + "command": "cd ${workspaceRoot} && meson setup -D leaf_boost_available=false -D leaf_enable_examples=true _bld/debug && meson setup -D leaf_boost_available=false -D leaf_enable_examples=true -D leaf_single_header=true _bld/debug_single_header && meson setup -D leaf_boost_available=false -D leaf_enable_examples=true _bld/release --buildtype release && meson setup -D leaf_boost_available=false -D leaf_enable_examples=true -D leaf_single_header=true _bld/release_single_header --buildtype release", "problemMatcher": [] }, { "label": "Setup Meson build directories (test embedded)", "type": "shell", - "command": "cd ${workspaceRoot} && meson setup -D leaf_embedded=true -D leaf_diagnostics=0 _bld/debug && meson setup -D leaf_embedded=true -D leaf_diagnostics=0 -D single_header=true _bld/debug_single_header && meson setup -D leaf_embedded=true -D leaf_diagnostics=0 _bld/release --buildtype release && meson setup -D leaf_embedded=true -D leaf_diagnostics=0 -D single_header=true _bld/release_single_header --buildtype release", + "command": "cd ${workspaceRoot} && meson setup -D leaf_embedded=true -D leaf_diagnostics=0 _bld/debug && meson setup -D leaf_embedded=true -D leaf_diagnostics=0 -D leaf_single_header=true _bld/debug_single_header && meson setup -D leaf_embedded=true -D leaf_diagnostics=0 _bld/release --buildtype release && meson setup -D leaf_embedded=true -D leaf_diagnostics=0 -D leaf_single_header=true _bld/release_single_header --buildtype release", "problemMatcher": [] }, { "label": "Setup Meson build directories (test embedded, no exceptions)", "type": "shell", - "command": "cd ${workspaceRoot} && meson setup -D cpp_eh=none -D leaf_embedded=true -D leaf_diagnostics=0 _bld/debug && meson setup -D cpp_eh=none -D leaf_embedded=true -D leaf_diagnostics=0 -D single_header=true _bld/debug_single_header && meson setup -D cpp_eh=none -D leaf_embedded=true -D leaf_diagnostics=0 _bld/release --buildtype release && meson setup -D cpp_eh=none -D leaf_embedded=true -D leaf_diagnostics=0 -D single_header=true _bld/release_single_header --buildtype release", + "command": "cd ${workspaceRoot} && meson setup -D cpp_eh=none -D leaf_embedded=true -D leaf_diagnostics=0 _bld/debug && meson setup -D cpp_eh=none -D leaf_embedded=true -D leaf_diagnostics=0 -D leaf_single_header=true _bld/debug_single_header && meson setup -D cpp_eh=none -D leaf_embedded=true -D leaf_diagnostics=0 _bld/release --buildtype release && meson setup -D cpp_eh=none -D leaf_embedded=true -D leaf_diagnostics=0 -D leaf_single_header=true _bld/release_single_header --buildtype release", "problemMatcher": [] }, { @@ -65,6 +65,15 @@ "relative", "${workspaceRoot}/_bld/debug" ] + }, + "windows": { + "problemMatcher": { + "base": "$msCompile", + "fileLocation": [ + "relative", + "${workspaceRoot}/_bld/debug" + ] + } } }, { @@ -81,6 +90,15 @@ "relative", "${workspaceRoot}/_bld/debug" ] + }, + "windows": { + "problemMatcher": { + "base": "$msCompile", + "fileLocation": [ + "relative", + "${workspaceRoot}/_bld/debug" + ] + } } }, { @@ -97,6 +115,15 @@ "relative", "${workspaceRoot}/_bld/release" ] + }, + "windows": { + "problemMatcher": { + "base": "$msCompile", + "fileLocation": [ + "relative", + "${workspaceRoot}/_bld/release" + ] + } } }, { @@ -106,16 +133,23 @@ "dependsOn": [ "Generate leaf.hpp" ], - "command": "../../b2 test link=shared,static variant=debug,release,leaf_debug_diag0,leaf_release_diag0,leaf_debug_single_header,leaf_release_single_header,leaf_debug_embedded,leaf_release_embedded exception-handling=off rtti=off cxxstd=11,14,1z,17 && ../../b2 test link=shared,static variant=debug,release,leaf_debug_diag0,leaf_release_diag0,leaf_debug_single_header,leaf_release_single_header exception-handling=on,off cxxstd=11,14,1z,17", - "windows": { - "command": "..\\..\\b2 test link=shared,static variant=debug,release,leaf_debug_diag0,leaf_release_diag0,leaf_debug_single_header,leaf_release_single_header,leaf_debug_embedded,leaf_release_embedded exception-handling=off rtti=off cxxstd=14,17,latest && ..\\..\\b2 test link=shared,static variant=debug,release,leaf_debug_diag0,leaf_release_diag0,leaf_debug_single_header,leaf_release_single_header exception-handling=on,off cxxstd=14,17,latest", - }, + "command": "../../b2 --abbreviate-paths test link=shared,static variant=debug,release,leaf_debug_diag0,leaf_release_diag0,leaf_debug_single_header,leaf_release_single_header,leaf_debug_embedded,leaf_release_embedded exception-handling=off rtti=off cxxstd=11,14,1z,17 && ../../b2 --abbreviate-paths test link=shared,static variant=debug,release,leaf_debug_diag0,leaf_release_diag0,leaf_debug_single_header,leaf_release_single_header exception-handling=on,off cxxstd=11,14,1z,17", "problemMatcher": { "base": "$gcc", "fileLocation": [ "relative", "${workspaceRoot}/_bld/release" ] + }, + "windows": { + "command": "..\\..\\b2 --abbreviate-paths test link=shared,static variant=debug,release,leaf_debug_diag0,leaf_release_diag0,leaf_debug_single_header,leaf_release_single_header,leaf_debug_embedded,leaf_release_embedded exception-handling=off rtti=off cxxstd=14,17,latest && ..\\..\\b2 --abbreviate-paths test link=shared,static variant=debug,release,leaf_debug_diag0,leaf_release_diag0,leaf_debug_single_header,leaf_release_single_header exception-handling=on,off cxxstd=14,17,latest", + "problemMatcher": { + "base": "$msCompile", + "fileLocation": [ + "relative", + "${workspaceRoot}/_bld/release" + ] + } } }, { @@ -125,9 +159,9 @@ }, "label": "Test current editor file", "type": "shell", - "command": "cd ${workspaceRoot}/_bld/debug && ninja && {meson test ${fileBasenameNoExtension} || cat ./meson-logs/testlog.txt}", + "command": "cd ${workspaceRoot}/_bld/debug && ninja && { meson test ${fileBasenameNoExtension} || cat ./meson-logs/testlog.txt; }",, "windows": { - "command": "cd ${workspaceRoot}/_bld/debug && ninja && (meson test ${fileBasenameNoExtension} || cat ./meson-logs/testlog.txt)", + "command": "cd ${workspaceRoot}/_bld/debug && ninja && (meson test ${fileBasenameNoExtension} || type .\\meson-logs\\testlog.txt)", }, "problemMatcher": { "base": "$gcc", @@ -135,6 +169,16 @@ "relative", "${workspaceRoot}/_bld/debug" ] + }, + "windows": { + "command": "cd ${workspaceRoot}/_bld/debug && ninja && (meson test ${fileBasenameNoExtension} || type .\\meson-logs\\testlog.txt)", + "problemMatcher": { + "base": "$msCompile", + "fileLocation": [ + "relative", + "${workspaceRoot}/_bld/debug" + ] + } } } ] diff --git a/README.md b/README.md index c204a98..53ca5f8 100644 --- a/README.md +++ b/README.md @@ -28,4 +28,4 @@ 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: [leaf.hpp](https://raw.githubusercontent.com/boostorg/leaf/gh-pages/leaf.hpp). -Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. Distributed under the http://www.boost.org/LICENSE_1_0.txt[Boost Software License, Version 1.0]. +Copyright 2018-2025 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/doc/leaf.adoc b/doc/leaf.adoc index cbb7f33..c2391e3 100644 --- a/doc/leaf.adoc +++ b/doc/leaf.adoc @@ -68,19 +68,15 @@ NOTE: LEAF does not depend on Boost or other libraries. [[tutorial]] == Tutorial -What is a failure? It is simply the inability of a function to return a valid result, instead producing an error object describing the reason for the failure. +Typically, error handling libraries define a variant result type, e.g. `result`. In LEAF we drop the `E`, using just `result`. -A typical design is to return a variant type, e.g. `result`. Internally, such variant types must store a discriminant (in this case a boolean) to indicate whether the object holds a `T` or an `E`. +In case of success, access to the value `T` is immediate and direct, but because error objects are not held in results, handling errors requires a special syntax. Error objects are transported directly to error handling scopes that need them. This is not only more efficient, but also enables any given handler to access multiple objects associated with the same failure, if needed. -The design of LEAF is informed by the observation that the immediate caller must have access to the discriminant in order to determine the availability of a valid `T`, but otherwise it is rare that it needs to access any error objects. They are only needed once an error handling scope is reached. - -Therefore what would have been a `result` becomes `result`, which stores the discriminant and (optionally) a `T`, while error objects are delivered directly to the error handling scope where they are needed. - -The benefit of this decomposition is that `result` becomes extremely lightweight, as it is not coupled with error types; further, error objects are communicated in constant time (independent of the call stack depth). Even very large objects are handled efficiently without dynamic memory allocation. +LEAF is also compatible with exception handling, providing identical functionality but without needing a result type. === Reporting Errors -A function that reports an error: +Report errors with `leaf::new_error`: [source,c++] ---- @@ -103,7 +99,7 @@ leaf::result f() [[checking_for_errors]] === Checking for Errors -Checking for errors communicated by a `leaf::result` works as expected: +To bail out on failure, return `result::error()`: [source,c++] ---- @@ -120,9 +116,7 @@ leaf::result g() [.text-right] <> -TIP: The the result of `r.error()` is compatible with any instance of the `leaf::result` template. In the example above, note that `g` returns a `leaf::result`, while `r` is of type `leaf::result`. - -The boilerplate `if` statement can be avoided using `BOOST_LEAF_AUTO`: +Use `BOOST_LEAF_AUTO` to avoid the boilerplate `if` statement: [source,c++] ---- @@ -136,7 +130,7 @@ leaf::result g() [.text-right] <> -`BOOST_LEAF_AUTO` can not be used with `void` results; in that case, to avoid the boilerplate `if` statement, use `BOOST_LEAF_CHECK`: +Use `BOOST_LEAF_CHECK` in case of `void` results: [source,c++] ---- @@ -151,7 +145,7 @@ leaf::result g() [.text-right] <> -On implementations that define `pass:[__GNUC__]` (e.g. GCC/clang), the `BOOST_LEAF_CHECK` macro definition takes advantage of https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html[GNU C statement expressions]. In this case, in addition to its portable usage with `result`, `BOOST_LEAF_CHECK` can be used in expressions with non-`void` result types: +On implementations that define `pass:[__GNUC__]` (e.g. GCC/clang), `BOOST_LEAF_CHECK` is compatible with with non-`void` results as well: [source,c++] ---- @@ -221,7 +215,7 @@ leaf::result r = leaf::try_handle_some( BOOST_LEAF_AUTO(v1, f1()); BOOST_LEAF_AUTO(v2, f2()); - return g(v1. v2); + return g(v1, v2); }, []( leaf::match ) -> leaf::result @@ -256,7 +250,7 @@ leaf::result r = leaf::try_handle_some( BOOST_LEAF_AUTO(v1, f1()); BOOST_LEAF_AUTO(v2, f2()); - return g(v1. v2); + return g(v1, v2); }, []( err1 e, leaf::error_info const & ei ) -> leaf::result @@ -285,7 +279,7 @@ U r = leaf::try_handle_all( BOOST_LEAF_AUTO(v1, f1()); BOOST_LEAF_AUTO(v2, f2()); - return g(v1. v2); + return g(v1, v2); }, []( leaf::match ) -> U @@ -400,9 +394,9 @@ leaf::result r = leaf::try_handle_some( Once again, error handlers are considered in order: -* The first error handler will be used if an error object of type `io_error` _and_ and error_object of type `e_file_name` are available; +* The first error handler will be used if an error object of type `io_error` _and_ an error_object of type `e_file_name` are available; * otherwise, the second error handler will be used if an error object of type `io_error` is available; -* otherwise, `leaf_try_handle_some` fails. +* otherwise, `leaf::try_handle_some` fails. An alternative way to write the above is to provide a single error handler that takes the `e_file_name` argument as a pointer: @@ -515,7 +509,7 @@ leaf::result r = leaf::try_handle_some( []( io_error e, e_line const * current_line ) { - std::cerr << "Parse error"; + std::cerr << "I/O error"; if( current_line ) std::cerr << " at line " << current_line->value; std::cerr << std::endl; @@ -550,7 +544,7 @@ leaf::result r = leaf::try_handle_some( []( io_error e, e_line const * l ) { - std::cerr << "Parse error"; + std::cerr << "I/O error"; if( l ) std::cerr << " at line " << l.value; std::cerr << std::endl; @@ -584,7 +578,7 @@ leaf::try_catch( []( io_error e, e_line const * l ) { - std::cerr << "Parse error"; + std::cerr << "I/O error"; if( l ) std::cerr << " at line " << l.value; std::cerr << std::endl; @@ -718,7 +712,6 @@ leaf::result r = leaf::try_handle_some( { // Handle lib2::error_code } ); -} ---- [.text-right] <> | <> @@ -803,7 +796,7 @@ Ideally, when an error is detected, a program using LEAF would always call <>. [[tutorial-loading]] === Loading of Error Objects -Recall that error objects communicated to LEAF are stored on the stack, local to the `try_handle_same`, `try_handle_all` or `try_catch` function used to handle errors. To _load_ an error object means to move it into such storage, if available. +Recall that error objects communicated to LEAF are stored on the stack, local to the `try_handle_some`, `try_handle_all` or `try_catch` function used to handle errors. To _load_ an error object means to move it into such storage, if available. Various LEAF functions take a list of error objects to load. As an example, if a function `copy_file` that takes the name of the input file and the name of the output file as its arguments detects a failure, it could communicate an error code `ec`, plus the two relevant file names using <>: @@ -1098,7 +1091,7 @@ It is also possible to select a handler based on `std::error_category`. The foll [source,c++] ---- -[]( std::error_code, leaf::category> ) +[]( std::error_code, leaf::category ) { } ---- @@ -1109,7 +1102,7 @@ The following predicates are available: * <>: as described above. * <>: where `match` compares the object `e` of type `E` with the values `V...`, `match_value` compare `e.value` with the values `V...`. -* <>: similar to `match_value`, but takes a pointer to the data member to compare; that is, `match_member<&E::value, V...>` is equvialent to `match_value`. Note, however, that `match_member` requires {CPP}17 or newer, while `match_value` does not. +* <>: similar to `match_value`, but takes a pointer to the data member to compare; that is, `match_member<&E::value, V...>` is equivalent to `match_value`. Note, however, that `match_member` requires {CPP}17 or newer, while `match_value` does not. * `<>`: Similar to `match`, but checks whether the caught `std::exception` object can be `dynamic_cast` to any of the `Ex` types. * <> is a special predicate that takes any other predicate `Pred` and requires that an error object of type `E` is available and that `Pred` evaluates to `false`. For example, `if_not>` requires that an object `e` of type `E` is available, and that it does not compare equal to any of the specified `V...`. @@ -1778,7 +1771,7 @@ NOTE: Follow this link to see the complete program: https://github.com/boostorg/ TIP: When using Lua with {CPP}, we need to protect the Lua interpreter from exceptions that may be thrown from {CPP} functions installed as `lua_CFunction` callbacks. Here is the program from this section rewritten to use a {CPP} exception (instead of `leaf::result`) to safely communicate errors out of the `do_work` function: https://github.com/boostorg/leaf/blob/master/example/lua_callback_exceptions.cpp?ts=4[lua_callback_exceptions.cpp]. -'''' +''' [[tutorial-diagnostic_information]] === Diagnostic Information @@ -5543,7 +5536,10 @@ The following configuration macros are recognized: * `BOOST_LEAF_CFG_GNUC_STMTEXPR`: This macro controls whether or not <> is defined in terms of a https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html[GNU C statement expression], which enables its use to check for errors similarly to how the questionmark operator works in some languages (see <>). By default the macro is defined as `1` under `pass:[__GNUC__]`, otherwise as `0`. -* `BOOST_LEAF_CFG_WIN32`: Defining this macro as 1 enables the default constructor in <>, and the automatic conversion to string (via `FormatMessageA`) when <> is printed. If the macro is left undefined, LEAF defines it as `0` (even on windows, since including `windows.h` is generally not desirable). Note that the `e_LastError` type itself is available on all platforms, there is no need for conditional compilation in error handlers that use it. +* `BOOST_LEAF_CFG_WIN32`: This macro controls the use of Win32 APIs. If left undefined, LEAF defines it as `0` (even on Windows, since including `windows.h` is generally not desirable). The possible values are: +** `0`: Disables all Win32-specific features. +** `1`: Includes `windows.h` and enables <> support, which is otherwise stubbed. +** `2`: In addition, switches LEAF to using the Win32 TLS API instead of {CPP}11 `thread_local`, enabling error objects to be used across DLL boundaries. * `BOOST_LEAF_NO_EXCEPTIONS`: Disables all exception handling support. If left undefined, LEAF defines it automatically based on the compiler configuration (e.g. `-fno-exceptions`). @@ -5552,7 +5548,13 @@ The following configuration macros are recognized: [[configuring_tls_access]] === Configuring TLS Access -LEAF requires support for thread-local `void` pointers. By default, this is implemented by means of the {CPP}11 `thread_local` keyword, but in order to support <>, it is possible to configure LEAF to use an array of thread local pointers instead, by defining `BOOST_LEAF_USE_TLS_ARRAY`. In this case, the user is required to define the following two functions to implement the required TLS access: +LEAF requires support for thread-local `void` pointers. The available TLS implementations are: + +** {CPP}11 `thread_local` keyword (the default). +** Win32 TLS API: selected by defining `BOOST_LEAF_CFG_WIN32=2`. This enables error objects to be used across DLL boundaries. +** Custom TLS array: selected by defining `BOOST_LEAF_USE_TLS_ARRAY`. This is intended for <> where the {CPP}11 `thread_local` keyword is not available or not suitable. + +When using `BOOST_LEAF_USE_TLS_ARRAY`, the user is required to define the following two functions to implement the required TLS access: [source,c++] ---- @@ -5577,7 +5579,7 @@ Under `BOOST_LEAF_USE_TLS_ARRAY` the following additional configuration macros a * `BOOST_LEAF_CFG_TLS_INDEX_TYPE` may be defined to specify the integral type used to store assigned TLS indices (if the macro is left undefined, LEAF defines it as `unsigned char`). -TIP: Reporting error objects of types that are not used by the program to handle failures does not consume TLS pointers. The minimum size of the TLS pointer array required by LEAF is the total number of different types used as arguments to error handlers (in the entire program), plus one. +TIP: TLS slots are allocated only for types used in error handlers. The minimum size of the TLS pointer array required by LEAF is the total number of different types used as arguments to error handlers (in the entire program), plus one. WARNING: Beware of `read_void_ptr`/`write_void_ptr` accessing thread local pointers beyond the static boundaries of the thread local pointer array; this will likely result in undefined behavior. @@ -5605,9 +5607,9 @@ Defining `BOOST_LEAF_EMBEDDED` is equivalent to the following: #endif ---- -LEAF supports FreeRTOS out of the box, please define `BOOST_LEAF_TLS_FREERTOS` (in which case LEAF automatically defines `BOOST_LEAF_EMBEDDED`, if it is not defined already). +LEAF supports FreeRTOS out of the box, define `BOOST_LEAF_TLS_FREERTOS` (in which case LEAF automatically defines `BOOST_LEAF_EMBEDDED`, if it is not defined already). -For other embedded platforms, please define `BOOST_LEAF_USE_TLS_ARRAY`, see <>. +For other embedded platforms, define `BOOST_LEAF_USE_TLS_ARRAY`, see <>. If your program does not use concurrency at all, simply define `BOOST_LEAF_NO_THREADS`, which requires no TLS support at all (but is NOT thread-safe). @@ -5618,8 +5620,6 @@ TIP: Contrary to popular belief, exception handling works great on embedded plat The source code is compatible with {CPP}11 or newer. -LEAF uses thread-local storage (only for pointers). By default, this is implemented via the {CPP}11 `thread_local` storage class specifier, but the library is easily configurable to use any platform-specific TLS API instead (it ships with built-in support for FreeRTOS). See <>. - == Running the Unit Tests The unit tests can be run with https://mesonbuild.com[Meson Build] or with Boost Build. To run the unit tests: @@ -5792,9 +5792,9 @@ return leaf::try_handle_some( [.text-right] <> | <> | <> | <> | <> -== Limitations +== Dynamic linking -When using dynamic linking, it is required that error types are declared with `default` visibility, e.g.: +When using dynamic linking, on POSIX platforms it is required that error types are declared with `default` visibility, e.g.: [source,c++] ---- @@ -5804,9 +5804,7 @@ struct __attribute__ ((visibility ("default"))) my_error_info }; ---- -This works as expected except on Windows, where thread-local storage is not shared between the individual binary modules. For this reason, to transport error objects across DLL boundaries. - -TIP: When using dynamic linking, it is always best to define module interfaces in terms of C (and implement them in {CPP} if appropriate). +On Windows, to use error objects across DLL boundaries, define `BOOST_LEAF_CFG_WIN32=2`. For two modules to be able to share LEAF error objects, it is required that they are both compiled with the same compiler (not necessarily with the same compiler version). For example, if one module is compiled with MSVC and the other with GCC, the error objects will not be shared. == Alternatives to LEAF @@ -5929,7 +5927,7 @@ leaf::try_catch( [] { f(); // throws - } + }, [](my_exception &, my_info const & x) { //my_info is available with diff --git a/example/error_log.cpp b/example/error_log.cpp index ea19e8c..b64e8aa 100644 --- a/example/error_log.cpp +++ b/example/error_log.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) diff --git a/example/error_trace.cpp b/example/error_trace.cpp index c3299b9..0bba6ba 100644 --- a/example/error_trace.cpp +++ b/example/error_trace.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) diff --git a/example/exception_to_result.cpp b/example/exception_to_result.cpp index d6e3a7b..16c18ef 100644 --- a/example/exception_to_result.cpp +++ b/example/exception_to_result.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) diff --git a/example/lua_callback_exceptions.cpp b/example/lua_callback_exceptions.cpp index ecad96f..fae54a2 100644 --- a/example/lua_callback_exceptions.cpp +++ b/example/lua_callback_exceptions.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) diff --git a/example/lua_callback_result.cpp b/example/lua_callback_result.cpp index fc7a046..db4d510 100644 --- a/example/lua_callback_result.cpp +++ b/example/lua_callback_result.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) diff --git a/example/print_file/print_file_exceptions.cpp b/example/print_file/print_file_exceptions.cpp index adf54f7..e235153 100644 --- a/example/print_file/print_file_exceptions.cpp +++ b/example/print_file/print_file_exceptions.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) diff --git a/example/print_file/print_file_leaf_result.cpp b/example/print_file/print_file_leaf_result.cpp index a32ec54..4c67069 100644 --- a/example/print_file/print_file_leaf_result.cpp +++ b/example/print_file/print_file_leaf_result.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) diff --git a/example/print_file/print_file_system_result.cpp b/example/print_file/print_file_system_result.cpp index b5addfb..468e115 100644 --- a/example/print_file/print_file_system_result.cpp +++ b/example/print_file/print_file_system_result.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) diff --git a/example/print_half.cpp b/example/print_half.cpp index 1211d27..71bda06 100644 --- a/example/print_half.cpp +++ b/example/print_half.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) diff --git a/example/try_capture_all_exceptions.cpp b/example/try_capture_all_exceptions.cpp index 4372482..91a8c6f 100644 --- a/example/try_capture_all_exceptions.cpp +++ b/example/try_capture_all_exceptions.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) diff --git a/example/try_capture_all_result.cpp b/example/try_capture_all_result.cpp index 617f77d..d2d17b8 100644 --- a/example/try_capture_all_result.cpp +++ b/example/try_capture_all_result.cpp @@ -1,4 +1,4 @@ -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) diff --git a/gen/generate_single_header.py b/gen/generate_single_header.py index ea190af..5eed44e 100644 --- a/gen/generate_single_header.py +++ b/gen/generate_single_header.py @@ -1,6 +1,6 @@ """ - Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. + Copyright 2018-2025 Emil Dotchevski and Reverge Studios, Inc. Copyright (c) Sorin Fetche Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -105,7 +105,7 @@ def _main(): '\n' '// Latest published version of this file: https://raw.githubusercontent.com/boostorg/leaf/gh-pages/leaf.hpp.\n' '\n' - '// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc.\n' + '// Copyright 2018-2025 Emil Dotchevski and Reverge Studios, Inc.\n' '// Distributed under the Boost Software License, Version 1.0. (See accompanying\n' '// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n' '\n') diff --git a/include/boost/leaf.hpp b/include/boost/leaf.hpp index 8c2b181..84aa7be 100644 --- a/include/boost/leaf.hpp +++ b/include/boost/leaf.hpp @@ -1,10 +1,10 @@ #ifndef BOOST_LEAF_HPP_INCLUDED #define BOOST_LEAF_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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 -#endif +#endif // #ifndef BOOST_LEAF_HPP_INCLUDED diff --git a/include/boost/leaf/common.hpp b/include/boost/leaf/common.hpp index 05ec987..4eac22e 100644 --- a/include/boost/leaf/common.hpp +++ b/include/boost/leaf/common.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_LEAF_COMMON_HPP_INCLUDED #define BOOST_LEAF_COMMON_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) @@ -18,7 +18,6 @@ #if BOOST_LEAF_CFG_WIN32 # include -# include # ifdef min # undef min # endif @@ -29,18 +28,18 @@ namespace boost { namespace leaf { -struct BOOST_LEAF_SYMBOL_VISIBLE e_api_function { char const * value; }; +struct e_api_function { char const * value; }; #if BOOST_LEAF_CFG_STD_STRING -struct BOOST_LEAF_SYMBOL_VISIBLE e_file_name +struct e_file_name { std::string value; }; #else -struct BOOST_LEAF_SYMBOL_VISIBLE e_file_name +struct e_file_name { char const * value = ""; BOOST_LEAF_CONSTEXPR explicit e_file_name( char const * ) { } @@ -48,7 +47,7 @@ struct BOOST_LEAF_SYMBOL_VISIBLE e_file_name #endif -struct BOOST_LEAF_SYMBOL_VISIBLE e_errno +struct e_errno { int value; @@ -61,9 +60,9 @@ struct BOOST_LEAF_SYMBOL_VISIBLE e_errno } }; -struct BOOST_LEAF_SYMBOL_VISIBLE e_type_info_name { char const * value; }; +struct e_type_info_name { char const * value; }; -struct BOOST_LEAF_SYMBOL_VISIBLE e_at_line { int value; }; +struct e_at_line { int value; }; namespace windows { @@ -97,18 +96,18 @@ namespace windows { BOOST_LEAF_ASSERT(mb.p != nullptr); char * z = std::strchr((LPSTR)mb.p,0); - if( z[-1] == '\n' ) + if( z != (LPSTR)mb.p && z[-1] == '\n' ) *--z = 0; - if( z[-1] == '\r' ) + if( z != (LPSTR)mb.p && z[-1] == '\r' ) *--z = 0; return os << err.value << ", \"" << (LPCSTR)mb.p << '"'; } return os; } -#endif +#endif // #if BOOST_LEAF_CFG_WIN32 }; -} +} // namespace windows -} } +} } // namespace boost::leaf -#endif // BOOST_LEAF_COMMON_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_COMMON_HPP_INCLUDED diff --git a/include/boost/leaf/config.hpp b/include/boost/leaf/config.hpp index a744c10..87a4e12 100644 --- a/include/boost/leaf/config.hpp +++ b/include/boost/leaf/config.hpp @@ -1,10 +1,13 @@ #ifndef BOOST_LEAF_CONFIG_HPP_INCLUDED #define BOOST_LEAF_CONFIG_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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 + #ifdef BOOST_LEAF_TLS_FREERTOS # ifndef BOOST_LEAF_EMBEDDED # define BOOST_LEAF_EMBEDDED @@ -24,7 +27,7 @@ # ifndef BOOST_LEAF_CFG_CAPTURE # define BOOST_LEAF_CFG_CAPTURE 0 # endif -#endif +#endif // #ifdef BOOST_LEAF_EMBEDDED //////////////////////////////////////// @@ -87,8 +90,13 @@ # error BOOST_LEAF_CFG_CAPTURE must be 0 or 1. #endif -#if BOOST_LEAF_CFG_WIN32 != 0 && BOOST_LEAF_CFG_WIN32 != 1 -# error BOOST_LEAF_CFG_WIN32 must be 0 or 1. +#if BOOST_LEAF_CFG_WIN32 != 0 && BOOST_LEAF_CFG_WIN32 != 1 && BOOST_LEAF_CFG_WIN32 != 2 +# error BOOST_LEAF_CFG_WIN32 must be 0 or 1 or 2. +#endif + +#if BOOST_LEAF_CFG_WIN32 && !defined(_WIN32) +# warning "Ignoring BOOST_LEAF_CFG_WIN32 because _WIN32 is not defined" +# define BOOST_LEAF_CFG_WIN32 0 #endif #if BOOST_LEAF_CFG_GNUC_STMTEXPR != 0 && BOOST_LEAF_CFG_GNUC_STMTEXPR != 1 @@ -154,7 +162,7 @@ # endif # endif -#endif +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS //////////////////////////////////////// @@ -203,7 +211,6 @@ //////////////////////////////////////// #ifndef BOOST_LEAF_NO_EXCEPTIONS -# include # if (defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L) || (defined(_MSC_VER) && _MSC_VER >= 1900) # define BOOST_LEAF_STD_UNCAUGHT_EXCEPTIONS 1 # else @@ -219,6 +226,8 @@ # define BOOST_LEAF_SYMBOL_VISIBLE #endif +#include + //////////////////////////////////////// #if defined(__GNUC__) && !(defined(__clang__) || defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) && (__GNUC__ * 100 + __GNUC_MINOR__) < 409 @@ -229,7 +238,36 @@ //////////////////////////////////////// +#ifdef _MSC_VER +# define BOOST_LEAF_UNREACHABLE __assume(0) +#else +# define BOOST_LEAF_UNREACHABLE __builtin_unreachable() +#endif + +//////////////////////////////////////// + +namespace boost +{ + [[noreturn]] void throw_exception( std::exception const & ); // user defined +} + +namespace boost { namespace leaf { + +template +[[noreturn]] void throw_exception_( T && e ) +{ +#ifdef BOOST_LEAF_NO_EXCEPTIONS + ::boost::throw_exception(std::move(e)); +#else + throw std::move(e); +#endif +} + +} } + +//////////////////////////////////////// + // Configure TLS access #include -#endif // BOOST_LEAF_CONFIG_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_CONFIG_HPP_INCLUDED diff --git a/include/boost/leaf/config/tls.hpp b/include/boost/leaf/config/tls.hpp index 0dd4248..a495383 100644 --- a/include/boost/leaf/config/tls.hpp +++ b/include/boost/leaf/config/tls.hpp @@ -1,12 +1,68 @@ #ifndef BOOST_LEAF_CONFIG_TLS_HPP_INCLUDED #define BOOST_LEAF_CONFIG_TLS_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) +namespace boost { namespace leaf { + +// The following declarations specify the thread local storage API used +// internally by LEAF. To port LEAF to a new TLS API, provide definitions for +// each of these functions. +namespace tls +{ + // Generate the next unique error_id. Values start at 1 and increment by 4. + // Error ids must be unique for the lifetime of the process, and this + // function must be thread-safe. Postcondition: (id & 3) == 1 && id != 0. + // + // This function may not fail. + unsigned generate_next_error_id() noexcept; + + // Write x to the TLS for the current error_id. The initial value for each + // thread must be 0. Precondition: x == 0 or (x & 3) == 1. + // + // This function may not fail. + void write_current_error_id( unsigned x ) noexcept; + + // Read the current error_id for this thread. The initial value for each + // thread must be 0. + // + // This function may not fail. + unsigned read_current_error_id() noexcept; + + // Reserve TLS storage for T. The TLS may be allocated dynamically on the + // first call to reserve_ptr, but subsequent calls must reuse the same + // TLS. On platforms where allocation is not needed, this function is + // still defined but does nothing. + // + // This function may throw on allocation failure. + template + void reserve_ptr(); + + // Write p to the TLS previously reserved for T by a call to reserve_ptr. + // It is illegal to call write_ptr without a prior successful call to + // reserve_ptr. + // + // This function may not fail. + template + void write_ptr( T * p ) noexcept; + + // Read the T * value previously written in the TLS for T. Returns nullptr + // if TLS for T has not yet been reserved. + // + // This function may not fail. + template + T * read_ptr() noexcept; +} // namespace tls + +} } // namespace boost::leaf + #if defined(BOOST_LEAF_TLS_FREERTOS) # include +# ifndef BOOST_LEAF_USE_TLS_ARRAY +# define BOOST_LEAF_USE_TLS_ARRAY +# endif #endif #ifndef BOOST_LEAF_USE_TLS_ARRAY @@ -23,10 +79,12 @@ #if defined BOOST_LEAF_USE_TLS_ARRAY # include +#elif BOOST_LEAF_CFG_WIN32 == 2 +# include #elif defined(BOOST_LEAF_NO_THREADS) # include #else # include #endif -#endif // BOOST_LEAF_CONFIG_TLS_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_CONFIG_TLS_HPP_INCLUDED \ No newline at end of file diff --git a/include/boost/leaf/config/tls_array.hpp b/include/boost/leaf/config/tls_array.hpp index 456e50f..a595022 100644 --- a/include/boost/leaf/config/tls_array.hpp +++ b/include/boost/leaf/config/tls_array.hpp @@ -1,16 +1,15 @@ #ifndef BOOST_LEAF_CONFIG_TLS_ARRAY_HPP_INCLUDED #define BOOST_LEAF_CONFIG_TLS_ARRAY_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) // Copyright (c) 2022 Khalil Estell -// LEAF requires thread local storage support for pointers and for uin32_t values. - -// This header implements thread local storage for pointers and for unsigned int -// values for platforms that support thread local pointers by index. +// This header implements the TLS API specified in tls.hpp for platforms that +// provide TLS by indexing an array (this is typical for embedded platforms). +// The array is accessed via user-defined functions. namespace boost { namespace leaf { @@ -26,8 +25,8 @@ namespace tls //////////////////////////////////////// -#include #include +#include #include #include @@ -56,19 +55,25 @@ namespace boost { namespace leaf { namespace detail { using atomic_unsigned_int = std::atomic; -} -namespace tls -{ + template + struct BOOST_LEAF_SYMBOL_VISIBLE id_factory + { + static atomic_unsigned_int counter; + }; + + template + atomic_unsigned_int id_factory::counter(1); + template class BOOST_LEAF_SYMBOL_VISIBLE index_counter { static int c_; - static BOOST_LEAF_CFG_TLS_INDEX_TYPE next_() noexcept + BOOST_LEAF_ALWAYS_INLINE static BOOST_LEAF_CFG_TLS_INDEX_TYPE next_() noexcept { int idx = ++c_; - BOOST_LEAF_ASSERT(idx > (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX)); + BOOST_LEAF_ASSERT(idx > (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX + 1)); BOOST_LEAF_ASSERT(idx < (BOOST_LEAF_CFG_TLS_ARRAY_SIZE)); return idx; } @@ -76,12 +81,15 @@ namespace tls public: template - static BOOST_LEAF_CFG_TLS_INDEX_TYPE next() noexcept + BOOST_LEAF_ALWAYS_INLINE static BOOST_LEAF_CFG_TLS_INDEX_TYPE next() noexcept { - return next_(); // Set breakpoint here to monitor TLS index allocation for T. + return next_(); } }; + template + int index_counter::c_ = BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX + 1; + template struct BOOST_LEAF_SYMBOL_VISIBLE tls_index { @@ -89,58 +97,72 @@ namespace tls }; template - struct BOOST_LEAF_SYMBOL_VISIBLE alloc_tls_index + BOOST_LEAF_CFG_TLS_INDEX_TYPE tls_index::idx = BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX + 1; + + template + struct BOOST_LEAF_SYMBOL_VISIBLE reserve_tls_index { static BOOST_LEAF_CFG_TLS_INDEX_TYPE const idx; }; template - int index_counter::c_ = BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX; + BOOST_LEAF_CFG_TLS_INDEX_TYPE const reserve_tls_index::idx = tls_index::idx = index_counter<>::next(); +} // namespace detail - template - BOOST_LEAF_CFG_TLS_INDEX_TYPE tls_index::idx = BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX; +} } // namespace boost::leaf - template - BOOST_LEAF_CFG_TLS_INDEX_TYPE const alloc_tls_index::idx = tls_index::idx = index_counter<>::next(); +//////////////////////////////////////// - //////////////////////////////////////// +namespace boost { namespace leaf { - template - T * read_ptr() noexcept +namespace tls +{ + BOOST_LEAF_ALWAYS_INLINE unsigned generate_next_error_id() noexcept { - int tls_idx = tls_index::idx; - if( tls_idx == (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX) ) - return nullptr; - --tls_idx; - return reinterpret_cast(read_void_ptr(tls_idx)); + unsigned id = (detail::id_factory<>::counter += 4); + BOOST_LEAF_ASSERT((id&3) == 1); + return id; + } + + BOOST_LEAF_ALWAYS_INLINE void write_current_error_id( unsigned x ) noexcept + { + static_assert(sizeof(std::intptr_t) >= sizeof(unsigned), "Incompatible tls_array implementation"); + write_void_ptr(BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX, (void *) (std::intptr_t) x); + } + + BOOST_LEAF_ALWAYS_INLINE unsigned read_current_error_id() noexcept + { + static_assert(sizeof(std::intptr_t) >= sizeof(unsigned), "Incompatible tls_array implementation"); + return (unsigned) (std::intptr_t) read_void_ptr(BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX); } template - void write_ptr( T * p ) noexcept + BOOST_LEAF_ALWAYS_INLINE void reserve_ptr() { - int tls_idx = alloc_tls_index::idx; + (void) detail::reserve_tls_index::idx; + } + + template + BOOST_LEAF_ALWAYS_INLINE void write_ptr( T * p ) noexcept + { + int tls_idx = detail::tls_index::idx; + BOOST_LEAF_ASSERT(tls_idx != (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX + 1)); --tls_idx; write_void_ptr(tls_idx, p); BOOST_LEAF_ASSERT(read_void_ptr(tls_idx) == p); } - //////////////////////////////////////// - - template - unsigned read_uint() noexcept + template + BOOST_LEAF_ALWAYS_INLINE T * read_ptr() noexcept { - static_assert(sizeof(std::intptr_t) >= sizeof(unsigned), "Incompatible tls_array implementation"); - return (unsigned) (std::intptr_t) (void *) read_ptr(); + int tls_idx = detail::tls_index::idx; + if( tls_idx == (BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX + 1) ) + return nullptr; + --tls_idx; + return reinterpret_cast(read_void_ptr(tls_idx)); } +} // namespace tls - template - void write_uint( unsigned x ) noexcept - { - static_assert(sizeof(std::intptr_t) >= sizeof(unsigned), "Incompatible tls_array implementation"); - write_ptr((Tag *) (void *) (std::intptr_t) x); - } -} +} } // namespace boost::leaf -} } - -#endif // BOOST_LEAF_CONFIG_TLS_ARRAY_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_CONFIG_TLS_ARRAY_HPP_INCLUDED diff --git a/include/boost/leaf/config/tls_cpp11.hpp b/include/boost/leaf/config/tls_cpp11.hpp index 9828c77..f5aa5c7 100644 --- a/include/boost/leaf/config/tls_cpp11.hpp +++ b/include/boost/leaf/config/tls_cpp11.hpp @@ -1,27 +1,34 @@ #ifndef BOOST_LEAF_CONFIG_TLS_CPP11_HPP_INCLUDED #define BOOST_LEAF_CONFIG_TLS_CPP11_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) -// LEAF requires thread local storage support for pointers and for uin32_t values. +// This header implements the TLS API specified in tls.hpp using the C++11 +// built-in thread_local storage class specifier. On Windows, this +// implementation does not allow error objects to cross DLL boundaries. If this +// is required, define BOOST_LEAF_CFG_WIN32=2 before including any LEAF headers +// to enable the alternative implementation defined in tls_win32.hpp. -// This header implements thread local storage for pointers and for unsigned int -// values using the C++11 built-in thread_local storage class specifier. - -#include #include +#include namespace boost { namespace leaf { namespace detail { using atomic_unsigned_int = std::atomic; -} -namespace tls -{ + template + struct BOOST_LEAF_SYMBOL_VISIBLE id_factory + { + static atomic_unsigned_int counter; + }; + + template + atomic_unsigned_int id_factory::counter(1); + template struct BOOST_LEAF_SYMBOL_VISIBLE ptr { @@ -31,42 +38,59 @@ namespace tls template thread_local T * ptr::p; - template - T * read_ptr() noexcept - { - return ptr::p; - } - - template - void write_ptr( T * p ) noexcept - { - ptr::p = p; - } - - //////////////////////////////////////// - - template - struct BOOST_LEAF_SYMBOL_VISIBLE tagged_uint + template + struct BOOST_LEAF_SYMBOL_VISIBLE current_error_id_storage { static thread_local unsigned x; }; - template - thread_local unsigned tagged_uint::x; + template + thread_local unsigned current_error_id_storage::x; +} // namespace detail - template - unsigned read_uint() noexcept +} } // namespace boost::leaf + +//////////////////////////////////////// + +namespace boost { namespace leaf { + +namespace tls +{ + BOOST_LEAF_ALWAYS_INLINE unsigned generate_next_error_id() noexcept { - return tagged_uint::x; + unsigned id = (detail::id_factory<>::counter += 4); + BOOST_LEAF_ASSERT((id&3) == 1); + return id; } - template - void write_uint( unsigned x ) noexcept + BOOST_LEAF_ALWAYS_INLINE void write_current_error_id( unsigned x ) noexcept { - tagged_uint::x = x; + detail::current_error_id_storage<>::x = x; } -} -} } + BOOST_LEAF_ALWAYS_INLINE unsigned read_current_error_id() noexcept + { + return detail::current_error_id_storage<>::x; + } -#endif // BOOST_LEAF_CONFIG_TLS_CPP11_HPP_INCLUDED + template + BOOST_LEAF_ALWAYS_INLINE void reserve_ptr() + { + } + + template + BOOST_LEAF_ALWAYS_INLINE void write_ptr( T * p ) noexcept + { + detail::ptr::p = p; + } + + template + BOOST_LEAF_ALWAYS_INLINE T * read_ptr() noexcept + { + return detail::ptr::p; + } +} // namespace tls + +} } // namespace boost::leaf + +#endif // #ifndef BOOST_LEAF_CONFIG_TLS_CPP11_HPP_INCLUDED diff --git a/include/boost/leaf/config/tls_freertos.hpp b/include/boost/leaf/config/tls_freertos.hpp index d1d0be9..0c817e4 100644 --- a/include/boost/leaf/config/tls_freertos.hpp +++ b/include/boost/leaf/config/tls_freertos.hpp @@ -1,23 +1,18 @@ #ifndef BOOST_LEAF_CONFIG_TLS_FREERTOS_HPP_INCLUDED #define BOOST_LEAF_CONFIG_TLS_FREERTOS_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) // Copyright (c) 2022 Khalil Estell -// LEAF requires thread local storage support for pointers and for uin32_t values. - -// This header implements "thread local" storage via FreeTOS functions +// This header implements the TLS API specified in tls.hpp via the FreeTOS // pvTaskGetThreadLocalStoragePointer / pvTaskSetThreadLocalStoragePointer +// functions, using the more general implementation defined in tls_array.hpp. #include -#ifndef BOOST_LEAF_USE_TLS_ARRAY -# define BOOST_LEAF_USE_TLS_ARRAY -#endif - #ifndef BOOST_LEAF_CFG_TLS_ARRAY_SIZE # define BOOST_LEAF_CFG_TLS_ARRAY_SIZE configNUM_THREAD_LOCAL_STORAGE_POINTERS #endif @@ -31,12 +26,12 @@ namespace tls { // See https://www.freertos.org/thread-local-storage-pointers.html. - inline void * read_void_ptr( int tls_index ) noexcept + BOOST_LEAF_ALWAYS_INLINE void * read_void_ptr( int tls_index ) noexcept { return pvTaskGetThreadLocalStoragePointer(0, tls_index); } - inline void write_void_ptr( int tls_index, void * p ) noexcept + BOOST_LEAF_ALWAYS_INLINE void write_void_ptr( int tls_index, void * p ) noexcept { vTaskSetThreadLocalStoragePointer(0, tls_index, p); } @@ -44,4 +39,4 @@ namespace tls } } -#endif // BOOST_LEAF_CONFIG_TLS_FREERTOS_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_CONFIG_TLS_FREERTOS_HPP_INCLUDED diff --git a/include/boost/leaf/config/tls_globals.hpp b/include/boost/leaf/config/tls_globals.hpp index 5e9b1e9..66f3d57 100644 --- a/include/boost/leaf/config/tls_globals.hpp +++ b/include/boost/leaf/config/tls_globals.hpp @@ -1,14 +1,12 @@ #ifndef BOOST_LEAF_CONFIG_TLS_GLOBALS_HPP_INCLUDED #define BOOST_LEAF_CONFIG_TLS_GLOBALS_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) -// LEAF requires thread local storage support for pointers and for uin32_t values. - -// This header implements "thread local" storage for pointers and for unsigned int -// values using globals, which is suitable for single thread environments. +// This header implements the TLS API specified in tls.hpp using globals, which +// is suitable for single thread environments. #include @@ -17,10 +15,16 @@ namespace boost { namespace leaf { namespace detail { using atomic_unsigned_int = unsigned int; -} -namespace tls -{ + template + struct BOOST_LEAF_SYMBOL_VISIBLE id_factory + { + static atomic_unsigned_int counter; + }; + + template + atomic_unsigned_int id_factory::counter = 1; + template struct BOOST_LEAF_SYMBOL_VISIBLE ptr { @@ -30,42 +34,59 @@ namespace tls template T * ptr::p; - template - T * read_ptr() noexcept - { - return ptr::p; - } - - template - void write_ptr( T * p ) noexcept - { - ptr::p = p; - } - - //////////////////////////////////////// - - template - struct BOOST_LEAF_SYMBOL_VISIBLE tagged_uint + template + struct BOOST_LEAF_SYMBOL_VISIBLE current_error_id_storage { static unsigned x; }; - template - unsigned tagged_uint::x; + template + unsigned current_error_id_storage::x = 0; +} // namespace detail - template - unsigned read_uint() noexcept +} } // namespace boost::leaf + +//////////////////////////////////////// + +namespace boost { namespace leaf { + +namespace tls +{ + BOOST_LEAF_ALWAYS_INLINE unsigned generate_next_error_id() noexcept { - return tagged_uint::x; + unsigned id = (detail::id_factory<>::counter += 4); + BOOST_LEAF_ASSERT((id&3) == 1); + return id; } - template - void write_uint( unsigned x ) noexcept + BOOST_LEAF_ALWAYS_INLINE void write_current_error_id( unsigned v ) noexcept { - tagged_uint::x = x; + detail::current_error_id_storage<>::x = v; } -} -} } + BOOST_LEAF_ALWAYS_INLINE unsigned read_current_error_id() noexcept + { + return detail::current_error_id_storage<>::x; + } -#endif // BOOST_LEAF_CONFIG_TLS_GLOBALS_HPP_INCLUDED + template + BOOST_LEAF_ALWAYS_INLINE void reserve_ptr() + { + } + + template + BOOST_LEAF_ALWAYS_INLINE void write_ptr( T * p ) noexcept + { + detail::ptr::p = p; + } + + template + BOOST_LEAF_ALWAYS_INLINE T * read_ptr() noexcept + { + return detail::ptr::p; + } +} // namespace tls + +} } // namespace boost::leaf + +#endif // #ifndef BOOST_LEAF_CONFIG_TLS_GLOBALS_HPP_INCLUDED diff --git a/include/boost/leaf/config/tls_win32.hpp b/include/boost/leaf/config/tls_win32.hpp new file mode 100644 index 0000000..cfa69ea --- /dev/null +++ b/include/boost/leaf/config/tls_win32.hpp @@ -0,0 +1,495 @@ +#ifndef BOOST_LEAF_CONFIG_TLS_WIN32_HPP_INCLUDED +#define BOOST_LEAF_CONFIG_TLS_WIN32_HPP_INCLUDED + +// Copyright 2025 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 header implements the TLS API specified in tls.hpp using Win32 TLS +// functions, allowing error objects to cross DLL boundaries on Windows. This +// implementation is enabled by defining BOOST_LEAF_CFG_WIN32=2 before including +// any LEAF headers. + +#ifndef _WIN32 +# error "This header is only for Windows" +#endif + +#include +#include +#include +#include +#include +#include +#ifdef min +# undef min +#endif +#ifdef max +# undef max +#endif + +namespace boost { namespace leaf { + +// Thrown on TLS allocation failure. +class win32_tls_error: + public std::runtime_error +{ +public: + explicit win32_tls_error(char const * what) noexcept: + std::runtime_error(what) + { + } +}; + +namespace detail +{ + __declspec(noreturn) inline void raise_fail_fast(NTSTATUS status) noexcept + { + EXCEPTION_RECORD rec = {}; + rec.ExceptionCode = status; + rec.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + RaiseFailFastException(&rec, nullptr, 0); + BOOST_LEAF_UNREACHABLE; + } + + template + T * heap_new(Args && ... args) noexcept + { + void * mem = HeapAlloc(GetProcessHeap(), 0, sizeof(T)); + if (!mem) + { + raise_fail_fast(STATUS_NO_MEMORY); + BOOST_LEAF_UNREACHABLE; + } + return new (mem) T(static_cast(args)...); + } + + template + void heap_delete(T * p) noexcept + { + if (p) + { + p->~T(); + BOOL r = HeapFree(GetProcessHeap(), 0, p); + BOOST_LEAF_ASSERT(r), (void) r; + } + } + + template + class heap_allocator + { + public: + + using value_type = T; + + heap_allocator() noexcept = default; + + template + heap_allocator(heap_allocator const &) noexcept + { + } + + T * allocate(std::size_t n) noexcept + { + if (void * p = HeapAlloc(GetProcessHeap(), 0, n * sizeof(T))) + return static_cast(p); + raise_fail_fast(STATUS_NO_MEMORY); + BOOST_LEAF_UNREACHABLE; + } + + void deallocate(T * p, std::size_t) noexcept + { + BOOL r = HeapFree(GetProcessHeap(), 0, p); + BOOST_LEAF_ASSERT(r), (void) r; + } + + friend bool operator==(heap_allocator const &, heap_allocator const &) noexcept { return true; } + friend bool operator!=(heap_allocator const &, heap_allocator const &) noexcept { return false; } + }; + + class critical_section_lock + { + critical_section_lock(critical_section_lock const &) = delete; + critical_section_lock & operator=(critical_section_lock const &) = delete; + + CRITICAL_SECTION & cs_; + + public: + + explicit critical_section_lock(CRITICAL_SECTION & cs) noexcept: + cs_(cs) + { + EnterCriticalSection(&cs_); + } + + ~critical_section_lock() noexcept + { + LeaveCriticalSection(&cs_); + } + }; + + using atomic_unsigned_int = std::atomic; + + template + struct cpp11_hash_step + { + BOOST_LEAF_ALWAYS_INLINE constexpr static std::uint32_t compute(char const (&str)[N], std::uint32_t hash) noexcept + { + return cpp11_hash_step::compute(str, (hash ^ static_cast(str[I])) * 16777619u); + } + }; + + template + struct cpp11_hash_step + { + BOOST_LEAF_ALWAYS_INLINE constexpr static std::uint32_t compute(char const (&)[N], std::uint32_t hash) noexcept + { + return hash; + } + }; + + template + BOOST_LEAF_ALWAYS_INLINE constexpr std::uint32_t cpp11_hash_string(char const (&str)[N]) noexcept + { + return cpp11_hash_step::compute(str, 2166136261u); // str[N-2] is the last character before the \0. + } +} // namespace detail + +namespace n +{ + template + BOOST_LEAF_ALWAYS_INLINE constexpr std::uint32_t __cdecl h() noexcept + { + return detail::cpp11_hash_string(BOOST_LEAF_PRETTY_FUNCTION); + } +} + +namespace detail +{ + template + BOOST_LEAF_ALWAYS_INLINE constexpr std::uint32_t type_hash() noexcept + { + return n::h(); + } +} + +} } // namespace boost::leaf + +//////////////////////////////////////// + +namespace boost { namespace leaf { + +namespace detail +{ + class slot_map + { + slot_map(slot_map const &) = delete; + slot_map & operator=(slot_map const &) = delete; + + class tls_slot_index + { + tls_slot_index(tls_slot_index const &) = delete; + tls_slot_index & operator=(tls_slot_index const &) = delete; + tls_slot_index & operator=(tls_slot_index &&) = delete; + + DWORD idx_; + + public: + + BOOST_LEAF_ALWAYS_INLINE tls_slot_index(): + idx_(TlsAlloc()) + { + if (idx_ == TLS_OUT_OF_INDEXES) + throw_exception_(win32_tls_error("TLS_OUT_OF_INDEXES")); + } + + BOOST_LEAF_ALWAYS_INLINE ~tls_slot_index() noexcept + { + if (idx_ == TLS_OUT_OF_INDEXES) + return; + BOOL r = TlsFree(idx_); + BOOST_LEAF_ASSERT(r), (void) r; + } + + BOOST_LEAF_ALWAYS_INLINE tls_slot_index(tls_slot_index && other) noexcept: + idx_(other.idx_) + { + other.idx_ = TLS_OUT_OF_INDEXES; + } + + BOOST_LEAF_ALWAYS_INLINE DWORD get() const noexcept + { + BOOST_LEAF_ASSERT(idx_ != TLS_OUT_OF_INDEXES); + return idx_; + } + }; + + int refcount_; + HANDLE const mapping_; + tls_slot_index const error_id_slot_; + mutable CRITICAL_SECTION cs_; + std::unordered_map< + std::uint32_t, + tls_slot_index, + std::hash, + std::equal_to, + heap_allocator>> map_; + atomic_unsigned_int error_id_storage_; + + public: + + explicit slot_map(HANDLE mapping) noexcept: + refcount_(1), + mapping_(mapping), + error_id_storage_(1) + { + BOOST_LEAF_ASSERT(mapping != INVALID_HANDLE_VALUE); + InitializeCriticalSection(&cs_); + } + + ~slot_map() noexcept + { + DeleteCriticalSection(&cs_); + BOOL r = CloseHandle(mapping_); + BOOST_LEAF_ASSERT(r), (void) r; + } + + BOOST_LEAF_ALWAYS_INLINE void add_ref() noexcept + { + BOOST_LEAF_ASSERT(refcount_ >= 1); + ++refcount_; + } + + BOOST_LEAF_ALWAYS_INLINE void release() noexcept + { + --refcount_; + BOOST_LEAF_ASSERT(refcount_ >= 0); + if (refcount_ == 0) + heap_delete(this); + } + + DWORD check(std::uint32_t type_hash) const noexcept + { + critical_section_lock lock(cs_); + auto it = map_.find(type_hash); + return (it != map_.end()) ? it->second.get() : TLS_OUT_OF_INDEXES; + } + + DWORD get(std::uint32_t type_hash) + { + critical_section_lock lock(cs_); + DWORD idx = map_[type_hash].get(); + BOOST_LEAF_ASSERT(idx != TLS_OUT_OF_INDEXES); + return idx; + } + + BOOST_LEAF_ALWAYS_INLINE DWORD error_id_slot() const noexcept + { + return error_id_slot_.get(); + } + + BOOST_LEAF_ALWAYS_INLINE atomic_unsigned_int & error_id_storage() noexcept + { + return error_id_storage_; + } + }; // class slot_map + + class module_state + { + module_state(module_state const &) = delete; + module_state & operator=(module_state const &) = delete; + + static constexpr unsigned tls_failure_create_mapping = 0x01; + static constexpr unsigned tls_failure_map_view = 0x02; + + void * hinstance_; + unsigned tls_failures_; + slot_map * sm_; + + public: + + constexpr module_state() noexcept: + hinstance_(nullptr), + tls_failures_(0), + sm_(nullptr) + { + } + + BOOST_LEAF_ALWAYS_INLINE slot_map & sm() const noexcept + { + BOOST_LEAF_ASSERT(hinstance_); + BOOST_LEAF_ASSERT(!(tls_failures_ & tls_failure_create_mapping)); + BOOST_LEAF_ASSERT(!(tls_failures_ & tls_failure_map_view)); + BOOST_LEAF_ASSERT(sm_); + return *sm_; + } + + BOOST_LEAF_ALWAYS_INLINE void update(PVOID hinstDLL, DWORD dwReason) noexcept + { + if (dwReason == DLL_PROCESS_ATTACH) + { + hinstance_ = hinstDLL; + char name[32] = "Local\\boost_leaf_"; + { + constexpr static char const hex[] = "0123456789ABCDEF"; + DWORD pid = GetCurrentProcessId(); + for (int i = 7; i >= 0; --i) + { + name[17 + i] = hex[pid & 0xf]; + pid >>= 4; + } + name[25] = '\0'; + } + HANDLE mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, sizeof(slot_map *), name); + DWORD mapping_status = GetLastError(); + if (!mapping) + { + tls_failures_ |= tls_failure_create_mapping; + return; + } + BOOST_LEAF_ASSERT(mapping_status == ERROR_ALREADY_EXISTS || mapping_status == ERROR_SUCCESS); + bool is_first_module = (mapping_status == ERROR_SUCCESS); + slot_map * * mapped_ptr = static_cast(MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, sizeof(slot_map *))); + if (!mapped_ptr) + { + tls_failures_ |= tls_failure_map_view; + BOOL r = CloseHandle(mapping); + BOOST_LEAF_ASSERT(r), (void) r; + return; + } + if (is_first_module) + sm_ = *mapped_ptr = heap_new(mapping); + else + { + sm_ = *mapped_ptr; + sm_->add_ref(); + BOOL r = CloseHandle(mapping); + BOOST_LEAF_ASSERT(r), (void) r; + } + UnmapViewOfFile(mapped_ptr); + } + else if (dwReason == DLL_PROCESS_DETACH) + { + BOOST_LEAF_ASSERT(sm_ || tls_failures_); + if (sm_) + { + sm_->release(); + sm_ = nullptr; + } + } + } + }; // class module_state + + template + struct module + { + static module_state state; + }; + + template + module_state module::state; + + BOOST_LEAF_ALWAYS_INLINE unsigned generate_next_error_id() noexcept + { + static atomic_unsigned_int & counter = module<>::state.sm().error_id_storage(); + unsigned id = (counter += 4); + BOOST_LEAF_ASSERT((id&3) == 1); + return id; + } + + inline void NTAPI tls_callback(PVOID hinstDLL, DWORD dwReason, PVOID) noexcept + { + module<>::state.update(hinstDLL, dwReason); + } + +#ifdef _MSC_VER +# pragma section(".CRT$XLB", long, read) +# pragma data_seg(push, ".CRT$XLB") + +extern "C" __declspec(selectany) PIMAGE_TLS_CALLBACK boost_leaf_tls_callback = tls_callback; + +# pragma data_seg(pop) +# ifdef _WIN64 +# pragma comment(linker, "/INCLUDE:boost_leaf_tls_callback") +# else +# pragma comment(linker, "/INCLUDE:_boost_leaf_tls_callback") +# endif +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wattributes" + +extern "C" __attribute__((used, selectany)) PIMAGE_TLS_CALLBACK boost_leaf_tls_callback __attribute__((section(".CRT$XLB"))) = tls_callback; + +# pragma GCC diagnostic pop +#else +# error Unknown compiler, unable to define .CRT$XLB section +#endif +} // namespace detail + +} } // namespace boost::leaf + +//////////////////////////////////////// + +namespace boost { namespace leaf { + +namespace tls +{ + BOOST_LEAF_ALWAYS_INLINE unsigned generate_next_error_id() noexcept + { + return detail::generate_next_error_id(); + } + + BOOST_LEAF_ALWAYS_INLINE void write_current_error_id(unsigned x) noexcept + { + using namespace detail; + DWORD slot = module<>::state.sm().error_id_slot(); + BOOL r = TlsSetValue(slot, reinterpret_cast(static_cast(x))); + BOOST_LEAF_ASSERT(r), (void) r; + } + + BOOST_LEAF_ALWAYS_INLINE unsigned read_current_error_id() noexcept + { + using namespace detail; + DWORD slot = module<>::state.sm().error_id_slot(); + LPVOID value = TlsGetValue(slot); + BOOST_LEAF_ASSERT(GetLastError() == ERROR_SUCCESS); + return static_cast(reinterpret_cast(value)); + } + + template + BOOST_LEAF_ALWAYS_INLINE void reserve_ptr() + { + using namespace detail; + thread_local DWORD const cached_slot = module<>::state.sm().get(type_hash()); + BOOST_LEAF_ASSERT(cached_slot != TLS_OUT_OF_INDEXES), (void) cached_slot; + } + + template + BOOST_LEAF_ALWAYS_INLINE void write_ptr(T * p) noexcept + { + using namespace detail; + thread_local DWORD const cached_slot = module<>::state.sm().check(type_hash()); + DWORD slot = cached_slot; + BOOST_LEAF_ASSERT(slot != TLS_OUT_OF_INDEXES); + BOOL r = TlsSetValue(slot, p); + BOOST_LEAF_ASSERT(r), (void) r; + } + + template + BOOST_LEAF_ALWAYS_INLINE T * read_ptr() noexcept + { + using namespace detail; + thread_local DWORD cached_slot = TLS_OUT_OF_INDEXES; + if (cached_slot == TLS_OUT_OF_INDEXES) + cached_slot = module<>::state.sm().check(type_hash()); + DWORD slot = cached_slot; + if (slot == TLS_OUT_OF_INDEXES) + return nullptr; + LPVOID value = TlsGetValue(slot); + BOOST_LEAF_ASSERT(GetLastError() == ERROR_SUCCESS); + return static_cast(value); + } +} // namespace tls + +} } // namespace boost::leaf + +#endif // #ifndef BOOST_LEAF_CONFIG_TLS_WIN32_HPP_INCLUDED diff --git a/include/boost/leaf/config/visibility.hpp b/include/boost/leaf/config/visibility.hpp new file mode 100644 index 0000000..86615bb --- /dev/null +++ b/include/boost/leaf/config/visibility.hpp @@ -0,0 +1,45 @@ +#ifndef BOOST_LEAF_CONFIG_VISIBILITY_HPP_INCLUDED +#define BOOST_LEAF_CONFIG_VISIBILITY_HPP_INCLUDED + +// Copyright 2018-2024 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) + +namespace boost { namespace leaf { + +class BOOST_LEAF_SYMBOL_VISIBLE error_id; +class BOOST_LEAF_SYMBOL_VISIBLE error_info; +class BOOST_LEAF_SYMBOL_VISIBLE diagnostic_info; +class BOOST_LEAF_SYMBOL_VISIBLE diagnostic_details; + +struct BOOST_LEAF_SYMBOL_VISIBLE e_api_function; +struct BOOST_LEAF_SYMBOL_VISIBLE e_file_name; +struct BOOST_LEAF_SYMBOL_VISIBLE e_errno; +struct BOOST_LEAF_SYMBOL_VISIBLE e_type_info_name; +struct BOOST_LEAF_SYMBOL_VISIBLE e_at_line; +struct BOOST_LEAF_SYMBOL_VISIBLE e_source_location; + +class BOOST_LEAF_SYMBOL_VISIBLE bad_result; +template class BOOST_LEAF_SYMBOL_VISIBLE result; + +namespace detail +{ + template class BOOST_LEAF_SYMBOL_VISIBLE slot; + + class BOOST_LEAF_SYMBOL_VISIBLE exception_base; + + template class BOOST_LEAF_SYMBOL_VISIBLE exception; + +#if BOOST_LEAF_CFG_CAPTURE + class BOOST_LEAF_SYMBOL_VISIBLE dynamic_allocator; +#endif + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + class BOOST_LEAF_SYMBOL_VISIBLE leaf_error_category; + template struct BOOST_LEAF_SYMBOL_VISIBLE get_leaf_error_category; +#endif +} + +} } // namespace boost::leaf + +#endif // BOOST_LEAF_CONFIG_VISIBILITY_HPP_INCLUDED diff --git a/include/boost/leaf/context.hpp b/include/boost/leaf/context.hpp index 15dcb22..63518f3 100644 --- a/include/boost/leaf/context.hpp +++ b/include/boost/leaf/context.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_LEAF_CONTEXT_HPP_INCLUDED #define BOOST_LEAF_CONTEXT_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) @@ -130,7 +130,7 @@ namespace detail { static_assert(sizeof(E) == 0, "Error handlers must take this type by value"); }; -} +} // namespace detail //////////////////////////////////////// @@ -171,7 +171,7 @@ namespace detail else return find_in_tuple(t); } -} +} // namespace detail //////////////////////////////////////// @@ -227,7 +227,7 @@ namespace detail { tuple_for_each::value, Tup>::print(os, tup, to_print, prefix); } -} +} // namespace detail //////////////////////////////////////// @@ -262,7 +262,7 @@ namespace detail template using deduce_e_tuple = typename deduce_e_tuple_impl>::type>::type; -} +} // namespace detail //////////////////////////////////////// @@ -292,11 +292,13 @@ class context if( ctx_ ) ctx_->activate(); } +#if __cplusplus < 201703L BOOST_LEAF_CONSTEXPR BOOST_LEAF_ALWAYS_INLINE raii_deactivator( raii_deactivator && x ) noexcept: ctx_(x.ctx_) { x.ctx_ = nullptr; } +#endif BOOST_LEAF_ALWAYS_INLINE ~raii_deactivator() noexcept { if( ctx_ && ctx_->is_active() ) @@ -313,7 +315,7 @@ public: BOOST_LEAF_ASSERT(!x.is_active()); } - BOOST_LEAF_CONSTEXPR context() noexcept: + BOOST_LEAF_CONSTEXPR context(): is_active_(false) { } @@ -398,7 +400,7 @@ public: { return raii_deactivator(ctx); } -}; +}; // template context //////////////////////////////////////// @@ -439,7 +441,7 @@ namespace detail { using type = deduce_context::type...>>; }; -} +} // namespace detail template using context_type_from_handlers = typename detail::context_type_from_handlers_impl::type; @@ -458,6 +460,6 @@ BOOST_LEAF_CONSTEXPR inline context_type_from_handlers make_context( H && return { }; } -} } +} } // namespace boost::leaf -#endif // BOOST_LEAF_CONTEXT_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_CONTEXT_HPP_INCLUDED diff --git a/include/boost/leaf/detail/all.hpp b/include/boost/leaf/detail/all.hpp index 49edc16..62030d5 100644 --- a/include/boost/leaf/detail/all.hpp +++ b/include/boost/leaf/detail/all.hpp @@ -1,4 +1,4 @@ -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) diff --git a/include/boost/leaf/detail/capture_list.hpp b/include/boost/leaf/detail/capture_list.hpp index 37d2480..9b51ed6 100644 --- a/include/boost/leaf/detail/capture_list.hpp +++ b/include/boost/leaf/detail/capture_list.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_LEAF_DETAIL_CAPTURE_LIST_HPP_INCLUDED #define BOOST_LEAF_DETAIL_CAPTURE_LIST_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) @@ -14,11 +14,8 @@ namespace boost { namespace leaf { -class error_id; - namespace detail { - struct BOOST_LEAF_SYMBOL_VISIBLE tls_tag_id_factory_current_id; class capture_list { @@ -87,7 +84,7 @@ namespace detail { capture_list moved(first_); first_ = nullptr; - tls::write_uint(unsigned(err_id)); + tls::write_current_error_id(unsigned(err_id)); moved.for_each( [err_id]( node & n ) { @@ -113,12 +110,12 @@ namespace detail (void) to_print; #endif } - }; + }; // class capture_list -} +} // namespace detail -} } +} } // namespace boost::leaf -#endif +#endif // #if BOOST_LEAF_CFG_CAPTURE -#endif // BOOST_LEAF_DETAIL_CAPTURE_LIST_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_DETAIL_CAPTURE_LIST_HPP_INCLUDED diff --git a/include/boost/leaf/detail/demangle.hpp b/include/boost/leaf/detail/demangle.hpp index 3562b47..e6003fa 100644 --- a/include/boost/leaf/detail/demangle.hpp +++ b/include/boost/leaf/detail/demangle.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_LEAF_DETAIL_DEMANGLE_HPP_INCLUDED #define BOOST_LEAF_DETAIL_DEMANGLE_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) @@ -41,14 +41,14 @@ # endif #endif -#endif +#endif // #if BOOST_LEAF_CFG_DIAGNOSTICS namespace boost { namespace leaf { namespace detail { // The functions below are C++11 constexpr, but we use BOOST_LEAF_ALWAYS_INLINE to control object file - // section count / template bleat. Evidently this makes a difference on gcc / windows at least. + // section count / template bloat. template = S2> struct cpp11_prefix @@ -111,7 +111,7 @@ namespace detail { return cpp11_suffix::check(str, suffix) ? S1 - S2 : 0; } -} +} // namespace detail namespace n { @@ -205,7 +205,7 @@ namespace n int const p = sizeof(char[1 + !!s02 * (p22 + p23 + p24)]) - 1; // p is not zero, we've static asserted the hell out of it return { BOOST_LEAF_PRETTY_FUNCTION + p, s02 - p }; } -} +} // namespace n using parsed = n::r; @@ -215,7 +215,7 @@ parsed parse() return n::p(); } -} } +} } // namespace boost::leaf //////////////////////////////////////// @@ -243,11 +243,11 @@ namespace detail } d(mangled_name); if( d.demangled_name ) return os << d.demangled_name; -#endif +#endif // #if defined(BOOST_LEAF_CFG_DIAGNOSTICS) && defined(BOOST_LEAF_HAS_CXXABI_H) return os << mangled_name; } -} +} // namespace detail -} } +} } // namespace boost::leaf -#endif // BOOST_LEAF_DETAIL_DEMANGLE_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_DETAIL_DEMANGLE_HPP_INCLUDED diff --git a/include/boost/leaf/detail/function_traits.hpp b/include/boost/leaf/detail/function_traits.hpp index bfc336c..e8ccb11 100644 --- a/include/boost/leaf/detail/function_traits.hpp +++ b/include/boost/leaf/detail/function_traits.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_LEAF_DETAIL_FUNCTION_TRAITS_HPP_INCLUDED #define BOOST_LEAF_DETAIL_FUNCTION_TRAITS_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) @@ -92,8 +92,8 @@ namespace detail template using fn_mp_args = typename function_traits::mp_args; -} +} // namespace detail -} } +} } // namespace boost::leaf -#endif // BOOST_LEAF_DETAIL_FUNCTION_TRAITS_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_DETAIL_FUNCTION_TRAITS_HPP_INCLUDED diff --git a/include/boost/leaf/detail/mp11.hpp b/include/boost/leaf/detail/mp11.hpp index 10e8e11..68e296f 100644 --- a/include/boost/leaf/detail/mp11.hpp +++ b/include/boost/leaf/detail/mp11.hpp @@ -2,7 +2,7 @@ #define BOOST_LEAF_DETAIL_MP11_HPP_INCLUDED // Copyright 2015-2017 Peter Dimov. -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 Emil Dotchevski and Reverge Studios, Inc. // // Distributed under the Boost Software License, Version 1.0. // @@ -300,4 +300,4 @@ template class F, class... T> using mp_valid = typename detai } } } -#endif // BOOST_LEAF_DETAIL_MP11_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_DETAIL_MP11_HPP_INCLUDED diff --git a/include/boost/leaf/detail/optional.hpp b/include/boost/leaf/detail/optional.hpp index 60c7783..06b1051 100644 --- a/include/boost/leaf/detail/optional.hpp +++ b/include/boost/leaf/detail/optional.hpp @@ -1,13 +1,11 @@ #ifndef BOOST_LEAF_DETAIL_OPTIONAL_HPP_INCLUDED #define BOOST_LEAF_DETAIL_OPTIONAL_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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 boost { namespace leaf { @@ -133,16 +131,6 @@ namespace detail return value_; } - BOOST_LEAF_CONSTEXPR T const * has_value_any_key() const noexcept - { - return key_ ? &value_ : nullptr; - } - - BOOST_LEAF_CONSTEXPR T * has_value_any_key() noexcept - { - return key_ ? &value_ : nullptr; - } - BOOST_LEAF_CONSTEXPR T const * has_value(int key) const noexcept { BOOST_LEAF_ASSERT(key); @@ -184,18 +172,10 @@ namespace detail reset(); return tmp; } + }; // template optional - BOOST_LEAF_CONSTEXPR T & value_or_default(int key) noexcept - { - if( T * v = has_value(key) ) - return *v; - else - return load(key); - } - }; +} // namespace detail -} +} } // namespace boost::leaf -} } - -#endif // BOOST_LEAF_DETAIL_OPTIONAL_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_DETAIL_OPTIONAL_HPP_INCLUDED diff --git a/include/boost/leaf/detail/print.hpp b/include/boost/leaf/detail/print.hpp index 2189fb2..4299de6 100644 --- a/include/boost/leaf/detail/print.hpp +++ b/include/boost/leaf/detail/print.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_LEAF_DETAIL_PRINT_HPP_INCLUDED #define BOOST_LEAF_DETAIL_PRINT_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) @@ -9,9 +9,7 @@ #include #include -#include #include -#include namespace boost { namespace leaf { @@ -155,8 +153,8 @@ namespace detail return print_impl(os, prefix, delimiter, ": ", static_cast::type>(enum_)); } }; -} +} // namespace detail -} } +} } // namespace boost::leaf -#endif // BOOST_LEAF_DETAIL_PRINT_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_DETAIL_PRINT_HPP_INCLUDED diff --git a/include/boost/leaf/diagnostics.hpp b/include/boost/leaf/diagnostics.hpp index 5ab490f..28449d6 100644 --- a/include/boost/leaf/diagnostics.hpp +++ b/include/boost/leaf/diagnostics.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_LEAF_DIAGNOSTICS_HPP_INCLUDED #define BOOST_LEAF_DIAGNOSTICS_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) @@ -44,7 +44,7 @@ protected: x.print_diagnostic_info(os); return os << '\n'; } -}; +}; // class diagnostic_info namespace detail { @@ -68,7 +68,7 @@ namespace detail }; } -#else +#else // #if BOOST_LEAF_CFG_DIAGNOSTICS class diagnostic_info: public error_info { @@ -94,7 +94,7 @@ protected: x.print_diagnostic_info(os); return os << "\n"; } -}; +}; // class diagnostic_info namespace detail { @@ -117,7 +117,7 @@ namespace detail }; } -#endif +#endif // #else (#if BOOST_LEAF_CFG_DIAGNOSTICS) //////////////////////////////////////// @@ -157,7 +157,7 @@ protected: x.print_diagnostic_details(os); return os << '\n'; } -}; +}; // class diagnostic_details namespace detail { @@ -177,12 +177,12 @@ namespace detail BOOST_LEAF_CONSTEXPR static diagnostic_details_ get( Tup const & tup, error_info const & ei ) noexcept { slot const * da = find_in_tuple>(tup); - return diagnostic_details_(ei, tup, da ? da->has_value_any_key() : nullptr ); + return diagnostic_details_(ei, tup, da ? &da->get() : nullptr ); } }; } -#else +#else // #if BOOST_LEAF_CFG_CAPTURE class diagnostic_details: public diagnostic_info { @@ -209,7 +209,7 @@ protected: x.print_diagnostic_details(os); return os << "\n"; } -}; +}; // class diagnostic_details namespace detail { @@ -233,9 +233,9 @@ namespace detail }; } -#endif +#endif // #else (#if BOOST_LEAF_CFG_CAPTURE) -#else +#else // #if BOOST_LEAF_CFG_DIAGNOSTICS class diagnostic_details: public diagnostic_info { @@ -284,10 +284,10 @@ namespace detail }; } -#endif +#endif // #else (#if BOOST_LEAF_CFG_DIAGNOSTICS) using verbose_diagnostic_info = diagnostic_details; -} } +} } // namespace boost::leaf -#endif // BOOST_LEAF_DIAGNOSTICS_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_DIAGNOSTICS_HPP_INCLUDED diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 9f96670..a9e3ba3 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_LEAF_ERROR_HPP_INCLUDED #define BOOST_LEAF_ERROR_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) @@ -45,7 +45,7 @@ std::move(BOOST_LEAF_TMP);\ }).value() -#else +#else // #if BOOST_LEAF_CFG_GNUC_STMTEXPR #define BOOST_LEAF_CHECK(r)\ {\ @@ -56,13 +56,13 @@ return BOOST_LEAF_TMP.error();\ } -#endif +#endif // #else (#if BOOST_LEAF_CFG_GNUC_STMTEXPR) #define BOOST_LEAF_NEW_ERROR ::boost::leaf::detail::inject_loc{__FILE__,__LINE__,__FUNCTION__}+::boost::leaf::new_error namespace boost { namespace leaf { -struct BOOST_LEAF_SYMBOL_VISIBLE e_source_location +struct e_source_location { char const * file; int line; @@ -82,11 +82,9 @@ struct show_in_diagnostics: std::false_type //////////////////////////////////////// -class BOOST_LEAF_SYMBOL_VISIBLE error_id; - namespace detail { - class BOOST_LEAF_SYMBOL_VISIBLE exception_base + class exception_base { public: virtual error_id get_error_id() const noexcept = 0; @@ -104,9 +102,10 @@ namespace detail namespace detail { template - class BOOST_LEAF_SYMBOL_VISIBLE slot: + class slot: optional { + static_assert(std::is_same::type>::value, "E must be decayed for slot"); slot( slot const & ) = delete; slot & operator=( slot const & ) = delete; @@ -115,9 +114,18 @@ namespace detail public: - BOOST_LEAF_CONSTEXPR slot() noexcept: + BOOST_LEAF_CONSTEXPR slot(): prev_(nullptr) { + tls::reserve_ptr>(); + } + + template + BOOST_LEAF_CONSTEXPR slot( int key, T && e ): + optional(key, std::forward(e)), + prev_(nullptr) + { + tls::reserve_ptr>(); } BOOST_LEAF_CONSTEXPR slot( slot && x ) noexcept: @@ -159,11 +167,9 @@ namespace detail using impl::load; using impl::has_value; - using impl::has_value_any_key; using impl::value; - using impl::value_or_default; - }; -} + }; // template slot +} // namespace detail //////////////////////////////////////// @@ -171,12 +177,22 @@ namespace detail namespace detail { - class BOOST_LEAF_SYMBOL_VISIBLE dynamic_allocator: + class preloaded_base; + + template + struct capturing_slot_node_allocator; + + class dynamic_allocator: capture_list { dynamic_allocator( dynamic_allocator const & ) = delete; dynamic_allocator & operator=( dynamic_allocator const & ) = delete; + template + friend struct capturing_slot_node_allocator; + + preloaded_base * preloaded_list_; + class capturing_node: public capture_list::node { @@ -192,54 +208,60 @@ namespace detail }; template - class capturing_slot_node: - public capturing_node, - public slot + class capturing_slot_node final: + public slot, + public capturing_node { using impl = slot; capturing_slot_node( capturing_slot_node const & ) = delete; capturing_slot_node & operator=( capturing_slot_node const & ) = delete; - void deactivate() const noexcept final override + void deactivate() const noexcept override { impl::deactivate(); } - void unload( int err_id ) final override + void unload( int err_id ) override { impl::unload(err_id); } #if BOOST_LEAF_CFG_DIAGNOSTICS - void print(std::ostream & os, error_id const & to_print, char const * & prefix) const final override + void print(std::ostream & os, error_id const & to_print, char const * & prefix) const override { impl::print(os, to_print, prefix); } #endif public: - template - BOOST_LEAF_CONSTEXPR capturing_slot_node( capture_list::node * * & last, int err_id, T && e ): + BOOST_LEAF_CONSTEXPR explicit capturing_slot_node( capture_list::node * * & last ): + capturing_node(last) + { + BOOST_LEAF_ASSERT(last == &next_); + BOOST_LEAF_ASSERT(next_ == nullptr); + } + template + BOOST_LEAF_CONSTEXPR capturing_slot_node( capture_list::node * * & last, int err_id, T && e ): + slot(err_id, std::forward(e)), capturing_node(last) { BOOST_LEAF_ASSERT(last == &next_); BOOST_LEAF_ASSERT(next_ == nullptr); - impl::load(err_id, std::forward(e)); } }; #ifndef BOOST_LEAF_NO_EXCEPTIONS - class capturing_exception_node: + class capturing_exception_node final: public capturing_node { capturing_exception_node( capturing_exception_node const & ) = delete; capturing_exception_node & operator=( capturing_exception_node const & ) = delete; - void deactivate() const noexcept final override + void deactivate() const noexcept override { BOOST_LEAF_ASSERT(0); } - void unload( int ) final override + void unload( int ) override { std::rethrow_exception(ex_); } #if BOOST_LEAF_CFG_DIAGNOSTICS - void print(std::ostream &, error_id const &, char const * &) const final override + void print(std::ostream &, error_id const &, char const * &) const override { } #endif @@ -253,7 +275,7 @@ namespace detail BOOST_LEAF_ASSERT(ex_); } }; -#endif +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS node * * last_; @@ -261,6 +283,7 @@ namespace detail dynamic_allocator() noexcept: capture_list(nullptr), + preloaded_list_(nullptr), last_(&first_) { BOOST_LEAF_ASSERT(first_ == nullptr); @@ -268,24 +291,52 @@ namespace detail dynamic_allocator( dynamic_allocator && other ) noexcept: capture_list(std::move(other)), + preloaded_list_(nullptr), last_(other.last_ == &other.first_? &first_ : other.last_) { BOOST_LEAF_ASSERT(last_ != nullptr); BOOST_LEAF_ASSERT(*last_ == nullptr); BOOST_LEAF_ASSERT(other.first_ == nullptr); + BOOST_LEAF_ASSERT(other.preloaded_list_ == nullptr); other.last_ = &other.first_; } - template - typename std::decay::type & dynamic_load(int err_id, E && e) + preloaded_base * preloaded_list() const noexcept + { + return preloaded_list_; + } + + preloaded_base * link_preloaded_item(preloaded_base * pb) noexcept + { + BOOST_LEAF_ASSERT(pb != nullptr); + preloaded_base * next = preloaded_list_; + preloaded_list_ = pb; + return next; + } + + void unlink_preloaded_item(preloaded_base * next) noexcept + { + preloaded_list_ = next; + } + + template + slot * alloc() { - using T = typename std::decay::type; BOOST_LEAF_ASSERT(last_ != nullptr); BOOST_LEAF_ASSERT(*last_ == nullptr); - BOOST_LEAF_ASSERT(tls::read_ptr>() == nullptr); - capturing_slot_node * csn = new capturing_slot_node(last_, err_id, std::forward(e)); + BOOST_LEAF_ASSERT(tls::read_ptr>() == nullptr); + capturing_slot_node * csn = capturing_slot_node_allocator::new_(last_); csn->activate(); - return csn->value(err_id); + return csn; + } + + template + slot * reserve() + { + if( slot * p = tls::read_ptr>() ) + return p; + else + return alloc(); } void deactivate() const noexcept @@ -312,180 +363,165 @@ namespace detail using capture_list::unload; using capture_list::print; + }; // class dynamic_allocator + + template + struct capturing_slot_node_allocator + { + template + static dynamic_allocator::capturing_slot_node * new_( A && ... a ) + { + return new dynamic_allocator::capturing_slot_node(std::forward(a)...); + } + + static void delete_( dynamic_allocator::capturing_slot_node * p ) noexcept + { + delete p; + } }; template <> - inline void slot::deactivate() const noexcept + class slot { - if( dynamic_allocator const * c = this->has_value_any_key() ) - c->deactivate(); - tls::write_ptr>(prev_); - } + slot( slot const & ) = delete; + slot & operator=( slot const & ) = delete; - template <> - inline void slot::unload( int err_id ) noexcept(false) - { - BOOST_LEAF_ASSERT(err_id); - if( dynamic_allocator * da1 = this->has_value_any_key() ) - da1->unload(err_id); - } + dynamic_allocator da_; + slot * prev_; - template - inline void dynamic_load_( int err_id, E && e ) - { - if( slot * sl = tls::read_ptr>() ) + public: + + slot() noexcept: + prev_(nullptr) { - if( dynamic_allocator * c = sl->has_value_any_key() ) - c->dynamic_load(err_id, std::forward(e)); - else - sl->load(err_id).dynamic_load(err_id, std::forward(e)); + tls::reserve_ptr>(); } - } - template - inline void dynamic_accumulate_( int err_id, F && f ) - { - if( slot * sl = tls::read_ptr>() ) + slot( slot && x ) noexcept: + da_(std::move(x.da_)), + prev_(nullptr) { - if( dynamic_allocator * c = sl->has_value(err_id) ) - (void) std::forward(f)(c->dynamic_load(err_id, E{})); - else - (void) std::forward(f)(sl->load(err_id).dynamic_load(err_id, E{})); + BOOST_LEAF_ASSERT(x.prev_ == nullptr); } - } - template - inline void dynamic_load( int err_id, E && e ) noexcept(OnError) - { - if( OnError ) + ~slot() noexcept { -#ifndef BOOST_LEAF_NO_EXCEPTIONS - try - { -#endif - dynamic_load_(err_id, std::forward(e)); -#ifndef BOOST_LEAF_NO_EXCEPTIONS - } - catch(...) - { - } -#endif + BOOST_LEAF_ASSERT(tls::read_ptr>() != this); } - else - dynamic_load_(err_id, std::forward(e)); - } - template - inline void dynamic_load_accumulate( int err_id, F && f ) noexcept(OnError) - { - if( OnError ) + dynamic_allocator const & get() const noexcept { -#ifndef BOOST_LEAF_NO_EXCEPTIONS - try - { -#endif - dynamic_accumulate_(err_id, std::forward(f)); -#ifndef BOOST_LEAF_NO_EXCEPTIONS - } - catch(...) - { - } -#endif + return da_; } - else - dynamic_accumulate_(err_id, std::forward(f)); - } -} -template <> -struct show_in_diagnostics: std::false_type -{ -}; + dynamic_allocator & get() noexcept + { + return da_; + } + void activate() noexcept + { + prev_ = tls::read_ptr>(); + tls::write_ptr>(this); + } + + void deactivate() const noexcept + { + da_.deactivate(); + tls::write_ptr>(prev_); + } + + void unload( int err_id ) + { + BOOST_LEAF_ASSERT(err_id); + da_.unload(err_id); + } + +#if BOOST_LEAF_CFG_DIAGNOSTICS + template + void print(std::basic_ostream &, ErrorID, char const * &) const + { + } #endif + }; // slot specialization for dynamic_allocator +} // namespace detail + +#endif // #if BOOST_LEAF_CFG_CAPTURE //////////////////////////////////////// namespace detail { +#if BOOST_LEAF_CFG_CAPTURE + inline dynamic_allocator * get_dynamic_allocator() noexcept + { + if( slot * sl = tls::read_ptr>() ) + return &sl->get(); + return nullptr; + } +#endif + + template + inline slot * get_slot() noexcept(!BOOST_LEAF_CFG_CAPTURE) + { + static_assert(!std::is_pointer::value, "Error objects of pointer types are not allowed"); + static_assert(!std::is_same::value, "Error objects of type error_id are not allowed"); + if( slot * p = tls::read_ptr>() ) + return p; +#if BOOST_LEAF_CFG_CAPTURE + if( dynamic_allocator * da = get_dynamic_allocator() ) + return da->alloc(); +#endif + return nullptr; + } + template inline void slot::unload( int err_id ) noexcept(!BOOST_LEAF_CFG_CAPTURE) { BOOST_LEAF_ASSERT(err_id); if( this->key() != err_id ) return; - if( impl * p = tls::read_ptr>() ) - { + if( impl * p = get_slot() ) if( !p->has_value(err_id) ) *p = std::move(*this); - } -#if BOOST_LEAF_CFG_CAPTURE - else - dynamic_load(err_id, std::move(*this).value(err_id)); -#endif } - template - BOOST_LEAF_CONSTEXPR inline int load_slot( int err_id, E && e ) noexcept(OnError) + template + BOOST_LEAF_CONSTEXPR inline int load_slot( int err_id, E && e ) noexcept(!BOOST_LEAF_CFG_CAPTURE) { - using T = typename std::decay::type; - static_assert(!std::is_pointer::value, "Error objects of pointer types are not allowed"); - static_assert(!std::is_same::value, "Error objects of type error_id are not allowed"); + using E_decayed = typename std::decay::type; BOOST_LEAF_ASSERT((err_id&3) == 1); - if( slot * p = tls::read_ptr>() ) - { - if( !OnError || !p->has_value(err_id) ) - (void) p->load(err_id, std::forward(e)); - } -#if BOOST_LEAF_CFG_CAPTURE - else - dynamic_load(err_id, std::forward(e)); -#endif + if( slot * p = get_slot() ) + (void) p->load(err_id, std::forward(e)); return 0; } - template - BOOST_LEAF_CONSTEXPR inline int load_slot_deferred( int err_id, F && f ) noexcept(OnError) + template + BOOST_LEAF_CONSTEXPR inline int load_slot_deferred( int err_id, F && f ) { using E = typename function_traits::return_type; - using T = typename std::decay::type; - static_assert(!std::is_pointer::value, "Error objects of pointer types are not allowed"); - static_assert(!std::is_same::value, "Error objects of type error_id are not allowed"); + using E_decayed = typename std::decay::type; BOOST_LEAF_ASSERT((err_id&3) == 1); - if( slot * p = tls::read_ptr>() ) - { - if( !OnError || !p->has_value(err_id) ) - (void) p->load(err_id, std::forward(f)()); - } -#if BOOST_LEAF_CFG_CAPTURE - else - dynamic_load(err_id, std::forward(f)()); -#endif + if( slot * p = get_slot() ) + (void) p->load(err_id, std::forward(f)()); return 0; } - template - BOOST_LEAF_CONSTEXPR inline int load_slot_accumulate( int err_id, F && f ) noexcept(OnError) + template + BOOST_LEAF_CONSTEXPR inline int load_slot_accumulate( int err_id, F && f ) { static_assert(function_traits::arity == 1, "Lambdas passed to accumulate must take a single e-type argument by reference"); - using E = typename std::decay>::type; - using T = typename std::decay::type; - static_assert(!std::is_pointer::value, "Error objects of pointer types are not allowed"); + using E = fn_arg_type; + using E_decayed = typename std::decay::type; BOOST_LEAF_ASSERT((err_id&3) == 1); - if( auto sl = tls::read_ptr>() ) - { - if( auto v = sl->has_value(err_id) ) + if( slot * p = get_slot() ) + if( E_decayed * v = p->has_value(err_id) ) (void) std::forward(f)(*v); else - (void) std::forward(f)(sl->load(err_id,E())); - } -#if BOOST_LEAF_CFG_CAPTURE - else - dynamic_load_accumulate(err_id, std::forward(f)); -#endif + (void) std::forward(f)(p->load(err_id, E_decayed())); return 0; } -} +} // namespace detail //////////////////////////////////////// @@ -500,27 +536,27 @@ namespace detail template struct load_item { - BOOST_LEAF_CONSTEXPR static int load_( int err_id, E && e ) noexcept + BOOST_LEAF_CONSTEXPR static int load_( int err_id, E && e ) { - return load_slot(err_id, std::forward(e)); + return load_slot(err_id, std::forward(e)); } }; template struct load_item { - BOOST_LEAF_CONSTEXPR static int load_( int err_id, F && f ) noexcept + BOOST_LEAF_CONSTEXPR static int load_( int err_id, F && f ) { - return load_slot_deferred(err_id, std::forward(f)); + return load_slot_deferred(err_id, std::forward(f)); } }; template struct load_item { - BOOST_LEAF_CONSTEXPR static int load_( int err_id, F && f ) noexcept + BOOST_LEAF_CONSTEXPR static int load_( int err_id, F && f ) { - return load_slot_accumulate(err_id, std::forward(f)); + return load_slot_accumulate(err_id, std::forward(f)); } }; } @@ -529,38 +565,63 @@ namespace detail namespace detail { - struct BOOST_LEAF_SYMBOL_VISIBLE tls_tag_id_factory_current_id; - - template - struct BOOST_LEAF_SYMBOL_VISIBLE id_factory +#if BOOST_LEAF_CFG_CAPTURE + class preloaded_base { - static atomic_unsigned_int counter; + protected: - BOOST_LEAF_CONSTEXPR static unsigned generate_next_id() noexcept + preloaded_base() noexcept: + next_( + []( preloaded_base * this_ ) -> preloaded_base * + { + if( dynamic_allocator * da = get_dynamic_allocator() ) + return da->link_preloaded_item(this_); + return nullptr; + }(this)) { - auto id = (counter+=4); - BOOST_LEAF_ASSERT((id&3) == 1); - return id; } - }; - template - atomic_unsigned_int id_factory::counter(1); + ~preloaded_base() noexcept + { + if( dynamic_allocator * da = get_dynamic_allocator() ) + da->unlink_preloaded_item(next_); + else + BOOST_LEAF_ASSERT(next_ == nullptr); + } + + public: + + preloaded_base * const next_; + + virtual void reserve( dynamic_allocator & ) const = 0; + }; +#endif // #if BOOST_LEAF_CFG_CAPTURE inline int current_id() noexcept { - unsigned id = tls::read_uint(); + unsigned id = tls::read_current_error_id(); BOOST_LEAF_ASSERT(id == 0 || (id&3) == 1); return int(id); } inline int new_id() noexcept { - unsigned id = id_factory<>::generate_next_id(); - tls::write_uint(id); + unsigned id = tls::generate_next_error_id(); + tls::write_current_error_id(id); return int(id); } + inline int start_new_error() + { + int id = new_id(); +#if BOOST_LEAF_CFG_CAPTURE + if( dynamic_allocator * da = get_dynamic_allocator() ) + for( preloaded_base const * e = da->preloaded_list(); e; e = e->next_ ) + e->reserve(*da); +#endif + return id; + } + struct inject_loc { char const * file; @@ -568,26 +629,27 @@ namespace detail char const * fn; template - friend T operator+( inject_loc loc, T && x ) noexcept + friend T operator+( inject_loc loc, T && x ) noexcept(!BOOST_LEAF_CFG_CAPTURE) { x.load_source_location_(loc.file, loc.line, loc.fn); return std::move(x); } }; -} +} // namespace detail #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR namespace detail { - class leaf_error_category final: public std::error_category + class leaf_error_category final: + public std::error_category { - bool equivalent( int, std::error_condition const & ) const noexcept final override { return false; } - bool equivalent( std::error_code const &, int ) const noexcept final override { return false; } - char const * name() const noexcept final override { return "LEAF error"; } - std::string message( int ) const final override { return name(); } + bool equivalent( int, std::error_condition const & ) const noexcept override { return false; } + bool equivalent( std::error_code const &, int ) const noexcept override { return false; } + char const * name() const noexcept override { return "LEAF error"; } + std::string message( int ) const override { return name(); } public: - ~leaf_error_category() noexcept final override { } + ~leaf_error_category() noexcept override { } }; template @@ -599,7 +661,7 @@ namespace detail template leaf_error_category get_leaf_error_category::cat; - inline int import_error_code( std::error_code const & ec ) noexcept + inline int import_error_code( std::error_code const & ec ) noexcept(!BOOST_LEAF_CFG_CAPTURE) { if( int err_id = ec.value() ) { @@ -611,15 +673,15 @@ namespace detail } else { - err_id = new_id(); - (void) load_slot(err_id, ec); + err_id = start_new_error(); + (void) load_slot(err_id, ec); return (err_id&~3)|1; } } else return 0; } -} +} // namespace detail inline bool is_error_id( std::error_code const & ec ) noexcept { @@ -628,7 +690,7 @@ inline bool is_error_id( std::error_code const & ec ) noexcept return res; } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR //////////////////////////////////////// @@ -637,7 +699,7 @@ namespace detail BOOST_LEAF_CONSTEXPR error_id make_error_id(int) noexcept; } -class BOOST_LEAF_SYMBOL_VISIBLE error_id +class error_id { friend error_id BOOST_LEAF_CONSTEXPR detail::make_error_id(int) noexcept; @@ -657,14 +719,14 @@ public: } #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR - explicit error_id( std::error_code const & ec ) noexcept: + explicit error_id( std::error_code const & ec ) noexcept(!BOOST_LEAF_CFG_CAPTURE): value_(detail::import_error_code(std::error_code(ec))) { BOOST_LEAF_ASSERT(!value_ || ((value_&3) == 1)); } template - error_id( Enum e, typename std::enable_if::value, int>::type = 0 ) noexcept: + error_id( Enum e, typename std::enable_if::value, int>::type = 0 ) noexcept(!BOOST_LEAF_CFG_CAPTURE): value_(detail::import_error_code(e)) { } @@ -674,7 +736,7 @@ public: { return std::error_code(value_, detail::get_leaf_error_category<>::cat); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR BOOST_LEAF_CONSTEXPR error_id load() const noexcept { @@ -682,7 +744,7 @@ public: } template - BOOST_LEAF_CONSTEXPR error_id load(Item && item) const noexcept + BOOST_LEAF_CONSTEXPR error_id load(Item && item) const { if (int err_id = value()) { @@ -693,7 +755,7 @@ public: } template - BOOST_LEAF_CONSTEXPR error_id load( Item && ... item ) const noexcept + BOOST_LEAF_CONSTEXPR error_id load( Item && ... item ) const { if( int err_id = value() ) { @@ -735,7 +797,7 @@ public: return os << (x.value_ / 4); } - BOOST_LEAF_CONSTEXPR void load_source_location_( char const * file, int line, char const * function ) const noexcept + BOOST_LEAF_CONSTEXPR void load_source_location_( char const * file, int line, char const * function ) const noexcept(!BOOST_LEAF_CFG_CAPTURE) { BOOST_LEAF_ASSERT(file&&*file); BOOST_LEAF_ASSERT(line>0); @@ -743,7 +805,7 @@ public: BOOST_LEAF_ASSERT(value_); (void) load(e_source_location {file,line,function}); } -}; +}; // class error_id namespace detail { @@ -754,15 +816,15 @@ namespace detail } } -inline error_id new_error() noexcept +inline error_id new_error() { - return detail::make_error_id(detail::new_id()); + return detail::make_error_id(detail::start_new_error()); } template -inline error_id new_error( Item && ... item ) noexcept +inline error_id new_error( Item && ... item ) { - return detail::make_error_id(detail::new_id()).load(std::forward(item)...); + return detail::make_error_id(detail::start_new_error()).load(std::forward(item)...); } inline error_id current_error() noexcept @@ -782,6 +844,6 @@ struct is_result_type: is_result_type { }; -} } +} } // namespace boost::leaf -#endif // BOOST_LEAF_ERROR_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_ERROR_HPP_INCLUDED diff --git a/include/boost/leaf/exception.hpp b/include/boost/leaf/exception.hpp index c022457..36fb6fd 100644 --- a/include/boost/leaf/exception.hpp +++ b/include/boost/leaf/exception.hpp @@ -1,52 +1,14 @@ #ifndef BOOST_LEAF_EXCEPTION_HPP_INCLUDED #define BOOST_LEAF_EXCEPTION_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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 -#ifdef BOOST_LEAF_NO_EXCEPTIONS - -namespace boost -{ - [[noreturn]] void throw_exception( std::exception const & ); // user defined -} - -namespace boost { namespace leaf { - -namespace detail -{ - template - [[noreturn]] void throw_exception_impl( T && e ) - { - ::boost::throw_exception(std::move(e)); - } -} - -} } - -#else - -namespace boost { namespace leaf { - -namespace detail -{ - template - [[noreturn]] void throw_exception_impl( T && e ) - { - throw std::move(e); - } -} - -} } - -#endif - //////////////////////////////////////// #define BOOST_LEAF_THROW_EXCEPTION ::boost::leaf::detail::throw_with_loc{__FILE__,__LINE__,__FUNCTION__}+::boost::leaf::detail::make_exception @@ -65,7 +27,7 @@ namespace detail [[noreturn]] friend void operator+( throw_with_loc loc, Ex && ex ) { ex.load_source_location_(loc.file, loc.line, loc.fn); - ::boost::leaf::detail::throw_exception_impl(std::move(ex)); + ::boost::leaf::throw_exception_(std::move(ex)); } }; } @@ -77,7 +39,7 @@ namespace detail inline void enforce_std_exception( std::exception const & ) noexcept { } template - class BOOST_LEAF_SYMBOL_VISIBLE exception: + class exception final: public Ex, public exception_base, public error_id @@ -86,17 +48,17 @@ namespace detail bool is_current_exception() const noexcept { - return tls::read_uint() == unsigned(error_id::value()); + return tls::read_current_error_id() == unsigned(error_id::value()); } - error_id get_error_id() const noexcept final override + error_id get_error_id() const noexcept override { clear_current_error_ = false; return *this; } #if BOOST_LEAF_CFG_DIAGNOSTICS && !defined(BOOST_LEAF_NO_EXCEPTIONS) - void print_type_name(std::ostream & os) const final override + void print_type_name(std::ostream & os) const override { detail::demangle_and_print(os, typeid(Ex).name()); } @@ -148,9 +110,9 @@ namespace detail ~exception() noexcept { if( clear_current_error_ && is_current_exception() ) - tls::write_uint(0); + tls::write_current_error_id(0); } - }; + }; // template exception template struct at_least_one_derives_from_std_exception; @@ -167,7 +129,7 @@ namespace detail template inline typename std::enable_if::type>::value, exception::type>>::type - make_exception( error_id err, Ex && ex, E && ... e ) noexcept + make_exception( error_id err, Ex && ex, E && ... e ) noexcept(!BOOST_LEAF_CFG_CAPTURE) { static_assert(!at_least_one_derives_from_std_exception::value, "Error objects passed to leaf::exception may not derive from std::exception"); return exception::type>( err.load(std::forward(e)...), std::forward(ex) ); @@ -176,7 +138,7 @@ namespace detail template inline typename std::enable_if::type>::value, exception>::type - make_exception( error_id err, E1 && car, E && ... cdr ) noexcept + make_exception( error_id err, E1 && car, E && ... cdr ) noexcept(!BOOST_LEAF_CFG_CAPTURE) { static_assert(!at_least_one_derives_from_std_exception::value, "Error objects passed to leaf::exception may not derive from std::exception"); return exception( err.load(std::forward(car), std::forward(cdr)...) ); @@ -190,7 +152,7 @@ namespace detail template inline typename std::enable_if::type>::value, exception::type>>::type - make_exception( Ex && ex, E && ... e ) noexcept + make_exception( Ex && ex, E && ... e ) noexcept(!BOOST_LEAF_CFG_CAPTURE) { static_assert(!at_least_one_derives_from_std_exception::value, "Error objects passed to leaf::exception may not derive from std::exception"); return exception::type>( new_error().load(std::forward(e)...), std::forward(ex) ); @@ -199,7 +161,7 @@ namespace detail template inline typename std::enable_if::type>::value, exception>::type - make_exception( E1 && car, E && ... cdr ) noexcept + make_exception( E1 && car, E && ... cdr ) noexcept(!BOOST_LEAF_CFG_CAPTURE) { static_assert(!at_least_one_derives_from_std_exception::value, "Error objects passed to leaf::exception may not derive from std::exception"); return exception( new_error().load(std::forward(car), std::forward(cdr)...) ); @@ -209,7 +171,7 @@ namespace detail { return exception(leaf::new_error()); } -} +} // namespace detail template [[noreturn]] void throw_exception( E && ... e ) @@ -217,17 +179,14 @@ template // Warning: setting a breakpoint here will not intercept exceptions thrown // via BOOST_LEAF_THROW_EXCEPTION or originating in the few other throw // points elsewhere in LEAF. To intercept all of those exceptions as well, - // set a breakpoint inside boost::leaf::detail::throw_exception_impl. - detail::throw_exception_impl(detail::make_exception(std::forward(e)...)); + // set a breakpoint inside boost::leaf::throw_exception_. + throw_exception_(detail::make_exception(std::forward(e)...)); } //////////////////////////////////////// #ifndef BOOST_LEAF_NO_EXCEPTIONS -template -class BOOST_LEAF_SYMBOL_VISIBLE result; - namespace detail { inline error_id catch_exceptions_helper( std::exception const &, leaf_detail_mp11::mp_list<> ) @@ -258,12 +217,12 @@ namespace detail template using deduce_exception_to_result_return_type = typename deduce_exception_to_result_return_type_impl::type; -} +} // namespace detail template inline detail::deduce_exception_to_result_return_type> -exception_to_result( F && f ) noexcept +exception_to_result( F && f ) noexcept(!BOOST_LEAF_CFG_CAPTURE) { try { @@ -279,8 +238,8 @@ exception_to_result( F && f ) noexcept } } -#endif +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS -} } +} } // namespace boost::leaf -#endif // BOOST_LEAF_EXCEPTION_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_EXCEPTION_HPP_INCLUDED diff --git a/include/boost/leaf/handle_errors.hpp b/include/boost/leaf/handle_errors.hpp index d0ba7b4..6552e12 100644 --- a/include/boost/leaf/handle_errors.hpp +++ b/include/boost/leaf/handle_errors.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_LEAF_HANDLE_ERRORS_HPP_INCLUDED #define BOOST_LEAF_HANDLE_ERRORS_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) @@ -11,9 +11,6 @@ namespace boost { namespace leaf { -template -class BOOST_LEAF_SYMBOL_VISIBLE result; - //////////////////////////////////////// #ifndef BOOST_LEAF_NO_EXCEPTIONS @@ -30,11 +27,11 @@ namespace detail } } -#endif +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS //////////////////////////////////////// -class BOOST_LEAF_SYMBOL_VISIBLE error_info +class error_info { error_info & operator=( error_info const & ) = delete; @@ -97,7 +94,7 @@ public: detail::demangle_and_print(os, typeid(*ex_).name()); os << ": \"" << ex_->what() << '"'; } -#endif +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS } template @@ -106,7 +103,7 @@ public: x.print_error_info(os); return os << '\n'; } -}; +}; // class error_info namespace detail { @@ -202,7 +199,7 @@ namespace detail return nullptr; } }; -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR template struct peek_exception @@ -222,7 +219,7 @@ namespace detail } }; -#endif +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS template ::value> struct peek_tuple; @@ -292,7 +289,7 @@ namespace detail } return nullptr; } -} +} // namespace detail //////////////////////////////////////// @@ -347,7 +344,7 @@ namespace detail return handler_argument_traits::check(tup, ei) && check_arguments::check(tup, ei); } }; -} +} // namespace detail //////////////////////////////////////// @@ -472,7 +469,7 @@ namespace detail std::forward(car), std::forward(cdr)...); } -} +} // namespace detail //////////////////////////////////////// @@ -570,7 +567,7 @@ try_catch( TryBlock && try_block, H && ... ) noexcept return std::forward(try_block)(); } -#else +#else // #ifdef BOOST_LEAF_NO_EXCEPTIONS namespace detail { @@ -610,7 +607,7 @@ namespace detail } ); } } -} +} // namespace detail template inline @@ -701,7 +698,7 @@ try_catch( TryBlock && try_block, H && ... h ) } } -#endif +#endif // #else (#ifdef BOOST_LEAF_NO_EXCEPTIONS) #if BOOST_LEAF_CFG_CAPTURE @@ -716,7 +713,7 @@ namespace detail inline static leaf_result - try_capture_all_( TryBlock && try_block ) noexcept + try_capture_all_( TryBlock && try_block ) { detail::slot sl; sl.activate(); @@ -733,7 +730,7 @@ namespace detail { sl.deactivate(); int const err_id = error_id(r.error()).value(); - return leaf_result(sl.value_or_default(err_id).template extract_capture_list(err_id)); + return leaf_result(sl.get().template extract_capture_list(err_id)); } } #ifndef BOOST_LEAF_NO_EXCEPTIONS @@ -741,15 +738,15 @@ namespace detail { sl.deactivate(); int err_id = unpack_error_id(ex).value(); - return sl.value_or_default(err_id).template extract_capture_list(err_id); + return sl.get().template extract_capture_list(err_id); } catch(...) { sl.deactivate(); int err_id = current_error().value(); - return sl.value_or_default(err_id).template extract_capture_list(err_id); + return sl.get().template extract_capture_list(err_id); } -#endif +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS } }; @@ -777,7 +774,7 @@ namespace detail inline static leaf_result - try_capture_all_( TryBlock && try_block ) noexcept + try_capture_all_( TryBlock && try_block ) { detail::slot sl; sl.activate(); @@ -793,18 +790,18 @@ namespace detail { sl.deactivate(); int err_id = unpack_error_id(ex).value(); - return sl.value_or_default(err_id).template extract_capture_list(err_id); + return sl.get().template extract_capture_list(err_id); } catch(...) { sl.deactivate(); int err_id = current_error().value(); - return sl.value_or_default(err_id).template extract_capture_list(err_id); + return sl.get().template extract_capture_list(err_id); } -#endif +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS } }; -} +} // namespace detail template inline @@ -813,9 +810,10 @@ try_capture_all( TryBlock && try_block ) noexcept { return detail::try_capture_all_dispatch()())>::try_capture_all_(std::forward(try_block)); } -#endif -} } +#endif // #if BOOST_LEAF_CFG_CAPTURE + +} } // namespace boost::leaf // Boost Exception Integration @@ -875,8 +873,8 @@ namespace detail template struct handler_argument_traits const *>: handler_argument_traits_require_by_value> { }; template struct handler_argument_traits &>: handler_argument_traits_require_by_value> { }; template struct handler_argument_traits *>: handler_argument_traits_require_by_value> { }; -} +} // namespace detail -} } +} } // namespace boost::leaf -#endif // BOOST_LEAF_HANDLE_ERRORS_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_HANDLE_ERRORS_HPP_INCLUDED diff --git a/include/boost/leaf/on_error.hpp b/include/boost/leaf/on_error.hpp index a01bce5..79b079e 100644 --- a/include/boost/leaf/on_error.hpp +++ b/include/boost/leaf/on_error.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_LEAF_ON_ERROR_HPP_INCLUDED #define BOOST_LEAF_ON_ERROR_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) @@ -40,7 +40,7 @@ public: # else if( std::uncaught_exception() ) # endif - return detail::new_id(); + return detail::start_new_error(); #endif return 0; } @@ -64,7 +64,7 @@ public: { return detail::make_error_id(get_id()); } -}; +}; // class error_monitor //////////////////////////////////////// @@ -79,19 +79,31 @@ namespace detail tuple_for_each_preload::trigger(tup,err_id); std::get(tup).trigger(err_id); } + +#if BOOST_LEAF_CFG_CAPTURE + static void reserve( Tup const & tup, dynamic_allocator & da ) + { + tuple_for_each_preload::reserve(tup,da); + std::get(tup).reserve(da); + } +#endif }; template struct tuple_for_each_preload<0, Tup> { BOOST_LEAF_CONSTEXPR static void trigger( Tup const &, int ) noexcept { } + +#if BOOST_LEAF_CFG_CAPTURE + static void reserve( Tup const &, dynamic_allocator & ) { } +#endif }; template class preloaded_item { - using decay_E = typename std::decay::type; - decay_E e_; + using E_decayed = typename std::decay::type; + E_decayed e_; public: @@ -102,26 +114,57 @@ namespace detail BOOST_LEAF_CONSTEXPR void trigger( int err_id ) noexcept { - (void) load_slot(err_id, std::move(e_)); + if( slot * p = tls::read_ptr>() ) + if( !p->has_value(err_id) ) + (void) p->load(err_id, std::move(e_)); } + +#if BOOST_LEAF_CFG_CAPTURE + void reserve( dynamic_allocator & da ) const + { + da.reserve(); + } +#endif }; template ::return_type> class deferred_item { + using E_decayed = typename std::decay::type; F f_; public: - BOOST_LEAF_CONSTEXPR deferred_item( F && f ) noexcept: + BOOST_LEAF_CONSTEXPR deferred_item( F && f ): f_(std::forward(f)) { } - BOOST_LEAF_CONSTEXPR void trigger( int err_id ) noexcept + void trigger( int err_id ) noexcept { - (void) load_slot_deferred(err_id, f_); + if( slot * p = tls::read_ptr>() ) + if( !p->has_value(err_id) ) + { +#ifndef BOOST_LEAF_NO_EXCEPTIONS + try + { +#endif + (void) p->load(err_id, f_()); +#ifndef BOOST_LEAF_NO_EXCEPTIONS + } + catch(...) + { + } +#endif + } } + +#if BOOST_LEAF_CFG_CAPTURE + void reserve( dynamic_allocator & da ) const + { + da.reserve(); + } +#endif }; template @@ -136,10 +179,26 @@ namespace detail { } - BOOST_LEAF_CONSTEXPR void trigger( int ) noexcept + void trigger( int ) noexcept { - f_(); +#ifndef BOOST_LEAF_NO_EXCEPTIONS + try + { +#endif + f_(); +#ifndef BOOST_LEAF_NO_EXCEPTIONS + } + catch(...) + { + } +#endif } + +#if BOOST_LEAF_CFG_CAPTURE + void reserve( dynamic_allocator & ) const + { + } +#endif }; template , int arity = function_traits::arity> @@ -148,50 +207,89 @@ namespace detail template class accumulating_item { + using E_decayed = typename std::decay::type; F f_; public: - BOOST_LEAF_CONSTEXPR accumulating_item( F && f ) noexcept: + BOOST_LEAF_CONSTEXPR accumulating_item( F && f ): f_(std::forward(f)) { } - BOOST_LEAF_CONSTEXPR void trigger( int err_id ) noexcept + void trigger( int err_id ) noexcept { - load_slot_accumulate(err_id, std::move(f_)); + if( slot * p = tls::read_ptr>() ) + { +#ifndef BOOST_LEAF_NO_EXCEPTIONS + try + { +#endif + if( E_decayed * v = p->has_value(err_id) ) + (void) std::move(f_)(*v); + else + (void) std::move(f_)(p->load(err_id, E_decayed())); +#ifndef BOOST_LEAF_NO_EXCEPTIONS + } + catch(...) + { + } +#endif + } } + +#if BOOST_LEAF_CFG_CAPTURE + void reserve( dynamic_allocator & da ) const + { + da.reserve(); + } +#endif }; template class preloaded +#if BOOST_LEAF_CFG_CAPTURE + : preloaded_base +#endif { + preloaded( preloaded const & ) = delete; preloaded & operator=( preloaded const & ) = delete; std::tuple p_; - bool moved_; error_monitor id_; +#if __cplusplus < 201703L + bool moved_ = false; +#endif + +#if BOOST_LEAF_CFG_CAPTURE + void reserve( dynamic_allocator & da ) const override + { + tuple_for_each_preload::reserve(p_,da); + } +#endif public: BOOST_LEAF_CONSTEXPR explicit preloaded( Item && ... i ): - p_(std::forward(i)...), - moved_(false) + p_(std::forward(i)...) { } +#if __cplusplus < 201703L BOOST_LEAF_CONSTEXPR preloaded( preloaded && x ) noexcept: p_(std::move(x.p_)), - moved_(false), id_(std::move(x.id_)) { x.moved_ = true; } +#endif ~preloaded() noexcept { +#if __cplusplus < 201703L if( moved_ ) return; +#endif if( auto id = id_.check_id() ) tuple_for_each_preload::trigger(p_,id); } @@ -217,7 +315,7 @@ namespace detail { using type = accumulating_item; }; -} +} // namespace detail template BOOST_LEAF_ATTRIBUTE_NODISCARD BOOST_LEAF_CONSTEXPR inline @@ -227,6 +325,6 @@ on_error( Item && ... i ) return detail::preloaded::type...>(std::forward(i)...); } -} } +} } // namespace boost::leaf -#endif // BOOST_LEAF_ON_ERROR_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_ON_ERROR_HPP_INCLUDED diff --git a/include/boost/leaf/pred.hpp b/include/boost/leaf/pred.hpp index 136368a..a00e861 100644 --- a/include/boost/leaf/pred.hpp +++ b/include/boost/leaf/pred.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_LEAF_PRED_HPP_INCLUDED #define BOOST_LEAF_PRED_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) @@ -33,7 +33,7 @@ namespace detail BOOST_LEAF_ASSERT(P != nullptr); return P(e); } -#endif +#endif // #if __cplusplus >= 201703L template BOOST_LEAF_CONSTEXPR BOOST_LEAF_ALWAYS_INLINE bool cmp_value_pack( MatchType const & e, V v ) @@ -46,7 +46,7 @@ namespace detail { return cmp_value_pack(e, car) || cmp_value_pack(e, cdr...); } -} +} // namespace detail //////////////////////////////////////// @@ -71,7 +71,7 @@ BOOST_LEAF_CONSTEXPR inline bool category( std::error_code const & ec ) return &ec.category() == &std::error_code(ErrorCodeEnum{}).category(); } #endif -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR //////////////////////////////////////// @@ -95,7 +95,7 @@ namespace detail { static_assert(sizeof(Enum) == 0, "leaf::condition should be used with leaf::match_value<>, not with leaf::match<>"); }; -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR } template , V1, V)> @@ -123,7 +123,7 @@ struct match, V1, V...> return detail::cmp_value_pack(e, V1, V...); } }; -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR template , V1, V)> struct is_predicate>: std::true_type @@ -152,7 +152,7 @@ namespace detail { static_assert(sizeof(Enum) == 0, "leaf::condition should be used with leaf::match<>, not with leaf::match_value<>"); }; -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR } template , V1, V)> @@ -179,7 +179,7 @@ struct match_value, V1, V...> return detail::cmp_value_pack(e.value, V1, V...); } }; -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR template , V1, V)> struct is_predicate>: std::true_type @@ -208,7 +208,7 @@ template struct is_predicate>: std::true_type { }; -#endif +#endif // #if __cplusplus >= 201703L //////////////////////////////////////// @@ -289,8 +289,8 @@ struct is_predicate>: std::true_type { }; -#endif +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS -} } +} } // namespace boost::leaf -#endif // BOOST_LEAF_PRED_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_PRED_HPP_INCLUDED diff --git a/include/boost/leaf/result.hpp b/include/boost/leaf/result.hpp index 55dffab..20d7cac 100644 --- a/include/boost/leaf/result.hpp +++ b/include/boost/leaf/result.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_LEAF_RESULT_HPP_INCLUDED #define BOOST_LEAF_RESULT_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) @@ -10,19 +10,16 @@ #include #include -#include #include namespace boost { namespace leaf { -namespace detail { class dynamic_allocator; } - //////////////////////////////////////// class bad_result: public std::exception { - char const * what() const noexcept final override + char const * what() const noexcept override { return "boost::leaf::bad_result"; } @@ -60,7 +57,7 @@ namespace detail { result_value_printer::print(s, x); } -} +} // namespace detail //////////////////////////////////////// @@ -158,20 +155,19 @@ namespace detail BOOST_LEAF_ASSERT(kind() == err_id_zero || kind() == err_id || kind() == err_id_capture_list); return make_error_id(int((state_&~3)|1)); } - }; -} + }; // class result_discriminant +} // namespace detail //////////////////////////////////////// template -class BOOST_LEAF_SYMBOL_VISIBLE BOOST_LEAF_ATTRIBUTE_NODISCARD result +class BOOST_LEAF_ATTRIBUTE_NODISCARD result { template friend class result; - friend class detail::dynamic_allocator; - #if BOOST_LEAF_CFG_CAPTURE + friend class detail::dynamic_allocator; using capture_list = detail::capture_list; #endif @@ -223,6 +219,7 @@ class BOOST_LEAF_SYMBOL_VISIBLE BOOST_LEAF_ATTRIBUTE_NODISCARD result #endif default: BOOST_LEAF_ASSERT(k == result_discriminant::err_id); + (void) k; case result_discriminant::err_id_zero: return result(what.get_error_id()); } @@ -243,6 +240,7 @@ class BOOST_LEAF_SYMBOL_VISIBLE BOOST_LEAF_ATTRIBUTE_NODISCARD result { default: BOOST_LEAF_ASSERT(k == result_discriminant::err_id); + (void) k; case result_discriminant::err_id_zero: break; case result_discriminant::err_id_capture_list: @@ -265,6 +263,7 @@ class BOOST_LEAF_SYMBOL_VISIBLE BOOST_LEAF_ATTRIBUTE_NODISCARD result { default: BOOST_LEAF_ASSERT(k == result_discriminant::err_id); + (void) k; case result_discriminant::err_id_zero: break; case result_discriminant::err_id_capture_list: @@ -412,7 +411,7 @@ public: { } -#else +#else // #if defined(BOOST_STRICT_CONFIG) || !defined(__clang__) private: static int init_T_with_A( T && ); @@ -426,7 +425,7 @@ public: { } -#endif +#endif // #else (#if defined(BOOST_STRICT_CONFIG) || !defined(__clang__)) #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR result( std::error_code const & ec ) noexcept: @@ -439,7 +438,7 @@ public: what_(error_id(e)) { } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR ~result() noexcept { @@ -490,7 +489,7 @@ public: return stored_; } -#else +#else // #ifdef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS value_cref value() const & { @@ -516,7 +515,7 @@ public: return std::move(stored_); } -#endif +#endif // #else (#ifdef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS) value_no_ref_const * operator->() const noexcept { @@ -544,7 +543,7 @@ public: return *p; } -#else +#else // #ifdef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS value_cref operator*() const & noexcept { @@ -574,7 +573,7 @@ public: return std::move(*p); } -#endif +#endif // #else (#ifdef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS) error_result error() noexcept { @@ -582,7 +581,7 @@ public: } template - error_id load( Item && ... item ) noexcept + error_id load( Item && ... item ) noexcept(!BOOST_LEAF_CFG_CAPTURE) { return error_id(error()).load(std::forward(item)...); } @@ -604,7 +603,7 @@ public: r.print_error_result(os); return os; } -}; +}; // template result //////////////////////////////////////// @@ -614,19 +613,19 @@ namespace detail } template <> -class BOOST_LEAF_SYMBOL_VISIBLE BOOST_LEAF_ATTRIBUTE_NODISCARD result: +class BOOST_LEAF_ATTRIBUTE_NODISCARD result: result { template friend class result; - friend class detail::dynamic_allocator; - using result_discriminant = detail::result_discriminant; using void_ = detail::void_; using base = result; #if BOOST_LEAF_CFG_CAPTURE + friend class detail::dynamic_allocator; + result( int err_id, detail::capture_list && cap ) noexcept: base(err_id, std::move(cap)) { @@ -663,7 +662,7 @@ public: base(e) { } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR ~result() noexcept { @@ -712,7 +711,7 @@ public: using base::error; using base::load; using base::unload; -}; +}; // result specialization for void //////////////////////////////////////// @@ -724,6 +723,6 @@ struct is_result_type>: std::true_type { }; -} } +} } // namespace boost::leaf -#endif // BOOST_LEAF_RESULT_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_RESULT_HPP_INCLUDED diff --git a/include/boost/leaf/to_variant.hpp b/include/boost/leaf/to_variant.hpp index 2acd1b0..7f20c47 100644 --- a/include/boost/leaf/to_variant.hpp +++ b/include/boost/leaf/to_variant.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_LEAF_TO_VARIANT_HPP_INCLUDED #define BOOST_LEAF_TO_VARIANT_HPP_INCLUDED -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 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) @@ -42,8 +42,8 @@ to_variant( TryBlock && try_block ) } ); } -} } +} } // namespace boost::leaf -#endif +#endif // #if __cplusplus >= 201703L -#endif // BOOST_LEAF_TO_VARIANT_HPP_INCLUDED +#endif // #ifndef BOOST_LEAF_TO_VARIANT_HPP_INCLUDED diff --git a/meson.build b/meson.build index 8b62aa8..b0250da 100644 --- a/meson.build +++ b/meson.build @@ -1,13 +1,12 @@ -# Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +# Copyright 2018-2025 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_single_header = get_option('single_header') -option_boost = get_option('leaf_boost_examples') -option_lua = get_option('leaf_lua_examples') +option_single_header = get_option('leaf_single_header') +option_boost_available = get_option('leaf_boost_available') option_diagnostics = get_option('leaf_diagnostics') option_capture = get_option('leaf_capture') option_exceptions = (get_option('cpp_eh')!='none') @@ -15,15 +14,6 @@ option_enable_unit_tests = get_option('leaf_enable_unit_tests') option_enable_examples = get_option('leaf_enable_examples') 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 - if target_machine.system() == 'windows' add_global_arguments('-D_CRT_SECURE_NO_WARNINGS', language: 'cpp') endif @@ -31,7 +21,7 @@ endif compiler = meson.get_compiler('cpp') compiler_id = compiler.get_id() if not meson.is_subproject() - if option_boost + if option_boost_available add_global_arguments( '-DBOOST_LEAF_BOOST_AVAILABLE', language:'cpp' ) @@ -88,12 +78,12 @@ if not meson.is_subproject() endif dep_boost = [ ] -if option_boost # Requires that LEAF resides under boost_root/libs/leaf. +if option_boost_available # Requires that LEAF resides under boost_root/libs/leaf. dep_boost = declare_dependency(include_directories: '../..') endif dep_lua = [ ] -if option_lua +if option_enable_examples dep_lua = subproject('lua').get_variable('all') endif @@ -139,7 +129,6 @@ if option_enable_unit_tests 'diagnostics_test5', 'diagnostics_test6', 'e_errno_test', - 'e_LastError_test', 'error_code_test', 'error_id_test', 'exception_test', @@ -163,6 +152,7 @@ if option_enable_unit_tests 'on_error_accumulate_nested_new_error_result_test', 'on_error_accumulate_nested_success_exception_test', 'on_error_accumulate_nested_success_result_test', + 'on_error_alloc_fail_test', 'on_error_defer_basic_test', 'on_error_defer_nested_error_exception_test', 'on_error_defer_nested_error_result_test', @@ -170,6 +160,9 @@ if option_enable_unit_tests 'on_error_defer_nested_new_error_result_test', 'on_error_defer_nested_success_exception_test', 'on_error_defer_nested_success_result_test', + 'on_error_dynamic_reserve_test1', + 'on_error_dynamic_reserve_test2', + 'on_error_dynamic_reserve_test3', 'on_error_preload_basic_test', 'on_error_preload_exception_test', 'on_error_preload_nested_error_exception_test', @@ -198,7 +191,7 @@ if option_enable_unit_tests 'try_catch_test', 'try_exception_and_result_test', ] - if option_boost and option_exceptions + if option_boost_available and option_exceptions tests += [ 'boost_exception_test' ] @@ -213,6 +206,11 @@ if option_enable_unit_tests test(t, executable(t, 'test/'+t+'.cpp', dependencies: [leaf, dep_thread, dep_boost, dep_test_single_header]) ) endforeach + if target_machine.system() == 'windows' + dep_e_LastError = declare_dependency(compile_args: ['-DBOOST_LEAF_CFG_WIN32=1']) + test('e_LastError_test', executable('e_LastError_test', 'test/e_LastError_test.cpp', dependencies: [leaf, dep_thread, dep_boost, dep_test_single_header, dep_e_LastError]) ) + endif + header_tests = [ '_hpp_common_test', '_hpp_config_test', @@ -231,6 +229,22 @@ if option_enable_unit_tests test(t, executable(t, 'test/'+t+'.cpp', dependencies: [leaf, dep_thread, dep_boost]) ) endforeach + # Shared library test + dep_so_dll = [] + if target_machine.system() == 'windows' + dep_so_dll = declare_dependency(compile_args: ['-DBOOST_LEAF_CFG_WIN32=2']) + endif + so_dll_test_lib1 = shared_library('so_dll_test_lib1', 'test/so_dll_lib1.cpp', dependencies: [leaf, dep_so_dll], gnu_symbol_visibility: 'hidden') + so_dll_test_lib2 = shared_library('so_dll_test_lib2', 'test/so_dll_lib2.cpp', dependencies: [leaf, dep_so_dll], gnu_symbol_visibility: 'hidden') + test('so_dll_test', executable('so_dll_test', 'test/so_dll_test.cpp', dependencies: [leaf, dep_so_dll], link_with: [so_dll_test_lib1, so_dll_test_lib2])) + + # Test that the DLL test works when linked statically as well. + # This is to verify that we don't get multiply defined symbol link errors when tls_win32.hpp is included in multiple compilation units.. + dep_so_dll_static = declare_dependency(compile_args: ['-DBOOST_LEAF_SO_DLL_TEST_STATIC']) + so_dll_static_lib1 = static_library('so_dll_static_lib1', 'test/so_dll_lib1.cpp', dependencies: [leaf, dep_so_dll, dep_so_dll_static]) + so_dll_static_lib2 = static_library('so_dll_static_lib2', 'test/so_dll_lib2.cpp', dependencies: [leaf, dep_so_dll, dep_so_dll_static]) + test('so_dll_static_test', executable('so_dll_static_test', 'test/so_dll_test.cpp', dependencies: [leaf, dep_so_dll, dep_so_dll_static], link_with: [so_dll_static_lib1, so_dll_static_lib2])) + endif ################################# @@ -241,7 +255,7 @@ if option_enable_examples if option_exceptions executable('print_file_exceptions', 'example/print_file/print_file_exceptions.cpp', dependencies: [leaf] ) endif - if option_boost + if option_boost_available executable('print_file_system_result', 'example/print_file/print_file_system_result.cpp', dependencies: [leaf, dep_boost] ) endif @@ -254,7 +268,7 @@ if option_enable_examples executable('exception_to_result', 'example/exception_to_result.cpp', dependencies: [leaf] ) endif - if option_lua + if option_enable_examples if option_exceptions executable('lua_callback_exceptions', 'example/lua_callback_exceptions.cpp', dependencies: [leaf, dep_lua] ) endif diff --git a/meson_options.txt b/meson_options.txt index 7b35317..f5a115b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,6 +1,5 @@ -option('single_header',type:'boolean',value:false,description:'Unit tests #include "leaf.hpp" instead of individual headers') -option('leaf_boost_examples',type:'boolean',value:false,description:'Builds the examples that use Boost') -option('leaf_lua_examples',type:'boolean',value:false,description:'Enable or disable downloading of Lua and building the Lua examples') +option('leaf_single_header',type:'boolean',value:false,description:'Unit tests #include "leaf.hpp" instead of individual headers') +option('leaf_boost_available',type:'boolean',value:false,description:'Set to true to enable tests and examples that require Boost') option('leaf_diagnostics',type:'integer',value:1,description:'BOOST_LEAF_CFG_DIAGNOSTICS value') option('leaf_capture',type:'integer',value:1,description:'BOOST_LEAF_CFG_CAPTURE value') option('leaf_embedded',type:'boolean',value:false,description:'Defines BOOST_LEAF_EMBEDDED') diff --git a/subprojects/.wraplock b/subprojects/.wraplock new file mode 100644 index 0000000..e69de29 diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 355d2ff..cff2af9 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -70,8 +70,8 @@ run diagnostics_test3.cpp ; run diagnostics_test4.cpp ; run diagnostics_test5.cpp ; run e_errno_test.cpp ; -run e_LastError_test.cpp ; -run error_code_test.cpp ; +run e_LastError_test.cpp : : : windows:BOOST_LEAF_CFG_WIN32=1 ; +run error_code_test.cpp : : : clang-darwin,off,off:"-Wl,-ld_classic" ; # workaround for macos-14 linker bug run error_id_test.cpp ; run exception_test.cpp ; run exception_to_result_test.cpp ; @@ -94,6 +94,7 @@ run on_error_accumulate_nested_new_error_exception_test.cpp ; run on_error_accumulate_nested_new_error_result_test.cpp ; run on_error_accumulate_nested_success_exception_test.cpp ; run on_error_accumulate_nested_success_result_test.cpp ; +run on_error_alloc_fail_test.cpp ; run on_error_defer_basic_test.cpp ; run on_error_defer_nested_error_exception_test.cpp ; run on_error_defer_nested_error_result_test.cpp ; @@ -101,6 +102,9 @@ run on_error_defer_nested_new_error_exception_test.cpp ; run on_error_defer_nested_new_error_result_test.cpp ; run on_error_defer_nested_success_exception_test.cpp ; run on_error_defer_nested_success_result_test.cpp ; +run on_error_dynamic_reserve_test1.cpp ; +run on_error_dynamic_reserve_test2.cpp ; +run on_error_dynamic_reserve_test3.cpp ; run on_error_preload_basic_test.cpp ; run on_error_preload_exception_test.cpp ; run on_error_preload_nested_error_exception_test.cpp ; @@ -129,9 +133,13 @@ run try_catch_system_error_test.cpp ; run try_catch_test.cpp ; run try_exception_and_result_test.cpp ; -lib visibility_test_lib : visibility_test_lib.cpp : hidden ; -run visibility_test.cpp visibility_test_lib/shared ; -run visibility_test.cpp visibility_test_lib/static : : : windows ; # This can't work on Windows, so use static link for the test. +lib so_dll_lib1 : so_dll_lib1.cpp : shared hidden windows:BOOST_LEAF_CFG_WIN32=2 : : windows:BOOST_LEAF_CFG_WIN32=2 ; +lib so_dll_lib2 : so_dll_lib2.cpp : shared hidden windows:BOOST_LEAF_CFG_WIN32=2 : : windows:BOOST_LEAF_CFG_WIN32=2 ; +run so_dll_test.cpp so_dll_lib1 so_dll_lib2 : : : windows:BOOST_LEAF_CFG_WIN32=2 ; + +lib so_dll_static_lib1 : so_dll_lib1.cpp : static BOOST_LEAF_SO_DLL_TEST_STATIC windows:BOOST_LEAF_CFG_WIN32=2 ; +lib so_dll_static_lib2 : so_dll_lib2.cpp : static BOOST_LEAF_SO_DLL_TEST_STATIC windows:BOOST_LEAF_CFG_WIN32=2 ; +run so_dll_test.cpp so_dll_static_lib1 so_dll_static_lib2 : : : BOOST_LEAF_SO_DLL_TEST_STATIC windows:BOOST_LEAF_CFG_WIN32=2 : so_dll_static_test ; compile-fail _compile-fail-arg_boost_error_info_1.cpp ; compile-fail _compile-fail-arg_boost_error_info_2.cpp ; diff --git a/test/capture_exception_async_test.cpp b/test/capture_exception_async_test.cpp index 21d681f..1d86830 100644 --- a/test/capture_exception_async_test.cpp +++ b/test/capture_exception_async_test.cpp @@ -155,4 +155,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #if defined(BOOST_LEAF_NO_EXCEPTIONS) || defined(BOOST_LEAF_NO_THREADS) || !BOOST_LEAF_CFG_CAPTURE diff --git a/test/capture_exception_result_async_test.cpp b/test/capture_exception_result_async_test.cpp index 6498380..40b9526 100644 --- a/test/capture_exception_result_async_test.cpp +++ b/test/capture_exception_result_async_test.cpp @@ -154,4 +154,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #if defined(BOOST_LEAF_NO_EXCEPTIONS) || defined(BOOST_LEAF_NO_THREADS) || !BOOST_LEAF_CFG_CAPTURE diff --git a/test/capture_exception_result_unload_test.cpp b/test/capture_exception_result_unload_test.cpp index 4085089..f7ae54f 100644 --- a/test/capture_exception_result_unload_test.cpp +++ b/test/capture_exception_result_unload_test.cpp @@ -185,4 +185,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #if defined(BOOST_LEAF_NO_EXCEPTIONS) || !BOOST_LEAF_CFG_CAPTURE diff --git a/test/capture_exception_state_test.cpp b/test/capture_exception_state_test.cpp index 5983e5c..9b1bc2f 100644 --- a/test/capture_exception_state_test.cpp +++ b/test/capture_exception_state_test.cpp @@ -82,7 +82,7 @@ int main() BOOST_TEST_NE(s.find("info<3> instance"), s.npos); } } - #endif + #endif // #if BOOST_LEAF_CFG_STD_STRING int ret = leaf::try_catch( [&] @@ -104,4 +104,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #if defined(BOOST_LEAF_NO_EXCEPTIONS) || !BOOST_LEAF_CFG_CAPTURE diff --git a/test/capture_exception_unload_test.cpp b/test/capture_exception_unload_test.cpp index 811dda9..a462053 100644 --- a/test/capture_exception_unload_test.cpp +++ b/test/capture_exception_unload_test.cpp @@ -183,4 +183,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #if defined(BOOST_LEAF_NO_EXCEPTIONS) || !BOOST_LEAF_CFG_CAPTURE diff --git a/test/capture_result_async_test.cpp b/test/capture_result_async_test.cpp index 8067a0d..e01ee98 100644 --- a/test/capture_result_async_test.cpp +++ b/test/capture_result_async_test.cpp @@ -156,4 +156,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #if defined(BOOST_LEAF_NO_THREADS) || !BOOST_LEAF_CFG_CAPTURE diff --git a/test/capture_result_state_test.cpp b/test/capture_result_state_test.cpp index 41125aa..02ed44c 100644 --- a/test/capture_result_state_test.cpp +++ b/test/capture_result_state_test.cpp @@ -81,7 +81,7 @@ int main() BOOST_TEST_NE(s.find("info<3> instance"), s.npos); } } -#endif +#endif // #if BOOST_LEAF_CFG_STD_STRING int ret = leaf::try_handle_all( [&] @@ -103,4 +103,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #if !BOOST_LEAF_CFG_CAPTURE diff --git a/test/capture_result_unload_test.cpp b/test/capture_result_unload_test.cpp index 91ac63e..1397d63 100644 --- a/test/capture_result_unload_test.cpp +++ b/test/capture_result_unload_test.cpp @@ -164,4 +164,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #if !BOOST_LEAF_CFG_CAPTURE diff --git a/test/e_LastError_test.cpp b/test/e_LastError_test.cpp index a8d1cc0..26841e2 100644 --- a/test/e_LastError_test.cpp +++ b/test/e_LastError_test.cpp @@ -8,13 +8,15 @@ int main() { - std::cout << "This test requires Windows"; + std::cout << "Test skipped (Windows only)"; return 0; } #else -#define BOOST_LEAF_CFG_WIN32 1 +#if BOOST_LEAF_CFG_WIN32 <= 0 +# error This test requires BOOST_LEAF_CFG_WIN32 > 0 +#endif #ifdef BOOST_LEAF_TEST_SINGLE_HEADER # include "leaf.hpp" @@ -67,4 +69,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifndef _WIN32 diff --git a/test/error_code_test.cpp b/test/error_code_test.cpp index 668445b..162ea5d 100644 --- a/test/error_code_test.cpp +++ b/test/error_code_test.cpp @@ -14,7 +14,7 @@ int main() return 0; } -#else +#else // #if !BOOST_LEAF_CFG_STD_SYSTEM_ERROR #ifdef BOOST_LEAF_TEST_SINGLE_HEADER # include "leaf.hpp" @@ -27,10 +27,12 @@ int main() #include "_test_res.hpp" #include "lightweight_test.hpp" +#if BOOST_LEAF_BOOST_AVAILABLE #include "boost/system/result.hpp" namespace boost { namespace leaf { template struct is_result_type>: std::true_type { }; } } +#endif namespace leaf = boost::leaf; @@ -97,7 +99,7 @@ void test() } ); BOOST_TEST_EQ(r, 42); } -#endif +#endif // #if __cplusplus >= 201703L { int r = leaf::try_handle_all( @@ -154,7 +156,7 @@ void test() } ); BOOST_TEST_EQ(r, 42); } -#endif +#endif // #if __cplusplus >= 201703L { int r = leaf::try_handle_all( []() -> R @@ -212,7 +214,7 @@ void test() } ); BOOST_TEST_EQ(r, 42); } -#endif +#endif // #if __cplusplus >= 201703L { int r = leaf::try_handle_all( @@ -271,7 +273,7 @@ void test() } ); BOOST_TEST_EQ(r, 42); } -#endif +#endif // #if __cplusplus >= 201703L { int r = leaf::try_handle_all( []() -> R @@ -313,7 +315,7 @@ void test() } ); BOOST_TEST_EQ(r, 42); } -#endif +#endif // #if __cplusplus >= 201703L } template @@ -380,7 +382,7 @@ void test_void() } ); BOOST_TEST_EQ(r, 42); } -#endif +#endif // #if __cplusplus >= 201703L { int r = 0; @@ -440,7 +442,7 @@ void test_void() } ); BOOST_TEST_EQ(r, 42); } -#endif +#endif // #if __cplusplus >= 201703L { int r = 0; leaf::try_handle_all( @@ -501,7 +503,7 @@ void test_void() } ); BOOST_TEST_EQ(r, 42); } -#endif +#endif // #if __cplusplus >= 201703L { int r = 0; @@ -563,7 +565,7 @@ void test_void() } ); BOOST_TEST_EQ(r, 42); } -#endif +#endif // #if __cplusplus >= 201703L { int r = 0; leaf::try_handle_all( @@ -607,7 +609,7 @@ void test_void() } ); BOOST_TEST_EQ(r, 42); } -#endif +#endif // #if __cplusplus >= 201703L } int main() @@ -616,8 +618,10 @@ int main() test>(); test_void>(); test_void>(); +#ifdef BOOST_LEAF_BOOST_AVAILABLE test>(); +#endif return boost::report_errors(); } -#endif +#endif // #if !BOOST_LEAF_CFG_STD_SYSTEM_ERROR diff --git a/test/exception_test.cpp b/test/exception_test.cpp index 3cd926d..f915863 100644 --- a/test/exception_test.cpp +++ b/test/exception_test.cpp @@ -291,4 +291,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifdef BOOST_LEAF_NO_EXCEPTIONS diff --git a/test/exception_to_result_test.cpp b/test/exception_to_result_test.cpp index d624e41..0168ba5 100644 --- a/test/exception_to_result_test.cpp +++ b/test/exception_to_result_test.cpp @@ -128,4 +128,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifdef BOOST_LEAF_NO_EXCEPTIONS diff --git a/test/handle_all_test.cpp b/test/handle_all_test.cpp index 2a89f01..665c342 100644 --- a/test/handle_all_test.cpp +++ b/test/handle_all_test.cpp @@ -52,7 +52,7 @@ leaf::result 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 +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR struct move_only { @@ -142,7 +142,7 @@ int main() } ); BOOST_TEST_EQ(c, 2); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // void, try_handle_all (failure), match cond_x (wrapped std::error_code) @@ -175,7 +175,7 @@ int main() } ); BOOST_TEST_EQ(c, 2); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // void, try_handle_all (failure), match enum (single enum value) { @@ -366,7 +366,7 @@ int main() } ); BOOST_TEST_EQ(r, 2); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // int, try_handle_all (failure), match cond_x (wrapped std::error_code) @@ -394,7 +394,7 @@ int main() } ); BOOST_TEST_EQ(r, 2); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // int, try_handle_all (failure), match enum (single enum value) { @@ -565,7 +565,7 @@ int main() } ); BOOST_TEST_EQ(r.value, 2); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // move_only, try_handle_all (failure), match cond_x (wrapped std::error_code) @@ -593,7 +593,7 @@ int main() } ); BOOST_TEST_EQ(r.value, 2); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // move_only, try_handle_all (failure), match enum (single enum value) { diff --git a/test/handle_some_test.cpp b/test/handle_some_test.cpp index ba02e8a..051d6d5 100644 --- a/test/handle_some_test.cpp +++ b/test/handle_some_test.cpp @@ -52,7 +52,7 @@ leaf::result 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 +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR int main() { @@ -120,7 +120,7 @@ int main() BOOST_TEST_EQ(c, 1); BOOST_TEST(r); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // void, try_handle_some (failure, matched), match cond_x (wrapped std::error_code) @@ -144,7 +144,7 @@ int main() BOOST_TEST_EQ(c, 1); BOOST_TEST(r); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // void, try_handle_some (failure, matched), match enum (single enum value) { @@ -330,7 +330,7 @@ int main() } ); BOOST_TEST_EQ(c, 2); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // void, try_handle_some (failure, initially not matched), match cond_x (wrapped std::error_code) @@ -370,7 +370,7 @@ int main() } ); BOOST_TEST_EQ(c, 2); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // void, try_handle_some (failure, initially not matched), match enum (single enum value) { @@ -600,7 +600,7 @@ int main() } ); BOOST_TEST_EQ(c, 1); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // void, try_handle_some (failure, initially matched), match cond_x (wrapped std::error_code) @@ -640,7 +640,7 @@ int main() } ); BOOST_TEST_EQ(c, 1); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // void, try_handle_some (failure, initially matched), match enum (single enum value) { @@ -853,7 +853,7 @@ int main() BOOST_TEST(r); BOOST_TEST_EQ(*r, 2); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // int, try_handle_some (failure, matched), match enum (single enum value) { @@ -1011,7 +1011,7 @@ int main() } ); BOOST_TEST_EQ(r, 2); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // int, try_handle_some (failure, initially not matched), match cond_x (wrapped std::error_code) @@ -1045,7 +1045,7 @@ int main() } ); BOOST_TEST_EQ(r, 2); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // int, try_handle_some (failure, initially not matched), match enum (single enum value) { @@ -1239,7 +1239,7 @@ int main() } ); BOOST_TEST_EQ(r, 1); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // int, try_handle_some (failure, initially matched), match cond_x (wrapped std::error_code) @@ -1273,7 +1273,7 @@ int main() } ); BOOST_TEST_EQ(r, 1); } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR // int, try_handle_some (failure, initially matched), match enum (single enum value) { @@ -1474,7 +1474,7 @@ int main() BOOST_TEST_EQ(r.value(), 1); BOOST_TEST_EQ(handle_some_handler_called, 1); } -#endif +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS #ifndef BOOST_LEAF_NO_EXCEPTIONS // exception caught, error not handled @@ -1509,7 +1509,7 @@ int main() }); BOOST_TEST_EQ(r, 1); } -#endif +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS return boost::report_errors(); } diff --git a/test/lightweight_test.hpp b/test/lightweight_test.hpp index 2e060d2..e3bf0bb 100644 --- a/test/lightweight_test.hpp +++ b/test/lightweight_test.hpp @@ -4,19 +4,18 @@ #include #include "boost/core/lightweight_test.hpp" -#include #ifdef BOOST_LEAF_NO_EXCEPTIONS namespace boost { - [[noreturn]] void throw_exception( std::exception const & e ) + [[noreturn]] inline void throw_exception( std::exception const & e ) { std::cerr << "Terminating due to a C++ exception under BOOST_LEAF_NO_EXCEPTIONS: " << e.what(); std::terminate(); } struct source_location; - [[noreturn]] void throw_exception( std::exception const & e, boost::source_location const & ) + [[noreturn]] inline void throw_exception( std::exception const & e, boost::source_location const & ) { throw_exception(e); } diff --git a/test/match_member_test.cpp b/test/match_member_test.cpp index 8ab4e28..8971dbe 100644 --- a/test/match_member_test.cpp +++ b/test/match_member_test.cpp @@ -24,7 +24,6 @@ int main() #include "_test_ec.hpp" #include "lightweight_test.hpp" -#include namespace leaf = boost::leaf; diff --git a/test/match_test.cpp b/test/match_test.cpp index 2c7f0fd..9dc1744 100644 --- a/test/match_test.cpp +++ b/test/match_test.cpp @@ -12,7 +12,6 @@ #include "_test_ec.hpp" #include "lightweight_test.hpp" -#include namespace leaf = boost::leaf; @@ -32,7 +31,7 @@ constexpr bool e_my_error_gt( e_my_error const & e ) noexcept { return e.value > S; } -#endif +#endif // #if __cplusplus >= 201703L struct my_exception: std::exception { @@ -100,9 +99,9 @@ int main() BOOST_TEST(( !test>>(e) )); BOOST_TEST(( test>>(e) )); BOOST_TEST(( !test>>(e) )); -#endif +#endif // #if __cplusplus >= 201703L } -#endif +#endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR #if __cplusplus >= 201703L { @@ -275,7 +274,7 @@ int main() } ); BOOST_TEST_EQ(r, 2); } -#endif +#endif // #if __cplusplus >= 201703L return boost::report_errors(); } diff --git a/test/match_value_test.cpp b/test/match_value_test.cpp index 1ee3d8c..82caa7d 100644 --- a/test/match_value_test.cpp +++ b/test/match_value_test.cpp @@ -12,7 +12,6 @@ #include "_test_ec.hpp" #include "lightweight_test.hpp" -#include namespace leaf = boost::leaf; diff --git a/test/multiple_errors_test.cpp b/test/multiple_errors_test.cpp index 8f36d1b..ece87b5 100644 --- a/test/multiple_errors_test.cpp +++ b/test/multiple_errors_test.cpp @@ -13,7 +13,6 @@ #endif #include "lightweight_test.hpp" -#include namespace leaf = boost::leaf; diff --git a/test/on_error_accumulate_nested_error_exception_test.cpp b/test/on_error_accumulate_nested_error_exception_test.cpp index b45c56c..9df615f 100644 --- a/test/on_error_accumulate_nested_error_exception_test.cpp +++ b/test/on_error_accumulate_nested_error_exception_test.cpp @@ -84,4 +84,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifdef BOOST_LEAF_NO_EXCEPTIONS diff --git a/test/on_error_accumulate_nested_new_error_exception_test.cpp b/test/on_error_accumulate_nested_new_error_exception_test.cpp index c14ba86..46ef0cb 100644 --- a/test/on_error_accumulate_nested_new_error_exception_test.cpp +++ b/test/on_error_accumulate_nested_new_error_exception_test.cpp @@ -99,4 +99,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifdef BOOST_LEAF_NO_EXCEPTIONS diff --git a/test/on_error_accumulate_nested_success_exception_test.cpp b/test/on_error_accumulate_nested_success_exception_test.cpp index a85f2be..b09e4ce 100644 --- a/test/on_error_accumulate_nested_success_exception_test.cpp +++ b/test/on_error_accumulate_nested_success_exception_test.cpp @@ -68,4 +68,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifdef BOOST_LEAF_NO_EXCEPTIONS diff --git a/test/on_error_alloc_fail_test.cpp b/test/on_error_alloc_fail_test.cpp new file mode 100644 index 0000000..ca9e8d8 --- /dev/null +++ b/test/on_error_alloc_fail_test.cpp @@ -0,0 +1,125 @@ +// Copyright 2018-2025 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 +# include +# include +# include +# include +#endif + +#if !BOOST_LEAF_CFG_CAPTURE || defined(BOOST_LEAF_NO_EXCEPTIONS) + +int main() +{ + return 0; +} + +#else + +#include +#include + +namespace leaf = boost::leaf; + +struct alloc_fail_info +{ + int value; +}; + +namespace boost { namespace leaf { namespace detail { + +template <> +struct capturing_slot_node_allocator +{ + template + static dynamic_allocator::capturing_slot_node * new_( A && ... ) + { + throw std::bad_alloc(); + } + + static void delete_( dynamic_allocator::capturing_slot_node * p ) noexcept + { + delete p; + } +}; + +} } } // namespace boost::leaf::detail + +#include "lightweight_test.hpp" + +template +struct other_info +{ + int value; +}; + +leaf::result f() +{ + auto load = leaf::on_error( other_info<1>{1}, alloc_fail_info{42}, other_info<2>{2} ); + return leaf::new_error(); +} + +int main() +{ + { + auto captured = leaf::try_capture_all( + []() -> leaf::result + { + return f(); + } ); + + int r = leaf::try_handle_all( + [&]() -> leaf::result + { + (void) captured.value(); + return 0; + }, + []( std::bad_alloc const &, other_info<1> const & oi1, other_info<2> const * oi2, alloc_fail_info const * afi ) + { + BOOST_TEST_EQ(oi1.value, 1); + BOOST_TEST_EQ(oi2, nullptr); + BOOST_TEST_EQ(afi, nullptr); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } + +#if BOOST_LEAF_CFG_DIAGNOSTICS && BOOST_LEAF_CFG_STD_STRING + { + int r = leaf::try_handle_all( + []() -> leaf::result + { + BOOST_LEAF_CHECK(f()); + return 0; + }, + []( std::bad_alloc const &, leaf::diagnostic_details const & dd ) + { + std::ostringstream s; + s << dd; + std::string str = s.str(); + BOOST_TEST(str.find("other_info<1>") != std::string::npos); + BOOST_TEST(str.find("other_info<2>") == std::string::npos); + BOOST_TEST(str.find("alloc_fail_info") == std::string::npos); + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + } +#endif + + return boost::report_errors(); +} + +#endif diff --git a/test/on_error_defer_nested_error_exception_test.cpp b/test/on_error_defer_nested_error_exception_test.cpp index eab9a2b..0ee2a33 100644 --- a/test/on_error_defer_nested_error_exception_test.cpp +++ b/test/on_error_defer_nested_error_exception_test.cpp @@ -84,4 +84,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifdef BOOST_LEAF_NO_EXCEPTIONS diff --git a/test/on_error_defer_nested_new_error_exception_test.cpp b/test/on_error_defer_nested_new_error_exception_test.cpp index 45efe6b..9fb9507 100644 --- a/test/on_error_defer_nested_new_error_exception_test.cpp +++ b/test/on_error_defer_nested_new_error_exception_test.cpp @@ -91,4 +91,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifdef BOOST_LEAF_NO_EXCEPTIONS diff --git a/test/on_error_defer_nested_success_exception_test.cpp b/test/on_error_defer_nested_success_exception_test.cpp index e471dbb..6899d58 100644 --- a/test/on_error_defer_nested_success_exception_test.cpp +++ b/test/on_error_defer_nested_success_exception_test.cpp @@ -68,4 +68,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifdef BOOST_LEAF_NO_EXCEPTIONS diff --git a/test/on_error_dynamic_reserve_test1.cpp b/test/on_error_dynamic_reserve_test1.cpp new file mode 100644 index 0000000..307dfae --- /dev/null +++ b/test/on_error_dynamic_reserve_test1.cpp @@ -0,0 +1,52 @@ +// Copyright 2018-2025 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 +# include +# include +# include +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +leaf::result f() +{ + BOOST_TEST(leaf::tls::read_ptr>>() == nullptr); + BOOST_TEST(leaf::tls::read_ptr>>() != nullptr); + auto load = leaf::on_error( info<42>{42} ); + BOOST_TEST(leaf::tls::read_ptr>>() == nullptr); + return leaf::new_error(info<43>{}); +} + +int main() +{ + int r = leaf::try_handle_all( + []() -> leaf::result + { + BOOST_LEAF_CHECK(f()); + return 0; + }, + []( info<43> const & ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} diff --git a/test/on_error_dynamic_reserve_test2.cpp b/test/on_error_dynamic_reserve_test2.cpp new file mode 100644 index 0000000..9a839b3 --- /dev/null +++ b/test/on_error_dynamic_reserve_test2.cpp @@ -0,0 +1,58 @@ +// Copyright 2018-2025 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 +# include +# include +# include +# include +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +leaf::result f() +{ + BOOST_TEST(leaf::tls::read_ptr>>() == nullptr); + BOOST_TEST(leaf::tls::read_ptr>>() != nullptr); + auto load = leaf::on_error( info<42>{42} ); + BOOST_TEST(leaf::tls::read_ptr>>() == nullptr); + auto e = leaf::new_error(info<43>{}); + BOOST_TEST( + (BOOST_LEAF_CFG_CAPTURE == 0 || BOOST_LEAF_CFG_DIAGNOSTICS == 0) + == + (leaf::tls::read_ptr>>() == nullptr)); + return e; +} + +int main() +{ + int r = leaf::try_handle_all( + []() -> leaf::result + { + BOOST_LEAF_CHECK(f()); + return 0; + }, + []( info<43> const & ) + { + return 1; + }, + []( leaf::diagnostic_details const & ) + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} diff --git a/test/on_error_dynamic_reserve_test3.cpp b/test/on_error_dynamic_reserve_test3.cpp new file mode 100644 index 0000000..20102fd --- /dev/null +++ b/test/on_error_dynamic_reserve_test3.cpp @@ -0,0 +1,74 @@ +// Copyright 2018-2025 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 + +#if !BOOST_LEAF_CFG_CAPTURE + +#include + +int main() +{ + std::cout << "Unit test not applicable." << std::endl; + return 0; +} + +#else + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include +# include +# include +# include +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +leaf::result f() +{ + BOOST_TEST(leaf::tls::read_ptr>>() == nullptr); + BOOST_TEST(leaf::tls::read_ptr>>() != nullptr); + auto load = leaf::on_error( info<42>{42} ); + BOOST_TEST(leaf::tls::read_ptr>>() == nullptr); + auto e = leaf::new_error(info<43>{}); + BOOST_TEST(leaf::tls::read_ptr>>() != nullptr); + return e; +} + +int main() +{ + int r = leaf::try_handle_all( + []() -> leaf::result + { + return leaf::try_capture_all( + []() -> leaf::result + { + BOOST_LEAF_CHECK(f()); + return 0; + } ); + }, + []( info<43> const & ) + { + return 1; + }, + [] + { + return 2; + } ); + BOOST_TEST_EQ(r, 1); + + return boost::report_errors(); +} + +#endif diff --git a/test/on_error_preload_basic_test.cpp b/test/on_error_preload_basic_test.cpp index e700be3..f65215e 100644 --- a/test/on_error_preload_basic_test.cpp +++ b/test/on_error_preload_basic_test.cpp @@ -6,6 +6,7 @@ # include "leaf.hpp" #else # include +# include # include # include #endif @@ -69,5 +70,22 @@ int main() auto load = leaf::on_error( inf1, info<-42>{-42} ); return leaf::new_error(); }); + +#if BOOST_LEAF_CFG_CAPTURE + (void) leaf::try_capture_all( + []() -> leaf::result + { + leaf::detail::dynamic_allocator * da = leaf::detail::get_dynamic_allocator(); + BOOST_TEST(da != nullptr); + BOOST_TEST(da->preloaded_list() == nullptr); + { + auto load = leaf::on_error( info<42>{42} ); + BOOST_TEST(da->preloaded_list() != nullptr); + } + BOOST_TEST(da->preloaded_list() == nullptr); + return { }; + } ); +#endif + return boost::report_errors(); } diff --git a/test/on_error_preload_exception_test.cpp b/test/on_error_preload_exception_test.cpp index 881ee4a..6dd7b7f 100644 --- a/test/on_error_preload_exception_test.cpp +++ b/test/on_error_preload_exception_test.cpp @@ -149,4 +149,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #if defined(BOOST_LEAF_NO_EXCEPTIONS) || defined(BOOST_LEAF_NO_THREADS) diff --git a/test/on_error_preload_nested_error_exception_test.cpp b/test/on_error_preload_nested_error_exception_test.cpp index 1eb0843..d585399 100644 --- a/test/on_error_preload_nested_error_exception_test.cpp +++ b/test/on_error_preload_nested_error_exception_test.cpp @@ -84,4 +84,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifdef BOOST_LEAF_NO_EXCEPTIONS diff --git a/test/on_error_preload_nested_new_error_exception_test.cpp b/test/on_error_preload_nested_new_error_exception_test.cpp index a46b5b5..7fa75f6 100644 --- a/test/on_error_preload_nested_new_error_exception_test.cpp +++ b/test/on_error_preload_nested_new_error_exception_test.cpp @@ -91,4 +91,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifdef BOOST_LEAF_NO_EXCEPTIONS diff --git a/test/on_error_preload_nested_success_exception_test.cpp b/test/on_error_preload_nested_success_exception_test.cpp index 103ee86..8ca0071 100644 --- a/test/on_error_preload_nested_success_exception_test.cpp +++ b/test/on_error_preload_nested_success_exception_test.cpp @@ -68,4 +68,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifdef BOOST_LEAF_NO_EXCEPTIONS diff --git a/test/result_state_test.cpp b/test/result_state_test.cpp index f323a82..0d95f2f 100644 --- a/test/result_state_test.cpp +++ b/test/result_state_test.cpp @@ -369,7 +369,7 @@ int main() } BOOST_TEST_EQ(err::count, 0); BOOST_TEST_EQ(val::count, 0); -#endif +#endif // #if BOOST_LEAF_CFG_CAPTURE // ^^ result ^^ @@ -545,7 +545,7 @@ int main() BOOST_TEST(r2.operator->() == 0); } BOOST_TEST_EQ(err::count, 0); -#endif +#endif // #if BOOST_LEAF_CFG_CAPTURE { leaf::result r = leaf::error_id(); diff --git a/test/visibility_test_lib.cpp b/test/so_dll_lib1.cpp similarity index 72% rename from test/visibility_test_lib.cpp rename to test/so_dll_lib1.cpp index dd90bac..a376263 100644 --- a/test/visibility_test_lib.cpp +++ b/test/so_dll_lib1.cpp @@ -2,8 +2,6 @@ // 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" - #ifdef BOOST_LEAF_TEST_SINGLE_HEADER # include "leaf.hpp" #else @@ -12,17 +10,23 @@ # include #endif +#include "lightweight_test.hpp" + +#define BOOST_LEAF_SO_DLL_TEST_BUILDING_LIB1 + +#include "so_dll_lib1.hpp" + namespace leaf = boost::leaf; -leaf::result BOOST_SYMBOL_VISIBLE hidden_result() +BOOST_LEAF_SO_DLL_TEST_LIB1_API leaf::result hidden_result1() { 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 +#ifndef BOOST_LEAF_NO_EXCEPTIONS -void BOOST_SYMBOL_VISIBLE hidden_throw() +BOOST_LEAF_SO_DLL_TEST_LIB1_API void hidden_throw1() { auto load = leaf::on_error( my_info<1>{1}, my_info<3>{3} ); leaf::throw_exception( my_info<2>{2} ); diff --git a/test/so_dll_lib1.hpp b/test/so_dll_lib1.hpp new file mode 100644 index 0000000..7e00dc4 --- /dev/null +++ b/test/so_dll_lib1.hpp @@ -0,0 +1,29 @@ +#ifndef SO_DLL_TEST_LIB1_HPP_INCLUDED +#define SO_DLL_TEST_LIB1_HPP_INCLUDED + +// Copyright 2018-2024 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 "so_dll_test.hpp" + +#ifdef _WIN32 +# ifdef BOOST_LEAF_SO_DLL_TEST_STATIC +# define BOOST_LEAF_SO_DLL_TEST_LIB1_API +# elif defined(BOOST_LEAF_SO_DLL_TEST_BUILDING_LIB1) +# define BOOST_LEAF_SO_DLL_TEST_LIB1_API __declspec(dllexport) +# else +# define BOOST_LEAF_SO_DLL_TEST_LIB1_API __declspec(dllimport) +# endif +#else +# define BOOST_LEAF_SO_DLL_TEST_LIB1_API [[gnu::visibility("default")]] +#endif + +BOOST_LEAF_SO_DLL_TEST_LIB1_API boost::leaf::result hidden_result1(); + +#ifndef BOOST_LEAF_NO_EXCEPTIONS +BOOST_LEAF_SO_DLL_TEST_LIB1_API void hidden_throw1(); +#endif + +#endif + diff --git a/test/so_dll_lib2.cpp b/test/so_dll_lib2.cpp new file mode 100644 index 0000000..56afd1e --- /dev/null +++ b/test/so_dll_lib2.cpp @@ -0,0 +1,35 @@ +// Copyright 2018-2024 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 +# include +# include +#endif + +#include "lightweight_test.hpp" + +#define BOOST_LEAF_SO_DLL_TEST_BUILDING_LIB2 + +#include "so_dll_lib2.hpp" + +namespace leaf = boost::leaf; + +BOOST_LEAF_SO_DLL_TEST_LIB2_API leaf::result hidden_result2() +{ + auto load = leaf::on_error( my_info<1>{1}, my_info<3>{3} ); + return leaf::new_error( my_info<2>{2} ); +} + +#ifndef BOOST_LEAF_NO_EXCEPTIONS + +BOOST_LEAF_SO_DLL_TEST_LIB2_API void hidden_throw2() +{ + auto load = leaf::on_error( my_info<1>{1}, my_info<3>{3} ); + leaf::throw_exception( my_info<2>{2} ); +} + +#endif diff --git a/test/so_dll_lib2.hpp b/test/so_dll_lib2.hpp new file mode 100644 index 0000000..35cb138 --- /dev/null +++ b/test/so_dll_lib2.hpp @@ -0,0 +1,29 @@ +#ifndef SO_DLL_TEST_LIB2_HPP_INCLUDED +#define SO_DLL_TEST_LIB2_HPP_INCLUDED + +// Copyright 2018-2024 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 "so_dll_test.hpp" + +#ifdef _WIN32 +# ifdef BOOST_LEAF_SO_DLL_TEST_STATIC +# define BOOST_LEAF_SO_DLL_TEST_LIB2_API +# elif defined(BOOST_LEAF_SO_DLL_TEST_BUILDING_LIB2) +# define BOOST_LEAF_SO_DLL_TEST_LIB2_API __declspec(dllexport) +# else +# define BOOST_LEAF_SO_DLL_TEST_LIB2_API __declspec(dllimport) +# endif +#else +# define BOOST_LEAF_SO_DLL_TEST_LIB2_API [[gnu::visibility("default")]] +#endif + +BOOST_LEAF_SO_DLL_TEST_LIB2_API boost::leaf::result hidden_result2(); + +#ifndef BOOST_LEAF_NO_EXCEPTIONS +BOOST_LEAF_SO_DLL_TEST_LIB2_API void hidden_throw2(); +#endif + +#endif + diff --git a/test/so_dll_test.cpp b/test/so_dll_test.cpp new file mode 100644 index 0000000..c16f41c --- /dev/null +++ b/test/so_dll_test.cpp @@ -0,0 +1,226 @@ +// Copyright 2018-2024 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_SO_DLL_TEST_BUILDING_EXE + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include +# include +#endif + +#include "so_dll_lib1.hpp" +#include "so_dll_lib2.hpp" + +#if BOOST_LEAF_CFG_STD_STRING +# include +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +int test_result(leaf::result (*f)(), bool print) +{ + int r = leaf::try_handle_all( + [f]() -> leaf::result + { + BOOST_LEAF_CHECK(f()); + return 0; + }, + [&]( my_info<1> x1, my_info<2> x2, leaf::diagnostic_details const & info, leaf::diagnostic_details const & vinfo ) + { + if( x1.value != 1 ) + return 1; + if( x2.value != 2 ) + return 2; + if( BOOST_LEAF_CFG_DIAGNOSTICS && print ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::ostringstream ss; ss << vinfo; + std::string s = ss.str(); + std::cout << "Handler matched, diagnostics:\n" << s << std::endl; + if( BOOST_LEAF_CFG_DIAGNOSTICS && BOOST_LEAF_CFG_CAPTURE ) + if( s.find("Test my_info<3>::value = 3") == std::string::npos ) + return 3; +#endif + } + return 0; + }, + [&](leaf::diagnostic_details const & vinfo) + { +#if BOOST_LEAF_CFG_STD_STRING + if( print ) + std::cout << "Test is failing (catch-all), diagnostics:\n" << vinfo << std::endl; +#endif + return 4; + } ); + return r; +} + +#ifndef BOOST_LEAF_NO_EXCEPTIONS +int test_exception(void (*f)(), bool print) +{ + int r = leaf::try_catch( + [f] + { + f(); + return 0; + }, + [&]( my_info<1> x1, my_info<2> x2, leaf::diagnostic_details const & info, leaf::diagnostic_details const & vinfo ) + { + if( x1.value != 1 ) + return 1; + if( x2.value != 2 ) + return 2; + if( BOOST_LEAF_CFG_DIAGNOSTICS && print ) + { +#if BOOST_LEAF_CFG_STD_STRING + std::ostringstream ss; ss << vinfo; + std::string s = ss.str(); + std::cout << s << std::endl; + if( BOOST_LEAF_CFG_DIAGNOSTICS && BOOST_LEAF_CFG_CAPTURE ) + if( s.find("Test my_info<3>::value = 3") == std::string::npos ) + return 3; +#endif + } + return 0; + }, + [&](leaf::diagnostic_details const & vinfo) + { +#if BOOST_LEAF_CFG_STD_STRING + if( print ) + std::cout << "Test is failing\n" << vinfo; +#endif + return 4; + } ); + return r; +} + +int test_catch(void (*f)()) +{ + try + { + f(); + return 1; + } + catch( leaf::error_id const & ) + { + return 0; + } + catch(...) + { + return 2; + } +} +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS + +void test_single_thread() +{ + BOOST_TEST_EQ(test_result(hidden_result2, true), 0); + BOOST_TEST_EQ(test_result(hidden_result1, true), 0); + +#ifndef BOOST_LEAF_NO_EXCEPTIONS + BOOST_TEST_EQ(test_exception(hidden_throw1, true), 0); + BOOST_TEST_EQ(test_catch(hidden_throw1), 0); + BOOST_TEST_EQ(test_exception(hidden_throw2, true), 0); + BOOST_TEST_EQ(test_catch(hidden_throw2), 0); +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS +} + +void test_multithreaded() +{ + constexpr int N = 100; + { + std::mutex mtx; + std::condition_variable cv; + bool ready = false; + auto test_function = [&] + { + std::unique_lock lock(mtx); + cv.wait(lock, [&]{ return ready; }); + lock.unlock(); + int result1 = test_result(hidden_result1, false); + int result2 = test_result(hidden_result2, false); + return result1 + result2; + }; + std::vector> futures; + for (int i = 0; i < N; ++i) + futures.push_back(std::async(std::launch::async, test_function)); + { + std::lock_guard lock(mtx); + ready = true; + } + cv.notify_all(); + for (auto start = std::chrono::steady_clock::now(); std::chrono::steady_clock::now() - start < std::chrono::seconds(1); ) + if ((std::rand() % 2) == 0 || futures.empty()) + futures.push_back(std::async(std::launch::async, test_function)); + else + { + BOOST_TEST_EQ(futures.back().get(), 0); + futures.pop_back(); + } + for (auto & f : futures) + BOOST_TEST_EQ(f.get(), 0); + } + +#ifndef BOOST_LEAF_NO_EXCEPTIONS + { + std::mutex mtx; + std::condition_variable cv; + bool ready = false; + auto test_function = [&] + { + std::unique_lock lock(mtx); + cv.wait(lock, [&]{ return ready; }); + lock.unlock(); + int result1 = test_exception(hidden_throw1, false); + int result2 = test_catch(hidden_throw1); + int result3 = test_exception(hidden_throw2, false); + int result4 = test_catch(hidden_throw2); + return result1 + result2 + result3 + result4; + }; + std::vector> futures; + for (int i = 0; i < N; ++i) + futures.push_back(std::async(std::launch::async, test_function)); + { + std::lock_guard lock(mtx); + ready = true; + } + cv.notify_all(); + for (auto start = std::chrono::steady_clock::now(); std::chrono::steady_clock::now() - start < std::chrono::seconds(1); ) + if ((std::rand() % 2) == 0 || futures.empty()) + futures.push_back(std::async(std::launch::async, test_function)); + else + { + BOOST_TEST_EQ(futures.back().get(), 0); + futures.pop_back(); + } + for (auto & f : futures) + BOOST_TEST_EQ(f.get(), 0); + } +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS +} + +int main() +{ + std::srand(std::hash{}(static_cast(std::time(nullptr)))); + + test_single_thread(); + test_multithreaded(); + + return boost::report_errors(); +} diff --git a/test/visibility_test_lib.hpp b/test/so_dll_test.hpp similarity index 54% rename from test/visibility_test_lib.hpp rename to test/so_dll_test.hpp index a314d6b..9d6306e 100644 --- a/test/visibility_test_lib.hpp +++ b/test/so_dll_test.hpp @@ -1,15 +1,20 @@ -#ifndef VISIBILITY_TEST_LIB_HPP_INCLUDED -#define VISIBILITY_TEST_LIB_HPP_INCLUDED +#ifndef SO_DLL_TEST_HPP_INCLUDED +#define SO_DLL_TEST_HPP_INCLUDED // Copyright 2018-2024 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 +#if defined(_WIN32) && BOOST_LEAF_CFG_WIN32 < 2 +# error This test requires BOOST_LEAF_CFG_WIN32 >= 2 +#endif + +// Note, under BOOST_LEAF_CFG_WIN32==2 (using Win32 TLS) we do not need +// import/export for error types. We still need default visibility on POSIX. template -struct BOOST_SYMBOL_VISIBLE my_info +struct [[gnu::visibility("default")]] my_info { int value; @@ -21,3 +26,4 @@ struct BOOST_SYMBOL_VISIBLE my_info }; #endif + diff --git a/test/to_variant_test.cpp b/test/to_variant_test.cpp index 8187309..24d5aad 100644 --- a/test/to_variant_test.cpp +++ b/test/to_variant_test.cpp @@ -58,9 +58,9 @@ int main() BOOST_TEST(std::get<0>(t).value() == E1::e12); BOOST_TEST(std::get<2>(t).value() == E3::e31); } -#endif +#endif // #if !defined(__clang__) || __clang_major__ < 5 || __clang_major__ > 7 return boost::report_errors(); } -#endif +#endif // #if __cplusplus < 201703L diff --git a/test/try_capture_all_test.cpp b/test/try_capture_all_test.cpp index c3254c9..08ea9bb 100644 --- a/test/try_capture_all_test.cpp +++ b/test/try_capture_all_test.cpp @@ -87,7 +87,7 @@ int main() } ); BOOST_TEST_EQ(r1, 1); } -#endif +#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS { leaf::result r = leaf::try_capture_all( []() -> leaf::result @@ -147,4 +147,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #if !BOOST_LEAF_CFG_CAPTURE diff --git a/test/try_catch_error_id_test.cpp b/test/try_catch_error_id_test.cpp index 1c14305..fcd3bc2 100644 --- a/test/try_catch_error_id_test.cpp +++ b/test/try_catch_error_id_test.cpp @@ -52,4 +52,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifdef BOOST_LEAF_NO_EXCEPTIONS diff --git a/test/try_catch_system_error_test.cpp b/test/try_catch_system_error_test.cpp index 012bffb..0c940c9 100644 --- a/test/try_catch_system_error_test.cpp +++ b/test/try_catch_system_error_test.cpp @@ -147,4 +147,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #if defined(BOOST_LEAF_NO_EXCEPTIONS) || !BOOST_LEAF_CFG_STD_SYSTEM_ERROR diff --git a/test/try_catch_test.cpp b/test/try_catch_test.cpp index a31f9ca..032b2cd 100644 --- a/test/try_catch_test.cpp +++ b/test/try_catch_test.cpp @@ -613,4 +613,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifdef BOOST_LEAF_NO_EXCEPTIONS diff --git a/test/try_exception_and_result_test.cpp b/test/try_exception_and_result_test.cpp index 2933311..21a3ff1 100644 --- a/test/try_exception_and_result_test.cpp +++ b/test/try_exception_and_result_test.cpp @@ -705,4 +705,4 @@ int main() return boost::report_errors(); } -#endif +#endif // #ifdef BOOST_LEAF_NO_EXCEPTIONS diff --git a/test/visibility_test.cpp b/test/visibility_test.cpp deleted file mode 100644 index 8b9bb0e..0000000 --- a/test/visibility_test.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2018-2024 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 -# include -#endif - -#include "visibility_test_lib.hpp" - -#if BOOST_LEAF_CFG_STD_STRING -# include -# include -#endif - -#include "lightweight_test.hpp" - -namespace leaf = boost::leaf; - -leaf::result hidden_result(); -void hidden_throw(); - -int main() -{ - { - int r = leaf::try_handle_all( - []() -> leaf::result - { - BOOST_LEAF_CHECK(hidden_result()); - return 0; - }, - []( my_info<1> x1, my_info<2> x2, leaf::diagnostic_details const & info, leaf::diagnostic_details 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::ostringstream ss; ss << vinfo; - std::string s = ss.str(); - std::cout << s << std::endl; - if( BOOST_LEAF_CFG_DIAGNOSTICS && BOOST_LEAF_CFG_CAPTURE ) - BOOST_TEST_NE(s.find("Test my_info<3>::value = 3"), std::string::npos); -#endif - } - return 1; - }, - [](leaf::diagnostic_details const & vinfo) - { - std::cout << "Test is failing\n" << vinfo; - 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_details const & info, leaf::diagnostic_details 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::ostringstream ss; ss << vinfo; - std::string s = ss.str(); - std::cout << s << std::endl; - if( BOOST_LEAF_CFG_DIAGNOSTICS && BOOST_LEAF_CFG_CAPTURE ) - BOOST_TEST_NE(s.find("Test my_info<3>::value = 3"), std::string::npos); -#endif - } - return 1; - }, - [](leaf::diagnostic_details const & vinfo) - { - std::cout << "Test is failing\n" << vinfo; - 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/wasm.txt b/wasm.txt index 7bcd432..6624398 100644 --- a/wasm.txt +++ b/wasm.txt @@ -1,4 +1,4 @@ -# Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +# Copyright 2018-2025 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)