diff --git a/.travis.yml b/.travis.yml index d67f527..67841b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -231,7 +231,7 @@ install: - gem install coderay # - pip3 install meson - cd .. - - git clone -b $TRAVIS_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root + - git clone -b master --depth 1 https://github.com/boostorg/boost.git boost-root - cd boost-root - git submodule update --init tools/build - git submodule update --init tools/inspect diff --git a/.vscode/launch.json b/.vscode/launch.json index 1bf17a0..bbfb2dc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,8 +8,8 @@ "name": "(lldb) Launch", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/bld/debug/preload_test.2", - "args": [ "test/Jafile.v2" ], + "program": "${workspaceFolder}/bld/debug/result_state_test", + "args": [ ], "cwd": "${workspaceFolder}", "stopAtEntry": false, "externalConsole": false, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 485fbdf..5e23d46 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -24,192 +24,129 @@ } }, { - "label": "basic_test", + "label": "capture_exception_async_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test basic_test", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_exception_async_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test basic_test" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_exception_async_test" } }, { - "label": "defer_test.1", + "label": "capture_exception_state_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.1", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_exception_state_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.1" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_exception_state_test" } }, { - "label": "defer_test.2", + "label": "capture_exception_unload_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.2", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_exception_unload_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.2" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_exception_unload_test" } }, { - "label": "defer_test.3", + "label": "capture_result_async_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.3", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_result_async_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.3" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_result_async_test" } }, { - "label": "defer_test.4", + "label": "capture_result_state_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.4", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_result_state_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.4" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_result_state_test" } }, { - "label": "defer_test.5", + "label": "capture_result_unload_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.5", + "command": "cd ${workspaceRoot}/bld/debug && meson test capture_result_unload_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.5" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test capture_result_unload_test" } }, { - "label": "defer_test.6", + "label": "defer_basic_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.6", + "command": "cd ${workspaceRoot}/bld/debug && meson test defer_basic_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.6" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_basic_test" } }, { - "label": "defer_test.7", + "label": "defer_nested_error_exception_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.7", + "command": "cd ${workspaceRoot}/bld/debug && meson test defer_nested_error_exception_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.7" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_nested_error_exception_test" } }, { - "label": "defer_test.8", + "label": "defer_nested_error_result_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test defer_test.8", + "command": "cd ${workspaceRoot}/bld/debug && meson test defer_nested_error_result_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_test.8" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_nested_error_result_test" } }, { - "label": "diagnostic_output_test", + "label": "defer_nested_new_error_exception_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test diagnostic_output_test", + "command": "cd ${workspaceRoot}/bld/debug && meson test defer_nested_new_error_exception_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test diagnostic_output_test" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_nested_new_error_exception_test" } }, { - "label": "error_capture_test.1", + "label": "defer_nested_new_error_result_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test error_capture_test.1", + "command": "cd ${workspaceRoot}/bld/debug && meson test defer_nested_new_error_result_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test error_capture_test.1" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_nested_new_error_result_test" } }, { - "label": "error_capture_test.2", + "label": "defer_nested_success_exception_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test error_capture_test.2", + "command": "cd ${workspaceRoot}/bld/debug && meson test defer_nested_success_exception_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test error_capture_test.2" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_nested_success_exception_test" } }, { - "label": "error_test", + "label": "defer_nested_success_result_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test error_test", + "command": "cd ${workspaceRoot}/bld/debug && meson test defer_nested_success_result_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test error_test" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test defer_nested_success_result_test" } }, { - "label": "exception_capture_test", + "label": "diagnostic_info_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test exception_capture_test", + "command": "cd ${workspaceRoot}/bld/debug && meson test diagnostic_info_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test exception_capture_test" - } - }, - { - "label": "exception_test.1", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test exception_test.1", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test exception_test.1" - } - }, - { - "label": "exception_test.2", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test exception_test.2", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test exception_test.2" - } - }, - { - "label": "expect_test.1", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test expect_test.1", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test expect_test.1" - } - }, - { - "label": "expect_test.2", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test expect_test.2", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test expect_test.2" - } - }, - { - "label": "expect_test.3", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test expect_test.3", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test expect_test.3" - } - }, - { - "label": "expect_test.4", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test expect_test.4", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test expect_test.4" - } - }, - { - "label": "expect_test.5", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test expect_test.5", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test expect_test.5" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test diagnostic_info_test" } }, { @@ -221,6 +158,33 @@ "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test function_traits_test" } }, + { + "label": "handle_all_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test handle_all_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test handle_all_test" + } + }, + { + "label": "handle_some_basic_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test handle_some_basic_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test handle_some_basic_test" + } + }, + { + "label": "handle_some_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test handle_some_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test handle_some_test" + } + }, { "label": "is_error_type_test", "type": "shell", @@ -230,82 +194,76 @@ "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test is_error_type_test" } }, - { - "label": "multiple_errors_test", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test multiple_errors_test", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test multiple_errors_test" - } - }, { "label": "optional_test", "type": "shell", "command": "cd ${workspaceRoot}/bld/debug && meson test optional_test", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] } - }, - { - "label": "preload_test.1", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test preload_test.1", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test optional_test" } }, { - "label": "preload_test.2", + "label": "preload_basic_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test preload_test.2", + "command": "cd ${workspaceRoot}/bld/debug && meson test preload_basic_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_test.2" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_basic_test" } }, { - "label": "preload_test.3", + "label": "preload_nested_error_exception_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test preload_test.3", + "command": "cd ${workspaceRoot}/bld/debug && meson test preload_nested_error_exception_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_test.3" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_nested_error_exception_test" } }, { - "label": "preload_test.4", + "label": "preload_nested_error_result_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test preload_test.4", + "command": "cd ${workspaceRoot}/bld/debug && meson test preload_nested_error_result_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_test.4" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_nested_error_result_test" } }, { - "label": "preload_test.5", + "label": "preload_nested_new_error_exception_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test preload_test.5", + "command": "cd ${workspaceRoot}/bld/debug && meson test preload_nested_new_error_exception_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_test.5" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_nested_new_error_exception_test" } }, { - "label": "preload_test.6", + "label": "preload_nested_new_error_result_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test preload_test.6", + "command": "cd ${workspaceRoot}/bld/debug && meson test preload_nested_new_error_result_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_test.6" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_nested_new_error_result_test" } }, { - "label": "preload_test.7", + "label": "preload_nested_success_exception_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test preload_test.7", + "command": "cd ${workspaceRoot}/bld/debug && meson test preload_nested_success_exception_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_test.7" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_nested_success_exception_test" + } + }, + { + "label": "preload_nested_success_result_test", + "type": "shell", + "command": "cd ${workspaceRoot}/bld/debug && meson test preload_nested_success_result_test", + "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, + "windows": { + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test preload_nested_success_result_test" } }, { @@ -318,75 +276,57 @@ } }, { - "label": "result_capture_test", + "label": "result_bad_result_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_capture_test", + "command": "cd ${workspaceRoot}/bld/debug && meson test result_bad_result_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_capture_test" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_bad_result_test" } }, { - "label": "result_test.1", + "label": "result_state_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_test.1", + "command": "cd ${workspaceRoot}/bld/debug && meson test result_state_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_test.1" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_state_test" } }, { - "label": "result_test.2", + "label": "static_store_deduction_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_test.2", + "command": "cd ${workspaceRoot}/bld/debug && meson test static_store_deduction_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_test.2" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test static_store_deduction_test" } }, { - "label": "result_test.3", + "label": "static_store_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_test.3", + "command": "cd ${workspaceRoot}/bld/debug && meson test static_store_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_test.3" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test static_store_test" } }, { - "label": "result_test.4", + "label": "try_exception_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_test.4", + "command": "cd ${workspaceRoot}/bld/debug && meson test try_exception_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_test.4" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test try_exception_test" } }, { - "label": "result_test.5", + "label": "try_test", "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_test.5", + "command": "cd ${workspaceRoot}/bld/debug && meson test try_test", "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_test.5" - } - }, - { - "label": "result_test.6", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_test.6", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_test.6" - } - }, - { - "label": "result_void_capture_test", - "type": "shell", - "command": "cd ${workspaceRoot}/bld/debug && meson test result_void_capture_test", - "problemMatcher": { "base": "$gcc", "fileLocation": ["relative","${workspaceRoot}/bld/debug"] }, - "windows": { - "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test result_void_capture_test" + "command": "${workspaceRoot}/.vscode/msvc.bat && cd ${workspaceRoot}/bld/debug && meson test try_test" } } ] diff --git a/doc/leaf.adoc b/doc/leaf.adoc index ea6920c..487316a 100644 --- a/doc/leaf.adoc +++ b/doc/leaf.adoc @@ -6,8 +6,9 @@ = LEAF Low-latency Error Augmentation Framework for C++11 :toclevels: 3 -:toc: left +:toc: right :toc-title: +:sourcedir: . [abstract] == Abstract @@ -17,227 +18,36 @@ LEAF is a {CPP}11 error handling library. Features: ==== * Header-only, no dependencies. -* No dynamic memory allocations.footnote:[Except when error objects are transported between threads, see <>.] +* No dynamic memory allocations.footnote:[Except when error objects are transported between threads, see <> / <>.] -* Associate objects of arbitrary types with any failure -- when it is initially reported or at a later time. +* Any error-related object of any movable type is efficiently delivered to the correct error handler. -* Compatible with `std::error_code`, `errno` and any other error handling API. - -* Use with or without exception handling. +* Compatible with `std::error_code`, `errno` and any other error code type. * Support for multi-thread programming. +* Can be used with or without exception handling. + [.text-right] -https://github.com/zajo/leaf[GitHub] | <> | <> | <> | <> | <> | <> +https://github.com/zajo/leaf[GitHub] | <> | <> | <> ==== -NOTE: If you are at least an intermediate level developer and have seen `expected`/`outcome`-like interfaces before, and you are also wondering how the heck you can return a type-erased error object without using heap allocations, you may want to read the <> first. - [[introduction]] -== Two Minute Introduction +== Five Minute Introduction -Let's suppose that to handle errors from a given function `f` we need an error code and a file name. In addition -- depending on the error -- we may need a request ID, also of type string. In terms of LEAF, we can write: +We'll write a short but complete program that reads a text file in a buffer and prints it to `std::cout`, using LEAF to handle errors. For demonstration, we'll implement two versions: one using error codes, and one using exception handling. -[source,c++] ----- -{ - leaf::expect exp; //<1> +[[introduction-result]] +=== Using Error Codes - if( leaf::result r = f() ) - { - //Success! //<2> - } - else - { - bool matched = leaf::handle_error( exp, r, //<3> - - [ ]( error_code ec, e_file_name const & fn, e_request_id const & id ) - { - //<4> - }, - - [ ]( error_code ec, e_file_name const & fn ) - { - //<5> - } - - ); - } -} ----- -[.text-right] -`<>` | `<>` | <> - -<1> The `exp` object provides storage for an `error_code`, an `e_file_name` and an `e_request_id`, which `f()` (next line) may communicate in case it fails. -<2> Successful result values are stored inside of `r` of type `result`, which is a value-or-error variant type. -<3> The call to `f()` has failed; `handle_error` takes `exp`, `r`, and any number of error handling functions to be matched, in order, against the objects currently stored in `exp`, associated with `r`. -<4> Handle the case where we received `error_code`, `e_file_name` and `e_request_id` (associated with the failure reported in `r`). -<5> Handle the case where we only received `error_code` and `e_file_name` (associated with the failure reported in `r`). - -The `error_code` can be a simple `enum`: +First, we need an `enum` to define our error codes: [source,c++] ---- enum error_code { - ec1=1, - ec2, - .... -}; ----- - -Because both the file name and the request ID are strings, we will wrap each of them in a simple `struct`, so that the type system can tell them apart: - -[source,c++] ----- -struct e_file_name { std::string value; }; -struct e_request_id { std::string value; }; ----- - -Reporting an error with LEAF looks like this: - -[source,c++] ----- -leaf::result g() noexcept -{ - if( success ) - return T(....); <1> - else - return leaf::error( ec1, e_request_id{id} ); //<2> -} ----- -[.text-right] -`<>` | `<>` - -<1> Initializing the returned `result` with a `T` object indicates a success. -<2> Report an error, store the passed `error_code` and `e_request_id` in appropriate `expect` object(s) from calling scopes. Presumably, at this point we don't have access to a relevant file name, but that's fine -- we associate with the reported error what relevant information we do have: the error code and the request ID. - -Forwarding errors reported by a lower level function looks like this: - -[source,c++] ----- -leaf::result f( char const * file_name ) noexcept -{ - if( leaf::result r=g() ) - { - .... - return r; //<1> - } - else - return r.error( e_file_name{file_name} ); //<2> -} ----- -[.text-right] -`<>` | `<>` - -<1> Success, return `r`. -<2> Forward the error reported (by `g`) in `r`, storing `e_file_name` in appropriate `expect` object(s) from calling scopes. Note that `g` has no access to the `file_name` passed to `f` and therefore is unable to include it in the reported error; yet the `file_name` is very much relevant to that error. At this point the file name is associated with the error in addition to the relevant `error_code` and `e_request_id` which, presumably, `g` has already communicated. - -== What If I Want to Use Exception Handling? - -That would be a 1-minute introduction. :-) - -Let's assume that to handle exceptions from a given function `f` we need a file name. In addition -- depending on the error -- we may need a request ID, also of type string. In terms of LEAF, we can write: - -[source,c++] ----- -{ - leaf::expect exp; //<1> - - try - { - f(); - } - catch( my_error const & e ) - { - leaf::handle_exception( exp, e, //<2> - - [ ]( e_file_name const & fn, e_request_id const & id ) - { - //<3> - }, - - [ ]( e_file_name const & fn ) - { - //<4> - } - - ); - } -} ----- -[.text-right] -`<>` | <> - -<1> The `exp` object provides storage for an `e_file_name` and an `e_request_id`, which `f()` (next line) may communicate in case it throws. -<2> We caught a `my_error` exception; `handle_error` takes `exp`, `e`, and any number of error handling functions to be matched, in order, against the error objects currently stored in `exp`, associated with `e`. -<3> Handle the case where we received `e_file_name` and `e_request_id` (associated with the failure reported by `e`). -<4> Handle the case where we only received `e_file_name` (associated with the failure reported by `e`). - -Because both the file name and the request ID are strings, we will wrap each of them in a simple `struct`, so that the type system can tell them apart: - -[source,c++] ----- -struct e_file_name { std::string value; }; -struct e_request_id { std::string value; }; ----- - -Such error objects can be passed to LEAF at the point of the `throw`: - -[source,c++] ----- -T g() -{ - if( success ) - return T(....); <1> - else - throw leaf::exception( my_error(), e_request_id{id} ); //<2> -} ----- -[.text-right] -`<>` - -<1> Success, just return `T`. -<2> Throw a `my_error` object, store the passed `e_request_id` in appropriate `expect` object(s) from calling scopes. Presumably, at this point we don't have access to a relevant file name, but that's fine -- we associate with the exception object what relevant information we do have: the request ID. - -Exceptions can be augmented in exception-neutral contexts: - -[source,c++] ----- -void f( char const * file_name ) -{ - auto propagate = leaf::preload( e_file_name{file_name} ); //<1> - - g(); -} ----- -[.text-right] -<> - -<1> In case `g()` (next line) throws, the `file_name` will be associated with the exception object, in addition to the relevant `e_request_id` which, presumably, `g` has already communicated. Note that `g` has no access to the `file_name` passed to `f` and therefore is unable to include it in the reported error; yet the `file_name` is very much relevant to that error. - -TIP: When used with exception handling, LEAF can be viewed as <>. - -[[tutorial]] -== Tutorial - -We'll write a short but complete program, using LEAF to handle errors. We'll implement two versions, one that uses exception handling, and one that does not. To see the source code of the complete programs from this tutorial, follow these links: - -* https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[print_file_result.cpp] (without exception handling) -* https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[print_file_eh.cpp] (with exception handling) - -First, let's see how to use LEAF without exception handling. - -[[tutorial-noexcept]] -=== Using `result` - -We'll write a program that reads a text file in a buffer and prints it to `std::cout`, using LEAF to handle errors. First, we need an `enum` to define our error codes: - -[source,c++] ----- -enum error_code -{ - input_file_open_error=1, + bad_command_line = 1, + input_file_open_error, input_file_size_error, input_file_read_error, input_eof_error, @@ -245,9 +55,7 @@ enum error_code }; ---- -We don't need an enumerated value that indicates success. That's because we will use the handy class template `<>` as the return type in functions which may fail. It is a value-or-error variant type which holds a `T` except if initialized with a `leaf::<>`. - -To enable `leaf::error` to work with our `error_code` `enum`, we need to specialize the <> template: +Very nice, but how does LEAF know that this `enum` represents error codes and not, say, types of cold cuts sold at http://order.bcdeli.com/[Bay Cities Italian Deli]? It doesn't, unless we tell it: [source,c++] ---- @@ -258,243 +66,195 @@ namespace boost { namespace leaf { } } ---- -[TIP] --- -The `is_error_type` template needs not be specialized for: - -* types that define an accessible data member `value`, - -* `std::error_code`, - -* `boost::system::error_code`. --- - -Here is a function that reads data from a `FILE` into a buffer and reports the various errors which may occur (it returns `result` because in case of success it doesn't return a value): +Now, reading and printing a file may not seem like a complex job, but let's split it into several functions, each communicating failures using `leaf::result`: [source,c++] ---- -leaf::result file_read( FILE & f, void * buf, int size ) -{ - int n = fread( buf, 1, size, &f ); - if( ferror(&f) ) - return leaf::error( input_file_read_error, e_errno{errno} ); //<1> +//Parse the command line, return the file name. +leaf::result parse_command_line( int argc, char const * argv[ ] ); - if( n!=size ) - return leaf::error( input_eof_error ); //<2> +//Open a file for reading. +leaf::result> file_open( char const * file_name ); - return { }; //<3> -} +//Return the size of the file. +leaf::result file_size( FILE & f ); + +//Read size bytes from f into buf. +leaf::result file_read( FILE & f, void * buf, int size ); ---- -[.text-right] -`<>` | `<>` -<1> If `ferror` indicates an error, we return `input_file_read_error` and, because there is a relevant `errno` code, we _also_ pass that to the `leaf::<>` constructor (LEAF defines `struct e_errno { int value; }`). -<2> If `fread` reports that it couldn't read all of the data requested, we return `input_eof_error`. In this case there is no relevant `errno` to pass on, because this is not an error as far as `fread` is concerned. -<3> `result` can be initialized with `{ }` to indicate success. +What's this `result` template, you ask? It's a simple variant: it holds a value of type `T` or else it represents an error condition. This way, functions can communicate failures -- and we don't even need an error code to represent success. Which makes sense if you think about it. -NOTE: `leaf::error` may be passed objects of arbitrary movable types, however objects of types that are not specific to error handling -- such as `errno` values, which are of type `int` -- should be wrapped in a `struct`. By convention, such structs use the `e_` prefix and contain a single data member called `value`, e.g. `struct e_errno { int value; }`. + - + -For example, we could define `struct e_input_name { std::string value; }` and `struct e_output_name { std::string value; }` and LEAF will treat them as separate entities even though their `.value` members are of the same type `std::string`. - -Now, let's consider a possible caller of `file_read`, called `print_file`: +Now on to the `main` function: it will call functions as needed and handle all the errors that occur. Did I say *all* the errors? I did, so we'll use `leaf::handle_all`. It has the following signature: [source,c++] ---- -leaf::result print_file( char const * file_name ) -{ - leaf::result> f = file_open(file_name); - if( !f ) //<1> - return f.error(); //<2> - - auto propagate = leaf::preload( e_file_name{file_name} ); //<3> - - leaf::result s = file_size(*f.value()); - if( !s ) //<4> - return s.error(); //<5> - - std::string buffer( 1+s.value(), '\0' ); - leaf::result fr = file_read( *f.value, &buffer[0], buffer.size()-1 ); - if( !fr ) - return fr.error(); - - std::cout << buffer; - std::cout.flush(); - if( std::cout.fail() ) - return leaf::error( cout_error ); //<6> - - return { }; //<7> -} +template +<> handle_all( TryBlock && try_block, Handler && ... handler ); ---- -[.text-right] -`<>` | `<>` | <> -<1> If `file_open` returns an error... -<2> ...we forward it to the caller. Notice that we don't return `leaf::error()`, which would indicate a newly detected error; we return `f.error()`, which propagates the error already stored in `f`. -<3> `<>` takes any number of error objects and prepares them to become associated (automatically, at the time the returned object expires) with a `leaf::<>` value created in the future. The effect is that from this point on, any error returned or forwarded by `print_file` will have an associated file name, in addition to everything else passed to `leaf::<>` explicitly (`e_file_name` is defined as `struct e_file_name { std::string value; }`). -<4> If `file_size` returns an error... -<5> ...we forward it to the caller. -<6> If `std::cout` fails to write the buffer, we return `cout_error`. -<7> Success! +`TryBlock` is a function type, almost always a lambda, which is required to return an instance of the `result` template. The first thing `handle_all` does is invoke the `try_block`. If the returned object indicates success, `handle_all` returns the contained value; otherwise it calls a suitable error handling function from the `handler...` list. -Notice the repetitiveness in simply forwarding errors to the caller. LEAF defines two macros, `<>` and `<>`, which can help reduce the clutter: - -* The `LEAF_AUTO` macro takes two arguments, an identifier and a `result`. In case the passed `result` indicates an error, `LEAF_AUTO` returns that error to the caller (therefore control leaves the enclosing function). In case of success, `LEAF_AUTO` defines a variable, of type `T &` (using the provided identifier) that refers to the `T` object stored inside the passed `result`. - -* The `LEAF_CHECK` macro is designed to be used similarly in functions that return `result`, but of course it doesn't define a variable. - -Below is the same `print_file` function simplified using `LEAF_AUTO` and `LEAF_CHECK` (remember that the variables defined by `LEAF_AUTO` are not of type `result`, but of type `T &`; for example `s` used to be of type `result`, but now its type is simply `int &`): - -[source,c++] ----- -leaf::result print_file( char const * file_name ) -{ - LEAF_AUTO( f, file_open(file_name) ); //<1> - - auto propagate = leaf::preload( e_file_name{file_name} ); - - LEAF_AUTO( s, file_size(*f) ); //<2> - - std::string buffer( 1+s, '\0' ); - LEAF_CHECK( file_read(*f,&buffer[0],buffer.size()-1) ); //<3> - - std::cout << buffer; - std::cout.flush(); - if( std::cout.fail() ) - return leaf::error( cout_error ); - - return { }; -} ----- -[.text-right] -<> | <> | <> | `<>` - -<1> Call `file_open`, check for errors, unpack the returned `result>` and define a variable `f` of type `std::shared_ptr &` that refers to its `<>()`. -<2> Call `file_size`, check for errors, unpack the returned `result` and define a variable `s` of type `int &` that refers to its `value()`. -<3> Call `file_read`, check for errors (`file_read` returns `result`). - -Finally, let's look at the `main` function, which handles all errors in this program: +Now, let's see just what kind of a `TryBlock` will our `main` function pass to `handle_all`: [source,c++] ---- int main( int argc, char const * argv[ ] ) { - char const * fn = parse_command_line(argc,argv); - if( !fn ) - { - std::cout << "Bad command line argument" << std::endl; - return 1; - } + return leaf::handle_all( - //We expect error_code, e_file_name and e_errno objects to be associated - //with errors handled in this function. They will be stored inside of exp. - leaf::expect exp; - - if( auto r = print_file(fn) ) - { - return 0; //Success, we're done! - } - else - { - //Probe exp for the error_code object associated with the error stored in r. - switch( auto ec = *leaf::peek(exp,r) ) + [&]() -> leaf::result <1> { - case input_file_open_error: - { - //handle_error takes a list of functions (in this case only one). It attempts to - //match each function (in order) to objects currently available in exp, which - //are associated with the error value stored in r. If no function can be matched, - //handle_error returns false. Otherwise the matched function is invoked with - //the corresponding available error objects. - bool matched = leaf::handle_error( exp, r, + leaf::result file_name = parse_command_line(argc,argv); + if( !file_name ) + return file_name.error(); <2> - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - if( errn.value==ENOENT ) - std::cerr << "File not found: " << fn.value << std::endl; - else - std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; - } + auto propagate = leaf::preload( leaf::e_file_name{*file_name} ); <3> - ); - assert(matched); - return 2; - } + leaf::result> f = file_open(*file_name); + if( !f ) + return f.error(); <4> - case input_file_size_error: - case input_file_read_error: - case input_eof_error: - { - //In this case handle_error is given 3 functions. It will first check if both - //e_file_name and e_errno, associated with r, are avialable in exp; if not, it will - //next check if just e_errno is available; and if not, the last function (which - //takes no arguments) will always match to print a generic error message. - bool matched = leaf::handle_error( exp, r, + leaf::result s = file_size(**f); + if( !s ) + return s.error(); <4> - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; - }, + std::string buffer( 1 + *s, '\0' ); + leaf::result fr = file_read(**f, &buffer[0], buffer.size()-1); + if( !fr ) + return fr.error(); <4> - [ ] ( e_errno const & errn ) - { - std::cerr << "I/O error, errno=" << errn << std::endl; - }, + std::cout << buffer; + std::cout.flush(); + if( std::cout.fail() ) + return leaf::new_error( cout_error, leaf::e_errno{errno} ); <5> - [ ] - { - std::cerr << "I/O error" << std::endl; - } + return 0; + }, - ); - assert(matched); - return 3; - } + .... <6> - case cout_error: - { - //Report failure to write to std::cout, print the relevant errno. - bool matched = leaf::handle_error( exp, r, - - [ ] ( e_errno const & errn ) - { - std::cerr << "Output error, errno=" << errn << std::endl; - } - - ); - assert(matched); - return 4; - } - - //This catch-all case helps diagnose logic errors (presumably, missing case labels - //in the switch statement). - default: - { - std::cerr << "Unknown error code " << ec << ", cryptic information follows." << std::endl; //<7> - leaf::diagnostic_output(std::cerr,exp,r); - return 5; - } - } - } + ); <7> } ---- -[.text-right] -`<>` | <> | <> | <> +<1> Our `TryBlock` returns a `result`. In case of success, it will hold `0`, which will be returned from `main` to the OS. +<2> If `parse_command_line` returns an error, we return that error to `handle_all` (which invoked us). Otherwise, the returned `file_name` stores a value of type `char const *`, which we can access by "dereferencing" it: `*file_name`. +<3> From now on, all errors escaping this scope will automatically communicate the (now successfully parsed from the command line) file name (LEAF defines `struct e_file_name {std::string value;}`). It's as if every time one of the following functions reports an error, `preload` says "wait, put this `e_file_name` thing with the error, it's important!" +<4> Call more functions, forward each failure to the caller... +<5> ...but this is slightly different: we didn't get a failure via `result` from another function, this is our own error we've detected! We return a `new_error`, passing the `cout_error` error code and the system `errno` (LEAF defines `struct e_errno {int value;}`). +<6> List of error handler goes here. We'll see that later. +<7> This concludes the `handle_all` arguments -- as well as our program! -To summarize, when using LEAF without exception handling: +Nice and simple! Writing the `TryBlock`, we concentrate on the "no errors" code path -- if we encounter any error we just return it to `handle_all` for processing. Well, that's if we're being good and using RAII for automatic clean-up -- which we are, `shared_ptr` will automatically close the file for us. -* Functions that may fail return instances of `<>`, a value-or-error variant class template. -* In case a function detects a failure, the returned `result` can be initialized implicitly by returning `leaf::<>`, which may be passed any and all information we have that is relevant to the failure, in the form of objects of arbitrary movable types. -* When a lower level function reports an error, that error is forwarded to the caller, passing any additional relevant information available in the current scope. -* In order for any object passed to `leaf::<>` to be stored rather than discarded, the function that handles the error must contain an instance of the class template `<>` that provides the necessary storage for its type. -* Using `<>`, available objects associated with the `<>` value being handled can be matched to what is required in order to deal with that `error`. +Now let's look at the juicy second part of the call to `handle_all`, which lists our error handlers: -NOTE: The complete program from this tutorial is available https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[here]. There is also https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[another] version of the same program that uses exception handling to report errors (see <>). +[source,c++] +---- +int main( int argc, char const * argv[ ] ) +{ + return leaf::handle_all( + [&]() -> leaf::result + { + .... <1> + }, + + [ ](leaf::match, <2> + leaf::match, + leaf::e_file_name const & fn) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 1; + }, + + [ ](leaf::match, <3> + leaf::e_errno const & errn, + leaf::e_file_name const & fn) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 2; + }, + + [ ](leaf::match, <4> + leaf::e_errno const & errn, + leaf::e_file_name const & fn) + { + std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; + return 3; + }, + + [ ](leaf::match, <5> + leaf::e_errno const & errn) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 4; + }, + + [ ](leaf::match) <6> + { + std::cout << "Bad command line argument" << std::endl; + return 5; + }, + + [ ](leaf::error_info const & unmatched) <7> + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 6; + } + ); +} +---- + +<1> This is the `TryBlock` from the previous listing, it does all the work and bails out if it encounters an error. In that case, `handle_all` will consider the error handlers that follow, in order, and it will call the first one that can deal with the error: + +<2> This handler will be called if the error includes: + +pass:[•] an object of type `error_code` equal to `input_file_open_error`, and + +pass:[•] an object of type `leaf::e_errno` that has `.value` equal to `ENOENT`, and + +pass:[•] an object of type `leaf::e_file_name`. + +<3> This handler will be called if the error includes: + +pass:[•] an object of type `error_code` equal to `input_file_open_error`, and + +pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), and + +pass:[•] an object of type `leaf::e_file_name`. + +<4> This handler will be called if the error includes: + +pass:[•] an object of type `error_code` equal to any of `input_file_size_error`, `input_file_read_error`, `input_eof_error`, and + +pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), and + +pass:[•] an object of type `leaf::e_file_name`. + +<5> This handler will be called if the error includes: + +pass:[•] an object of type `error_code` equal to `cout_error`, and + +pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), + +<6> This handler will be called if the error includes an object of type `error_code` equal to `bad_command_line`. + +<7> This last handler matches any error: it prints diagnostic information to help debug logic errors in the program, since it failed to match an appropriate error handler to the error condition it encountered. In this program this handler will never be called, but it is required by `handle_all` because, well, it must handle all errors (the alternative is to use `handle_some` instead, which doesn't require a "catch all" last-resort handler; instead, if it fails to find a suitable handler for an error, it returns the error to its caller). + +To conclude this introduction, let's look at one of the error-reporting functions that our `TryBlock` calls, for example `file_open`: + +[source,c++] +---- +leaf::result> file_open( char const * file_name ) +{ + if( FILE * f = fopen(file_name,"rb") ) + return std::shared_ptr(f,&fclose); + else + return leaf::new_error( input_file_open_error, leaf::e_errno{errno} ); +} +---- + +If `fopen` succeeds, we return a `shared_ptr` which will automatically call `fclose` as needed. If `fopen` fails, we report an error by calling `new_error`, which takes any number of error objects to send with the error. In this case we pass the error code (`input_file_open_error`), as well as the system `errno` (LEAF defines `struct e_errno {int value;}`). + +TIP: The complete program from this tutorial is available https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[here]. The https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[other] version of the same program uses exception handling to report errors (see <>). ''' -[[tutorial-eh]] +[[introduction-eh]] === Using Exception Handling And now, we'll write the same program that reads a text file in a buffer and prints it to `std::cout`, this time using exceptions to report errors. @@ -516,245 +276,313 @@ struct input_file_read_error : virtual input_file_error { }; struct input_eof_error : virtual input_file_error { }; ---- -NOTE: To avoid ambiguities in the dynamic type conversion which occurs when catching a base type, it is generally recommended to use virtual inheritance in exception type hierarchies. +NOTE: To avoid ambiguities in the dynamic type conversion which occur when catching a base type, it is generally recommended to use virtual inheritance in exception type hierarchies. -Here is a function that reads data from a file into a buffer and throws exceptions to communicate failures: +We'll split the job into several functions, each communicating failures by throwing exceptions: [source,c++] ---- -void file_read( FILE & f, void * buf, int size ) -{ - int n = fread( buf, 1, size, &f ); +//Parse the command line, return the file name. +char const * parse_command_line( int argc, char const * argv[ ] ); - if( ferror(&f) ) - throw leaf::exception( input_file_read_error(), e_errno{errno} ); //<1> +//Open a file for reading. +std::shared_ptr file_open( char const * file_name ); - if( n!=size ) - throw input_eof_error(); //<2> -} +//Return the size of the file. +int file_size( FILE & f ); + +//Read size bytes from f into buf. +void file_read( FILE & f, void * buf, int size ); ---- -[.text-right] -`<>` -<1> If `ferror` indicates an error, we throw `input_file_read_error` and, because there is a relevant `errno` code, we pass that to the `<>` function template _also_ (LEAF defines `struct e_errno { int value; }`). -<2> If `fread` reports that it couldn't read all of the data requested, we throw `input_eof_error`. In this case there is no relevant `errno` to pass on, because this is not an error as far as `fread` is concerned. - -NOTE: `leaf::error` may be passed objects of arbitrary movable types, however objects of types that are not specific to error handling -- such as `errno` values, which are of type `int` -- should be wrapped in a `struct`. By convention, such structs use the `e_` prefix and contain a single data member called `value`, e.g. `struct e_errno { int value; }`. + - + -Similarly, we could define `struct e_input_name { std::string value; }` and `struct e_output_name { std::string value; }` and LEAF will treat them as separate entities even though their `.value` members are of the same type `std::string`. - -Now, let's consider a possible caller of `file_read`, called `print_file`: +The `main` function brings everything together and handles all the exceptions that are thrown, but instead of using `try`, it will use the function template `leaf::try_`, which has the following signature: [source,c++] ---- -void print_file( char const * file_name ) -{ - std::shared_ptr f = file_open( file_name ); //<1> - - auto propagate1 = leaf::preload( e_file_name{file_name} ); //<2> - - std::string buffer( 1+file_size(*f), '\0' ); //<3> - file_read( *f, &buffer[0], buffer.size()-1 ); - - auto propagate2 = leaf::defer( [ ] { return e_errno{errno}; } ); //<4> - std::cout << buffer; - std::cout.flush(); -} +template +<> try_( TryBlock && try_block, Handler && ... handler ); ---- -[.text-right] -<> | <> -<1> `std::shared_ptr file_open( char const * file_name)` throws on error. -<2> `<>` takes any number of error objects and prepares them to become associated (automatically, at the time the returned object expires) with an exception thrown in the future. The effect is that from this point on, a file name will be associated with any exception escaping `print_file`, in addition to everything else passed earlier to the `leaf::<>` function template explicitly (`e_file_name` is defined as `struct e_file_name { std::string value; }`). -<3> `int file_size( FILE & f )` throws on error. -<4> `<>` is similar to `preload`: it prepares an error object to become associated with an exception thrown in the future, but instead of taking the object itself, `defer` takes a function that returns it. The function is invoked in the returned object's destructor, at which point it becomes associated with the exception being propagated. Assuming `std::cout` is configured to throw on error, the effect of this line is that those exceptions will have the relevant `errno` associated with them. +`TryBlock` is a function type, almost always a lambda; `try_` simply returns the value returned by the `try_block`, catching any exception it throws, in which case `try_` calls a suitable error handling function from the `handler...` list. -Finally, let's consider the `main` function, which is able to handle exceptions thrown by `print_file`: +Let's look at the `TryBlock` our `main` function passes to `try_`: [source,c++] ---- int main( int argc, char const * argv[ ] ) { - //Configure std::cout to throw on error. - std::cout.exceptions ( std::ostream::failbit | std::ostream::badbit ); + std::cout.exceptions(std::ostream::failbit | std::ostream::badbit); <1> - //We expect e_file_name and e_errno objects to be associated with errors - //handled in this function. They will be stored inside of exp. - leaf::expect exp; + return leaf::try_( - try - { - print_file(parse_command_line(argc,argv)); - return 0; - } - catch( bad_command_line const & ) - { - std::cout << "Bad command line argument" << std::endl; - return 1; - } - catch( input_file_open_error const & e ) - { - //handle_exception takes a list of functions (in this case only one). It attempts to - //match each function (in order) to objects currently available in exp, which - //are associated with the error value stored in e. If no function can be matched, - //handle_exception returns false. Otherwise the matched function is invoked with - //the corresponding available error objects. - leaf::handle_exception( exp, e, + [&] <2> + { + char const * file_name = parse_command_line(argc,argv); <3> - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - if( errn.value==ENOENT ) - std::cerr << "File not found: " << fn.value << std::endl; - else - std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; - } + auto propagate = leaf::preload( leaf::e_file_name{file_name} ); <4> - ); - return 2; - } - catch( input_error const & e ) - { - //In this case handle_exception is given 3 functions. It will first check if both - //e_file_name and e_errno, associated with e, are avialable in exp; if not, it will - //next check if just e_errno is available; and if not, the last function (which - //takes no arguments) will always match to print a generic error message. - leaf::handle_exception( exp, e, + std::shared_ptr f = file_open( file_name ); - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - std::cerr << "Input error, " << fn.value << ", errno=" << errn << std::endl; - }, + std::string buffer( 1+file_size(*f), '\0' ); + file_read(*f,&buffer[0],buffer.size()-1); - [ ] ( e_errno const & errn ) - { - std::cerr << "Input error, errno=" << errn << std::endl; - }, + auto propagate2 = leaf::defer([ ] { return leaf::e_errno{errno}; } ); <5> + std::cout << buffer; + std::cout.flush(); - [ ] - { - std::cerr << "Input error" << std::endl; - } + return 0; + }, - ); - return 3; - } - catch( std::ostream::failure const & e ) - { - //Report failure to write to std::cout, print the relevant errno. - leaf::handle_exception( exp, e, + .... <6> - [ ] ( e_errno const & errn ) - { - std::cerr << "Output error, errno=" << errn << std::endl; - } - - ); - return 4; - } - catch(...) - { - //This catch-all case helps diagnose logic errors (presumably, missing catch). - std::cerr << "Unknown error, cryptic information follows." << std::endl; - leaf::diagnostic_output_current_exception(std::cerr,exp); - return 5; - } + ); <7> } ---- -[.text-right] -`<>` | <> | <> +<1> Configure `std::cout` to throw on error. +<2> Except if it throws, our `TryBlock` returns `0`, which will be returned from `main` to the OS. +<3> If any of the functions we call throws, `try_` will find an appropriate handler to invoke. We'll look at that later. +<4> From now on, all exceptions escaping this scope will automatically communicate the (now successfully parsed from the command line) file name (LEAF defines `struct e_file_name {std::string value;}`). It's as if every time one of the following functions throws an exception, `preload` says "wait, put this `e_file_name` thing with the exception, it's important!" +<5> `defer` is similar to `preload`, but instead of the error object, it takes a function that returns it. From this point on, when an exception escapes this scope, `defer` will call the passed function and include the returned `e_errno` with the exception (LEAF defines `struct e_errno {int value;}`). +<6> List of error handler goes here. We'll see that later. +<7> This concludes the `try_` arguments -- as well as our program! -To summarize, when using LEAF with exception handling: +As it is always the case when using exception handling, as long as our `TryBlock` is exception-safe, we can concentrate on the "no errors" code path. Of course, our `TryBlock` is exception-safe, since `shared_ptr` will automatically close the file for us in case an exception is thrown. -* In case a function detects a failure, it may throw instances of the `leaf::<>` class template, initializing it with any number of error objects, to associate with the exception any information it has that is relevant to the failure. -* Alternatively it may use `<>` to associate error objects with an exception object (of any type) thrown later on, including exceptions thrown by third-party code. -* In order for any error object passed to the `leaf::<>` function template to be stored rather than discarded, the function that catches the exception must contain an instance of the class template `<>` that provides the necessary storage for its type. -* Using `<>`, available error objects associated with the exception being handled can be matched to what is required in order to deal with that exception. +Now let's look at the second part of the call to `try_`, which lists the error handlers: -NOTE: The complete program from this tutorial is available https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[here]. There is also https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[another] version of the same program that does not use exception handling to report errors (see <>). +[source,c++] +---- +int main( int argc, char const * argv[ ] ) +{ + std::cout.exceptions(std::ostream::failbit | std::ostream::badbit); <1> + + return leaf::try_( + [&] + { + .... <2> + }, + + [ ](leaf::catch_, <3> + leaf::match, + leaf::e_file_name const & fn) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 1; + }, + + [ ](leaf::catch_, <4> + leaf::e_errno const & errn, + leaf::e_file_name const & fn ) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 2; + }, + + [ ](leaf::catch_, <5> + leaf::e_errno const & errn, + leaf::e_file_name const & fn ) + { + std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; + return 3; + }, + + [ ](leaf::catch_, <6> + leaf::e_errno const & errn ) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 4; + }, + + [ ](leaf::catch_) <7> + { + std::cout << "Bad command line argument" << std::endl; + return 5; + }, + + [ ](leaf::error_info const & unmatched) <8> + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 6; + } ); +} +---- + +<1> Configure `std::cout` to throw on error. + +<2> This is the `TryBlock` from the previous listing; if it throws, `try_` will catch the exception, then consider the error handlers that follow, in order, and it will call the first one that can deal with the error: + +<3> This handler will be called if: + +pass:[•] an `input_file_open_error` exception was caught, with + +pass:[•] an object of type `leaf::e_errno` that has `.value` equal to `ENOENT`, and + +pass:[•] an object of type `leaf::e_file_name`. + +<4> This handler will be called if: + +pass:[•] an `input_file_open_error` exception was caught, with + +pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), and + +pass:[•] an object of type `leaf::e_file_name`. + +<5> This handler will be called if: + +pass:[•] an `input_error` exception was caught (which is a base type), with + +pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), and + +pass:[•] an object of type `leaf::e_file_name`. + +<6> This handler will be called if: + +pass:[•] an `std::ostream::failure` exception was caught, with + +pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), + +<7> This handler will be called if a `bad_command_line` exception was caught. + +<8> If `try_` fails to find an appropriate handler, it will re-throw the exception. But this is the `main` function which should handle all exceptions, so this last handler matches any error and prints diagnostic information, to help debug logic errors. + +To conclude this introduction, let's look at one of the error-reporting functions that our `TryBlock` calls, for example `file_open`: + +[source,c++] +---- +std::shared_ptr file_open( char const * file_name ) +{ + if( FILE * f = fopen(file_name,"rb") ) + return std::shared_ptr(f,&fclose); + else + throw leaf::exception( input_file_open_error(), leaf::e_errno{errno} ); +} +---- + +If `fopen` succeeds, it returns a `shared_ptr` which will automatically call `fclose` as needed. If `fopen` fails, we throw the exception object returned by `leaf::exception`, which takes as its first argument an exception object, followed by any number of error objects to send with it. In this case we pass the system `errno` (LEAF defines `struct e_errno {int value;}`). The returned object can be caught as `input_file_open_error`. + +NOTE: `try_` can work with any exception, not only exceptions thrown using `leaf::exception`. + +TIP: The complete program from this tutorial is available https://github.com/zajo/leaf/blob/master/example/print_file_eh.cpp?ts=3[here]. The https://github.com/zajo/leaf/blob/master/example/print_file_result.cpp?ts=3[other] version of the same program does not use exception handling to report errors (see the <>). [[synopsis]] == Synopsis -=== `expect.hpp` -include::synopses/expect.adoc[] +=== Reporting Errors + +==== `error.hpp` +include::{sourcedir}/synopses/error.adoc[] ''' -=== `error.hpp` -include::synopses/error.adoc[] +==== `common.hpp` +include::{sourcedir}/synopses/common.adoc[] ''' -=== `preload.hpp` -include::synopses/preload.adoc[] +==== `result.hpp` +include::{sourcedir}/synopses/result.adoc[] ''' -=== `error_capture.hpp` -include::synopses/error_capture.adoc[] +==== `throw.hpp` +include::{sourcedir}/synopses/throw.adoc[] ''' -=== `result.hpp` -include::synopses/result.adoc[] - -''' - -=== `common.hpp` -include::synopses/common.adoc[] - -''' - -=== `exception.hpp` -include::synopses/exception.adoc[] - -''' - -=== `diagnostic_output_current_exception.hpp` -include::synopses/diagnostic_output_current_exception.adoc[] +==== `exception_to_result.hpp` +include::{sourcedir}/synopses/exception_to_result.adoc[] [.text-right] -`<>` +<> -=== `exception_capture.hpp` -include::synopses/exception_capture.adoc[] +''' + +==== `preload.hpp` +include::{sourcedir}/synopses/preload.adoc[] + +''' + +=== Error Handling + +==== `handle.hpp` +include::{sourcedir}/synopses/handle.adoc[] + +''' + +==== `try.hpp` +include::{sourcedir}/synopses/try.adoc[] + +''' + +=== Multi-Thread Programming + +==== `capture_result.hpp` +include::{sourcedir}/synopses/capture_result.adoc[] + +[.text-right] +<> + +''' + +==== `capture_exception.hpp` +include::{sourcedir}/synopses/capture_exception.adoc[] + +[.text-right] +<> [[reference]] == Reference -[[wrapping]] -=== Wrapping of Error Types +[[e_objects]] +=== E-Objects -With LEAF, users can efficiently associate with errors or with exceptions any number of values that pertain to a failure. These values may be of any no-throw movable type for which the <> template is properly specialized. +[[is_error_type]] +==== `is_error_type` -However, when transporting objects of general types that are not specific to error handling, each value should be enclosed in a C-`struct` that acts as its compile-time identifier and gives it semantic meaning. Examples: +With LEAF, users can efficiently associate with errors or with exceptions any number of values that pertain to a failure. These values may be of any no-throw movable type `E` for which `<>::value` is `true`. The expectation is that this template will be specialized as needed for e.g. all user-defined error enums. + +Throughout this text, types for which `is_error_type` is `true` are called E-types. Objects of those types are called error objects or E-objects. + +The main `is_error_type` template is defined so that `is_error_type::value` is `true` when `E` is: + +* `std::error_code`, +* `boost::system::error_code`, +* `std::exception_ptr`, +* any type `E` for which `std::is_base_of::value` is `true`, +* any type which defines an accessible data member `value`. + +Often, error values that need to be communicated are of generic types (e.g. `std::string`). Such values should be enclosed in a C-`struct` that acts as their compile-time identifier and gives them semantic meaning. Examples: [source,c++] ---- struct e_input_name { std::string value; }; struct e_output_name { std::string value; }; -struct e_minimum_temperature { int value; }; -struct e_maximum_temperature { int value; }; +struct e_minimum_temperature { float value; }; +struct e_maximum_temperature { float value; }; ---- -Various LEAF functions take a list of error objects to associate with an `<>` value. For example, to indicate an error, a function that returns a `<>` may use something like: - -[source,c++] ----- -return leaf::error( my_error_code, e_input_name{n1}, e_output_name{n2} ); ----- - -Similar types that define an accessible data member `value` can be used with LEAF without having to specialize the `is_error_type` template. By convention such types use the `e_` prefix. +By convention, the enclosing C-`struct` names use the `e_` prefix. ''' -=== Diagnostic Information +[[propagation]] +==== Propagation -LEAF will attempt to print error objects in various `diagnostic_output` overloads it defines. It will first attempt to use `operator<<` overload that takes the actual error object type (e.g. the enclosing `struct`, see <>). If such overload does not exist, the fallback is to attempt to use `operator<<` overload that takes the `.value` (in case the error object type defines it). If that also doesn't work, LEAF is unable to print values of that particular error type (this is permissible, not an error). +"To propagate" an E-object is to associate it with a particular <>, making it available to functions that handle that error. -Even with types that define a printable `.value`, the user may still want to overload `operator<<` for the enclosing `struct`, e.g.: +More formally, when an E-object is propagated, it is immediately moved to available storage in a `handle_some`, a `handle_all` or a `try_` scope currently active in the calling thread, where it becomes uniquely associated with a specific <> -- or discarded if storage is not available; see <>. + +Various LEAF functions take a list of E-objects to propagate. 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 <>: + +[source,c++] +---- +return leaf::new_error( ec, e_input_name{n1}, e_output_name{n2} ); +---- + +''' + +[[diagnostic_information]] +==== Diagnostic Information + +LEAF is able to automatically generate diagnostic messages that include information about all E-objects available to error handlers. For this purpose, it needs to be able to print objects of user-defined E-types. + +To do this, LEAF attempts to bind an unqualified call to `operator<<`, passing a `std::ostream` and the E-object. If that fails, it will also attempt to bind `operator<<` that takes the `.value` of the E-object. If that also doesn't compile, the E-object will not appear in diagnostic messages, though LEAF will still print its type. + +Even with E-types that define a printable `.value`, the user may still want to overload `operator<<` for the enclosing `struct`, e.g.: [source,c++] ---- @@ -769,922 +597,9 @@ struct e_errno }; ---- -The `e_errno` type above is designed to hold `errno` values. The defined `operator<<` overload will automatically include the output from `strerror` when `e_errno` values are printed by `diagnostic_output` overloads (LEAF defines `e_errno` in ``, together with other commonly-used error types). +The `e_errno` type above is designed to hold `errno` values. The defined `operator<<` overload will automatically include the output from `strerror` when `e_errno` values are printed (LEAF defines `e_errno` in ``, together with other commonly-used error types). -TIP: The output from `diagnostic_output` overloads is developer-friendly but not user-friendly. Therefore, `operator<<` overloads for error types should only print technical information in English, and should not attempt to localize strings or to format a message. Formatting a localized user-friendly message should be done at the time individual errors are handled. - -''' - -[[expect]] -=== Class Template `expect` - -include::synopses/expect.adoc[] - -All `expect` objects must use automatic storage duration. They are not copyable and are not movable. - -An `expect` object contains exactly `sizeof...(E)` _slots_, each slot providing storage for a single object of the corresponding type `E`. It is invalid to specify the same type more than once in `E...`; so, each type `E` uniquely identifies an `expect` slot. All slots are initially empty. - -Slots of the same type `E` across different `expect` objects (that belong to the calling thread) form a stack. The slot created last for a given type `E` is at the top of that stack. When an object is passed to the `leaf::<>` constructor, it is moved into the corresponding slot on the top of that stack, and is associated with that `leaf::error` value. If no `expect` objects contain a corresponding slot, the object passed to the `leaf::error` constructor is discarded (however, see <>). - -An object stored in an `expect` slot can be accessed in several different ways, all requiring the `leaf::error` value it was associated with. While an `expect` object can not store multiple values of the same type, this association guarantees that the object being accessed pertains to that specific `error` value (`error` values act as program-wide unique failure identifiers). - -When an `expect` object is destroyed, each stored object is moved to the corresponding slot one level below the top of the stack formed by the slots of the same type across different `expect` objects. If that stack is empty, the object is discarded. - -''' - -[[expect::expect]] -==== Constructor - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - expect::expect() noexcept; - -} } ----- - -Description: :: Initializes an empty `expect` instance. - -Postcondition: :: `<>

(*this,e)` returns a null pointer for any `P` and any `<>` value `e`. - -''' - -[[expect-dtor]] -==== Destructor - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - expect::~expect() noexcept; - -} } ----- - -Effects: :: Each stored object is moved to a corresponding slot in other existing `expect` instances according to the rules described `<>`. - -''' - -[[capture-expect]] -==== `capture` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - error_capture capture( expect & exp, error const & e ); - -} } ----- - -Effects: :: Moves all objects currently stored in `exp` and associated with the `leaf::<>` value `e`, into the returned `<>` object. The contents of the `error_capture` object are immutable and allocated on the heap. - -NOTE: `error_capture` objects are useful for transporting error objects across thread boundaries. - -''' - -[[handle_error-expect]] -==== `handle_error` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - struct uhnandled_error - { - static constexpr R value( error const & e ) noexcept; - }; - - template - <> handle_error( expect const & exp, error const & e, F && ... f ) noexcept; - -} } ----- - -Requirements: :: -* `sizeof...(F) > 0`; -* Each `f...` must be callable; -* All `f...` must return the same static type. - -Return type: :: The return type `R` of `handle_error` is deduced as the return type of the `f...` functions, except if their return type is `void`, in which case `R` is deduced as `bool`, and the behavior (described below) is as if the `f...` functions return `true`. - -Effects: :: Attempts to match, by type, the objects currently stored in `exp`, associated with the `<>` value `e`, with the arguments of each of the `f...` function objects, in order. -+ -If a complete match is found among `f...`, the matched function is called with the entire set of corresponding objects from `exp` (the function may not modify those values), and the returned value is forwarded to the caller of `handle_error`. -+ -Otherwise, `handle_error` returns `unhandled_error::value(e)`, where `R` is the deduced return type of `handle_error`. The main `unhadled_error` template is defined such that: -+ --- -* if `R` is `bool`, `unhandled_error::value(e)` returns `false`; -* else, if `R` is an instance of the `<>` template, `unhandled_error::value(e)` returns `e` (note, `result` can be implicitly initialized by the `<>` object `e`); -* else, if `std::is_integral::value`, the call to `unhandled_error::value(e)` returns `static_cast(-1)`; -* else, the call to `unhandled_error::value(e)` returns `R()`. --- -+ -NOTE: The `unhandled_error` template may be specialized for user-defined types as needed. - -Example: :: -+ -[source,c++] ----- -bool matched = handle_error( exp, e, - - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; - }, - - [ ] ( e_errno const & errn ) - { - std::cerr << "I/O error, errno=" << errn << std::endl; - } - -); ----- -+ -Assuming `struct e_file_name { std::string value; }` and `struct e_errno { int value; }`, the call to `handle_error` above will: + -+ -* Check if the `expect` object `exp` contains `e_file_name` and `e_errno` objects, associated with the `leaf::<>` value `e`. If it does, it will pass them to the first lambda function, then return `true`; -* Otherwise if it contains just `e_errno`, it will pass it to the second lambda function, then return `true`; -* Otherwise, `handle_error` returns `false`. - -''' - -[[peek-expect]] -==== `peek` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - P const * peek( expect const & exp, error const & e ) noexcept; - -} } ----- - -Returns: :: If `exp` currently stores an object of type `P` associated with the `<>` value `e`, returns a read-only pointer to that object. Otherwise returns a null pointer. - -''' - -[[diagnostic_output-expect]] -==== `diagnostic_output` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - void diagnostic_output( std::ostream & os, expect const & exp ); - - template - void diagnostic_output( std::ostream & os, expect const & exp, error const & e ); - -} } ----- - -Effects: :: Prints diagnostic information about the objects stored in `exp`. The second overload will only print diagnostic information about objects stored in `exp` which are associated with the `leaf::<>` value `e`. - -NOTE: The printing of each individual object is done by the rules described <>. - -''' - -[[error]] -=== Class `error` - -include::synopses/error.adoc[] - -Objects of class `error` are values that identify an error across the entire program. They can be copied, moved, assigned to, and compared to other error objects. They occupy as much memory, and are as efficient as `unsigned int`. - -When an `e...` sequence is passed to the `error` constructor or to <>, these objects are moved into matching storage provided by `<>` instances, where it is associated with the `error` object (which can later be passed to `<>` or `<>` to retrieve them). - -''' - -[[error::error]] -==== Constructor - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - error::error( E && ... e ) noexcept; - -} } ----- - -Requirements: :: `<>::value` must be `true` for each `E`. - -Effects: :: Each of the `e...` objects is either moved into the corresponding storage provided by `expect` instances (where it is associated with `*this`), or discarded. See `<>`. - -Postconditions: :: `*this` is a unique value across the entire program. The user may create any number of other `error` values that compare equal to `*this`, by copy, move or assignment, just like with any other value type. - -''' - -[[error::propagate]] -==== `propagate` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - error error::propagate( E && ... e ) const noexcept; - -} } ----- - -Effects: :: Each of the `e...` objects is either moved into the corresponding storage provided by `expect` instances (where it is associated with `*this`), or discarded. See `<>`. - -Returns: :: `*this`. - -''' - -[[operator_eq-error]] -==== `operator==` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - bool operator==( error const & e1, error const & e2 ) noexcept; - -} } ----- - -Returns: :: `true` if the two values `e1` and `e2` are equal, `false` otherwise. - -''' - -[[operator_neq-error]] -==== `operator!=` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - bool operator!=( error const & e1, error const & e2 ) noexcept; - -} } ----- - -Returns: :: `!(e1==e2)`. - -''' - -[[operator_shl-error]] -==== `operator<<` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - std::ostream & operator<<( std::ostream & os, error const & e ) - -} } ----- - -Effects: :: Prints an `unsigned int` value that uniquely identifies the value `e`. - -''' - -[[next_error_value]] -==== `next_error_value` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - error next_error_value() noexcept; - -} } ----- - -Returns: :: The `error` value which will be constructed the next time the `<>` constructor is invoked from the calling thread. -+ -This function can be used to associate error objects with the next `error` value to be reported. Use with caution, only when restricted to reporting errors using specific third-party types, incompatible with LEAF -- for example when reporting an error from a C callback. As soon as control exits this critical path, you should create and return a `leaf::error` object (which will be equal to the `error` object returned by the earlier call to `next_error_value`). - -IMPORTANT: `error` values are unique across the entire program. - -''' - -[[last_error_value]] -==== `last_error_value` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - error last_error_value() noexcept; - -} } ----- - -Returns: :: The last `error` value constructed by the calling thread. - -IMPORTANT: `error` values are unique across the entire program. - -''' - -[[is_error_type]] -==== `is_error_type` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - struct is_error_type - { - static constexpr bool value = <>; - }; - -} } ----- - -The `is_error_type` template should be specialized for each user-defined type which the user desires to be used as an error type within LEAF, for example: - -[source,c++] ----- -enum my_error -{ - error1, - error2, - .... -}; - -namespace boost { namespace leaf { - - template<> struct is_error_type: std::true_type { }; - -} } ----- - -This requirement is designed to trigger a diagnostic in case the user unintentionally passes some random object to the `leaf::error` constructor. - -[TIP] --- -The `is_error_type` template needs not be specialized for: - -* types that define an accessible data member `value`, - -* `std::error_code`, - -* `boost::system::error_code`. --- - -''' - -=== Automatic Propagation - -include::synopses/preload.adoc[] - -These two functions are used to automatically associate error objects with failures that propagate through the scope where they are invoked. - -''' - -[[preload]] -==== `preload` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - <> preload( E && ... e ) noexcept; - -} } ----- - -Requirements: :: `<>::value` must be `true` for each `E`. - -Effects: :: All `e...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: -* If a new `leaf::<>` value was created (in the calling thread) since the object returned by `preload` was created, the stored `e...` objects are propagated and become associated with the _last_ such `leaf::error` value; -* Otherwise, if `std::unhandled_exception()` returns `true`, the stored `e...` objects are propagated and become associated with the _first_ `leaf::error` value created later on; -* Otherwise, the stored `e...` objects are discarded. - -''' - -[[defer]] -==== `defer` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - <> defer( F && ... f ) noexcept; - -} } ----- - -Requirements: :: Each of the `f~i~` objects must be a function that does not throw exceptions, takes no arguments and returns an object of a no-throw movable type `E~i~` for which `<>::value` is `true`. - -Effects: :: All `f...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: -* If a new `leaf::<>` value was created (in the calling thread) since the object returned by `defer` was created, each of the stored `f...` is called, and the returned object is propagated and becomes associated with the _last_ such `leaf::error` value; -* Otherwise, if `std::unhandled_exception()` returns `true`, each of the stored `f...` is called, and the returned objects are propagated and become associated with the _first_ `leaf::error` value created later on; -* Otherwise, the stored `f...` objects are discarded. - -''' - -[[error_capture]] -=== Class `error_capture` - -include::synopses/error_capture.adoc[] - -Objects of class `error_capture` are similar to `<>` instances in that they contain error objects and can be examined by (their own overloads of) `<>` and `<>`. However, unlike `expect` objects, `error_capture` objects: - -* are immutable; -* are allocated on the heap; -* associate all of the stored objects with exactly one `error` value; -* when probed with `peek`/`handle_error`, the lookup is dynamic; -* define `noexcept` copy/move/assignment operations. - -The default constructor can be used to initialize an empty `error_capture`. Use `<>` to capture all objects associated with a given `error` value from a given `expect` object. - -[NOTE] --- -Typical use of `error_capture` objects is to transport error objects across threads, however they are rarely used directly. Instead: - -* With exception handling, use `<>` / `<>`; -* Without exception handling, simply return a <> `result` from a worker thread. --- - -''' - -[[error_capture::error_capture]] -==== Constructor - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - error_capture::error_capture() noexcept; - -} } ----- - -Effects: Initializes an empty `error_capture` instance. - -Postcondition: :: `<>

(*this,e)` returns a null pointer for any `P` and any `<>` value `e`. - -''' - -[[error_capture::operator_bool]] -==== Conversion to `bool` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - error_capture::operator bool() const noexcept; - -} } ----- - -Returns: :: `false` if `*this` is empty, `true` otherwise. - -''' - -[[error_capture::unload]] -==== `unload` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - error error::unload() noexcept; - -} } ----- - -Effects: :: The objects stored in `*this` are moved into storage provided by `<>` objects in the calling thread, as if each stored object is passed to the constructor of `<>`. - -Returns: :: The `error` value the unloaded objects are associated with. - -Postcondition: :: `!(*this)`. - -''' - -[[handle_error-error_capture]] -==== `handle_error` - -.#include -[source,c++] ----- - -namespace boost { namespace leaf { - - template - <> error_capture::handle_error( error_capture const & ec, F && ... f ) noexcept; - -} } ----- - -Requirements: :: -* `sizeof...(F) > 0`; -* Each `f...` must be callable; -* All `f...` must return the same static type. - -Return type: :: The return type `R` of `handle_error` is deduced as the return type of the `f...` functions, except if their return type is `void`, in which case `R` is deduced as `bool`, and the behavior (described below) is as if the `f...` functions return `true`. - -Effects: :: Attempts to match, by type, the objects currently stored in `ec`, with the arguments of each of the `f...` function objects, in order. -+ -If a complete match is found among `f...`, the matched function is called with the entire set of corresponding objects from `ec` (the function may not modify those values), and the returned value is forwarded to the caller of `handle_error`. -+ -Otherwise, `handle_error` returns `unhandled_error::value(e)`, where `R` is the deduced return type of `handle_error`, and `e` is the `<>` object captured in `ec`. The main `unhadled_error` template is defined such that: -+ --- -* if `R` is `bool`, `unhandled_error::value(e)` returns `false`; -* else, if `R` is an instance of the `<>` template, `unhandled_error::value(e)` returns `e` (note, `result` can be implicitly initialized by the `<>` object `e`); -* else, if `std::is_integral::value`, the call to `unhandled_error::value(e)` returns `static_cast(-1)`; -* else, the call to `unhandled_error::value(e)` returns `R()`. --- -+ -NOTE: The `unhandled_error` template may be specialized for user-defined types as needed. - -Example: :: -+ -[source,c++] ----- -bool matched = handle_error( ec, - - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - std::cerr << "Failed to access " << fn << ", errno=" << errn << std::endl; - }, - - [ ] ( e_errno const & errn ) - { - std::cerr << "I/O error, errno=" << errn << std::endl; - } - -); ----- -+ -Assuming `struct e_file_name { std::string value; }` and `struct e_errno { int value; }`, the call to `handle_error` above will: + -+ -* Check if the `error_capture` object `ec` contains `e_file_name` and `e_errno` objects. If it does, it will pass them to the first lambda function, then return `true`; -* Otherwise if it contains just `e_errno`, it will pass it to the second lambda function, then return `true`; -* Otherwise, `handle_error` returns `false`. - -''' - -[[peek-error_capture]] -==== `peek` - -.#include -[source,c++] ----- - -namespace boost { namespace leaf { - - template - P const * peek( error_capture const & ec ) noexcept; - -} } ----- - -Returns: :: If `ec` currently stores an object of type `P`, returns a read-only pointer to that object. Otherwise returns a null pointer. - -''' - -[[diagnostic_output-error_capture]] -==== `diagnostic_output` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - void diagnostic_output( std::ostream & os, error_capture const & ec ); - -} } ----- - -Effects: :: Prints diagnostic information about the objects stored in `ec`. - -NOTE: The printing of each individual object is done by the rules described <>. - -''' - -[[result]] -=== Class Template `result` - -include::synopses/result.adoc[] - -''' - -[[result::result]] -==== Constructors - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - result::result() noexcept; - - template - result::result( T const & v ); - - template - result::result( T && v ) noexcept; - - template - result::result( leaf::error const & e ) noexcept; - - template - result::result( leaf::error_capture const & ec ) noexcept; - -} } ----- - -A `result` object is in one of two states: - -* Value state, in which case it contains an object of type `T`, and `<>`/`<>` can be used to access the contained value. -* Error state, in which case it contains an object of type `<>` or an object of type `<>`, and calling `<>`/`<>` throws `leaf::<>`. - -To get a `result` object in error state, initialize it with a `leaf::error` or a `leaf::error_capture` . - -Otherwise a `result` is initialized in value state using the default constructor of `T`, or by copying or moving from `v`. - -IMPORTANT: A `result` that is in value state converts to `true` in boolean contexts. A `result` that is in error state converts to `false` in boolean contexts. - -''' - -[[result::operator_bool]] -==== Conversion to `bool` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - result::operator bool() const noexcept; - -} } ----- - -Returns: :: If `*this` was initialized in value state, returns `true`, otherwise returns `false`. See `<>`. - -''' - -[[result::value]] -==== `value`/`operator*` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - T const & result::value() const; - - template - T & result::value(); - - template - T const & result::operator*() const; - - template - T & result::operator*(); - -} } ----- - -Effects: :: If `*this` was initialized in value state, returns a reference to the stored value, otherwise throws `leaf::<>`. See `<>`. - -''' - -[[result::error]] -==== `error` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - template - leaf::error result::error( E && ... e ) noexcept; - -} } ----- - -This member function is designed to be used in `return` statements in functions that return `result` (or `leaf::<>`) to return an error to the caller. - -Effects: :: -* If `*this` is in value state, returns `leaf::<>(std::forward(e...))`, which begins propagating a new `error` value (as opposed to forwarding an existing `error` value); -* If `*this` is in error state, it stores either an `<>` or a `leaf::<>`: -** if `*this` stores an `<> cap`, `*this` is converted to store the `leaf::<>` value returned from `cap.<>()`, then -** if `*this` stores a `leaf::error` value `err`, returns `err.<>(std::forward(e...))`, which forwards the same `error` to the caller, augmenting it with the additional error objects `e...`. - -''' - -[[capture-result]] -==== `capture` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - result capture( expect & exp, result const & r ); - -} } ----- - -Returns: :: -* If `*this` is in value state, returns `*this`. -* If `*this` is in error state and stores an `<>` object, returns `*this`. -* If `*this` is in error state and stores a `leaf::<>` value `err`, returns `<>(exp,err)`. - -NOTE: For an example, see <`>>. - -''' - -[[handle_error-result]] -==== `handle_error` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - <> result::handle_error( expect const & exp, result const & r, F && ... f ) noexcept; - -} } ----- - -Preconditions: :: `!r`. - -Returns: :: -* If `r` stores an `<>` object `cap`, returns `<>(cap,f...)`. -* If `r` stores a `leaf::<>` value `err`, returns `<>(exp,err,f...)`. - -NOTE: The return type is deduced as described <>. - -Example: :: -+ -If `g` succeeds, `f` consumes its return value and indicates success to its caller. Otherwise, if `g` fails with a `my_error`, `f` handles it and, again, returns success; all other error types are automatically forwarded to the caller, due to `<>`. -+ -[source,c++] ----- -leaf::result f() -{ - leaf::expect exp; - if( leaf::result r = g() ) - { - //use r, then indicate success: - return { }; - } - else - { - //handle my_error, forward all other errors to the caller: - return leaf::handle_error( exp, r, - [ ]( my_error const & err ) -> result - { - //Deal with my_error: - .... - //Indicate success: - return { }; - } ); - } -} ----- - -''' - -[[peek-result]] -==== `peek` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - P const * peek( expect const & exp, result const & r ) noexcept; - -} } ----- - -Preconditions: :: `!r`. - -Returns: :: -* If `r` stores an `<>` object `cap`, returns `<>

(cap)`. -* If `r` stores a `leaf::<>` value `err`, returns `<>

(exp,err)`. - -''' - -[[diagnostic_output-result]] -==== `diagnostic_output` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - void diagnostic_output( std::ostream & os, expect const & exp, result const & r ); - -} } ----- - -Preconditions: :: `!r`. - -Returns: :: -* If `r` stores an `<>` object `cap`, returns `<>(os,cap)`. -* If `r` stores a `leaf::<>` value `err`, returns `<>(os,exp,err)`. - -''' - -[[bad_result]] -==== `bad_result` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - struct bad_result: std::exception { }; - -} } ----- - -This exception is thrown by `<>()`/`<>()` if they`re invoked for a `result` object that is in error state. - -''' - -[[LEAF_ERROR]] -==== `LEAF_ERROR` - -.#include -[source,c++] ----- -#define LEAF_ERROR(...) <> ----- - -Effects: :: `LEAF_ERROR(e...)` is equivalent to `leaf::<>(e...)`, except the current source location is automatically passed to the `leaf::error` constructor, in addition to `e...`, in a `<>` object. - -NOTE: `LEAF_ERROR` is designed for use in `return` expressions, to automatically communicate the location of the error being reported (but see <>). - -''' - -[[LEAF_AUTO]] -==== `LEAF_AUTO` - -.#include -[source,c++] ----- -#define LEAF_AUTO(v,r) auto _r_##v = r; if( !_r_##v ) return _r_##v.error(); auto & v = *_r_##v ----- - -''' - -[[LEAF_CHECK]] -==== `LEAF_CHECK` - -.#include -[source,c++] ----- -#define LEAF_CHECK(r) {auto _r = r; if( !_r ) return _r.error();} ----- - -''' - -[[common]] -=== Common Error Types -include::synopses/common.adoc[] - -This header defines some common error types which can be used directly. +TIP: These automatically-generated diagnostic messages are developer-friendly, but not user-friendly. Therefore, `operator<<` overloads for E-types should only print technical information in English, and should not attempt to localize strings or to format a user-friendly message; this should be done in error-handling functions specifically designed for that purpose. ''' @@ -1695,15 +610,15 @@ This header defines some common error types which can be used directly. ---- namespace boost { namespace leaf { - struct e_api_function { char const * value; }; + struct e_api_function {char const * value;}; } } ---- -The `e_api_function` type is designed to capture the name of the API function which returned an error. For example, if you're reporting an error from `fread`, you could use `leaf::e_api_function { "fread" }`. +The `e_api_function` type is designed to capture the name of the API function that failed. For example, if you're reporting an error from `fread`, you could use `leaf::e_api_function {"fread"}`. -WARNING: The passed value is stored as a C string (`char const *`), so you should only pass string literals for `value`. +WARNING: The passed value is stored as a C string (`char const *`), so `value` should only be initialized with a string literal. ''' @@ -1714,7 +629,7 @@ WARNING: The passed value is stored as a C string (`char const *`), so you shoul ---- namespace boost { namespace leaf { - struct e_file_name { std::string value; }; + struct e_file_name {std::string value;}; } } ---- @@ -1739,9 +654,7 @@ namespace boost { namespace leaf { } } ---- -`e_errno` is suitable to capture `errno`. `e_errno` objects use `strerror` to convert the `errno` code to a friendlier error message when `<>` is invoked. - -WARNING: It is a logic error to use `e_errno` with <>; it should be passed to the `error` <> directly, or used with <>. +To capture `errno`, use `e_errno`. When printed in automatically-generated diagnostic messages, `e_errno` objects use `strerror` to convert the `errno` code to string. ''' @@ -1757,6 +670,7 @@ namespace boost { namespace leaf { struct e_LastError { unsigned value; + friend std::ostream & operator<<( std::ostream & os, e_LastError const & err ); }; } @@ -1779,7 +693,7 @@ namespace boost { namespace leaf { } } ---- -`e_at_line` can be used to communicate the line number when communicating errors (for example parse errors) about a text file. +`e_at_line` can be used to communicate the line number when reporting errors (for example parse errors) about a text file. ''' @@ -1818,62 +732,826 @@ namespace boost { namespace leaf { } } ---- -The <>, <> and <> macros capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` into a `e_source_location` object, if there is currently `<>` storage available for it. When `<>` is invoked, all three items are printed. +The <>, <> and <> macros capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` into a `e_source_location` object. ''' -[[e_unexpected]] -==== `e_unexpected` +[[error_id]] +=== `error_id` + +include::{sourcedir}/synopses/error.adoc[] + +Values of type `error_id` identify an error across the entire program. They can be copied, moved, assigned to, and compared to other `error_id` objects. They occupy as much memory, and are as efficient as `unsigned int`. + +''' + +[[new_error]] +==== `new_error` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + error_id new_error( E && ... e ) noexcept; + +} } +---- + +Requirements: :: `<>::value` must be `true` for each `E`. + +Effects: :: Each of the `e...` objects is <> and uniquely associated with the returned value. + +Returns: :: A new `error_id` value, which is unique across the entire program. + +''' + +[[next_error]] +==== `next_error` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + error_id next_error() noexcept; + +} } +---- + +Returns: :: The `error_id` value which will be returned the next time <> is invoked from the calling thread. + +This function can be used to associate E-objects with the next `error_id` value to be reported. Use with caution, only when restricted to reporting errors via specific third-party types, incompatible with LEAF -- for example when reporting an error from a C callback. As soon as control exits this critical path, you should create a <> (which will be equal to the `error_id` object returned by the earlier call to `next_error`). + +''' + +[[last_error]] +==== `last_error` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + error_id last_error() noexcept; + +} } +---- + +Returns: :: The `error_id` value returned the last time <> was invoked from the calling thread. + +''' + +[[propagate]] +==== `propagate` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + error_id error_id::propagate( E && ... e ) const noexcept; + +} } +---- + +Effects: :: Each of the `e...` objects is <> and uniquely associated with `*this`. + +Returns: :: `*this`. + +''' + +[[operator_eq-error_id]] +==== `operator==` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + bool operator==( error_id const & e1, error_id const & e2 ) noexcept; + +} } +---- + +Returns: :: `true` if the two values `e1` and `e2` are equal, `false` otherwise. + +''' + +[[operator_neq-error_id]] +==== `operator!=` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + bool operator!=( error_id const & e1, error_id const & e2 ) noexcept; + +} } +---- + +Returns: :: `!(e1==e2)`. + +''' + +[[operator_shl-error_id]] +==== `operator<<` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + std::ostream & operator<<( std::ostream & os, error_id const & id ) + +} } +---- + +Effects: :: Prints an `unsigned int` value that uniquely identifies the value `id`. + +''' + +[[preload]] +=== `preload` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + <> preload( E && ... e ) noexcept; + +} } +---- + +Requirements: :: `<>::value` must be `true` for each `E`. + +Effects: :: All `e...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: +* If <> was invoked (by the calling thread) since the object returned by `preload` was created, the stored `e...` objects are <> and become uniquely associated with the _last_ such `leaf::error_id`; +* Otherwise, if `std::unhandled_exception()` returns `true`, the stored `e...` objects are propagated and will become uniquely associated with the _first_ `leaf::error_id` created later on; +* Otherwise, the stored `e...` objects are discarded. + +''' + +[[defer]] +=== `defer` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + <> defer( F && ... f ) noexcept; + +} } +---- + +Requirements: :: Each of the `f~i~` objects must be a function that does not throw exceptions, takes no arguments and returns an object of a no-throw movable type `E~i~` for which `<>::value` is `true`. + +Effects: :: All `f...` objects are forwarded and stored into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed: ++ +-- +* If <> was invoked (by the calling thread) since the object returned by `defer` was created, each of the stored `f...` is called, and each returned object is <> and becomes uniquely associated with the _last_ such `leaf::error_id`; +* Otherwise, if `std::unhandled_exception()` returns `true`, each of the stored `f...` is called, and each returned object is propagated and will become uniquely associated with the _first_ `leaf::error_id` created later on; +-- ++ +The stored `f...` objects are discarded. + +''' + +[[error_info]] +=== `error_info` [source,c++] ---- namespace boost { namespace leaf { - struct e_unexpected + class error_info { - char const * (*first_type)(); - int count; + //Constructors unspecified - friend std::ostream & operator<<( std::ostream & os, e_unexpected const & x ); + public: + + leaf::error_id const & error() const noexcept; + + bool exception_caught() const noexcept; + + std::exception const * exception() const noexcept; + + friend std::ostream & operator<<( std::ostream & os, error_info const & x ); }; } } ---- -Whenever an error object of unexpected type (a type for which there is currently no `<>` storage available) is passed to the `leaf::error` <> or other similar function, if there is currently `expect` storage available for `e_unexpected`, LEAF will communicate the type of the object in the first such occurrence, as well as the count of these occurrences regardless of type. +Handlers passed to <>, <> or <> may take an argument of type `error_info const &` to receive information about the error. + +The `error` member function returns the program-wide unique <> of the error. + +The `exception_caught` member function returns `true` if the handler that received `*this` is being invoked by <>, `false` if invoked by <> or <>. + +The `exception` member function returns a pointer to the `std::exception` subobject of the exception caught by `try_`, or `0` if that exception could not be converted to `std::exception`. It is illegal to call `exception` unless `exception_caught()` is `true`. + +The `operator<<` overload prints diagnostic information about each E-object currently stored in any active <>, <> or <> scope, but only if it is associated with the <> returned by `error()`. ''' -[[e_unexpected_diagnostic_output]] -==== `e_unexpected_diagnostic_output` +[[diagnostic_info]] +=== `diagnostic_info` [source,c++] ---- namespace boost { namespace leaf { - struct e_unexpected_diagnostic_output + class diagnostic_info { - <> + //Constructors unspecified - friend std::ostream & operator<<( std::ostream & os, e_unexpected_diagnostic_output const & x ); + public: + + friend std::ostream & operator<<( std::ostream & os, diagnostic_info const & x ); }; } } ---- -Whenever an error object of unexpected type (a type for which there is currently no `<>` storage available) is passed to the `leaf::error` <> or other similar function, if there is currently `expect` storage available for `e_unexpected_diagnostic_output`, even though the error object itself is discarded, it will show in the text printed by <>. +Handlers passed to <>, <> or <> may take an argument of type `diagnostic_info const &` if they need to print diagnostic information about the error. -WARNING: Using `e_unexpected_diagnostic_output` may allocate memory dynamically. +The message printed by `operator<<` includes the message printed by `error_info`, followed by basic information about E-objects that were communicated to LEAF (to be associated with the error) for which there was no storage available in any active <>, <> or <> scope (these E-objects were discarded by LEAF, because no handler needed them). -== Exception Handling Reference - -=== Working with Exception Objects -include::synopses/exception.adoc[] +The additional information is limited the type name of the first such E-object, as well as their total count. ''' +[[verbose_diagnostic_info]] +=== `verbose_diagnostic_info` + +[source,c++] +---- +namespace boost { namespace leaf { + + class verbose_diagnostic_info + { + //Constructors unspecified + + public: + + friend std::ostream & operator<<( std::ostream & os, verbose_diagnostic_info const & x ); + }; + +} } +---- + +Handlers passed to <>, <> or <> may take an argument of type `verbose_diagnostic_info const &` if they need to print diagnostic information about the error. + +The message printed by `operator<<` includes the message printed by `error_info`, followed by information about E-objects that were communicated to LEAF (to be associated with the error) for which there was no storage available in any active <>, <> or <> scope (these E-objects were discarded by LEAF, because no handler needed them). + +The additional information includes the types and the values of all such E-objects. + +WARNING: Using `verbose_diagnostic_info` will likely allocate memory dynamically. + +== `noexcept` API + +[[result]] +=== `result` + +include::{sourcedir}/synopses/result.adoc[] + +The `result` type can be returned by functions which produce a value of type `T` but may fail doing so. + +Requirements: :: `T` must be movable, and its move constructor may not throw. + +Invariant: :: A `result` object is in one of three states: +* Value state, in which case it contains an object of type `T`, and `<>`/`<>` can be used to access the contained value. +* Error state, in which case it contains an object of type <>, and calling `<>`/`<>` throws `leaf::<>`. +* Error-capture state, which is the same as the Error state, but in addition to the <> object, it holds captured E-objects. + +''' + +[[result::result]] +==== Constructors + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + result::result() noexcept; + + template + result::result( T && v ) noexcept; + + template + result::result( T const & v ); + + template + result::result( leaf::error_id const & err ) noexcept; + + template + result::result( result && ) noexcept; + + template + result::result( result const & ); + + template + template + result::result( result && ) noexcept; + + template + template + result::result( result const & ); + +} } +---- + +Requirements: :: `T` must be movable, and its move constructor may not throw. + +Effects: :: + +Establishes the `result` invariant: +* To get a `result` in <>, initialize it with an object of type `T` or use the default constructor. +* To get a `result` in <>, initialize it with an <> object. +* To get a `result` in <>, call <>. + +Throws: :: +* Initializing the `result` in Value state may throw, depending on which constructor of `T` is invoked; +* Copying a `result` in Value state throws any exceptions thrown by the `T` copy constructor; +* Other constructors do not throw. + +TIP: A `result` that is in value state converts to `true` in boolean contexts. A `result` that is not in value state converts to `false` in boolean contexts. + +''' + +[[result::operator_eq]] +==== `operator=` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + result & result::operator=( result && ) noexcept; + + template + result & result::operator=( result const & ); + + template + template + result & result::operator=( result && ) noexcept; + + template + template + result & result::operator=( result const & ); + +} } +---- + +Effects: :: Destroys `*this`, then re-initializes it using the appropriate `result` constructor. Basic exception-safety guarantee. + +''' + +[[result::operator_bool]] +==== Conversion to `bool` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + result::operator bool() const noexcept; + +} } +---- + +Returns: :: If `*this` is in <>, returns `true`, otherwise returns `false`. . + +''' + +[[result::value]] +==== `value`/`operator*` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + T const & result::value() const; + + template + T & result::value(); + + template + T const & result::operator*() const; + + template + T & result::operator*(); + +} } +---- + +Effects: :: If `*this` is in <>, returns a reference to the stored value, otherwise throws `leaf::<>`. + +''' + +[[result::error]] +==== `error` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + template + error_id result::error( E && ... e ) noexcept; + +} } +---- + +This member function is designed for use in `return` statements in functions that return `result` (or `leaf::<>`) to return an error to the caller. + +Returns: :: +* If `*this` is in <>, returns `<>(std::forward(e...))`, which creates a new error (as opposed to forwarding an existing error); +* If `*this` is in <>, all captured E-objects are <>, `*this` is converted to error state, and then +* If `*this` is in <>, returns `err.<>(std::forward(e...))`, where `err` is the <> stored in `*this`. + +''' + +[[bad_result]] +==== `bad_result` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + struct bad_result: std::exception { }; + +} } +---- + +This exception is thrown by `<>()`/`<>()` if they're invoked for a `result` object that is in not in <>. + +''' + +[[handle_some]] +=== `handle_some` + +[source,c++] +---- +namespace boost { namespace leaf { + + template + decltype(std::declval()()) + handle_some( TryBlock && try_block, Handler && ... handler ); + +} } +---- + +When errors are communicated by `<>`, the `handle_some` function template can be used to recognize and handle some errors, forwarding any unrecognized errors to the caller. + +Requirements: :: + +* The `try_block` must be a function callable with no arguments that returns `<>`; +* All `handler...` arguments must be functions that return the same type as the `try_block`, or types that convert implicitly to it (note: a `result` object can be initialized by an <> object, regardless of `T`); + +* Each of the `handler...` functions: +** may take arguments of <>, either by value or by `const &`, or as a `const *`; +** may take arguments, either by value or by `const &`, of the predicate type `<>`, where `E` is an E-type; +** may take an <> argument by `const &`; +** may take a <> argument by `const &`; +** may take a <> argument by `const &`; +** may not take any other types of arguments. + +Effects: :: + +When `handle_some` is invoked, LEAF reserves space inside of the `handle_some` scope (using automatic storage duration) to store the E-objects it would pass to the appropriate `handler` in case the `try_block` indicates an error. ++ +The list of E-types that `handle_some` needs to store internally is deduced automatically, using the following steps, in order: ++ +-- +* A type list is deduced by concatenating all argument types of all `handler...` functions; +* References and cv-qualifiers are stripped from each type in the list; +* The <>, <>, and <> types are removed from the list; +* Any duplicate types are removed. +-- ++ +Next, `handle_some` calls the `try_block` function, which generally would call other functions as needed. When any function needs to report an error, it passes one or more E-objects to LEAF. For example, a function that returns a `<>` can report an error with a call to <> in a `return` expression: ++ +[source,c++] +---- +if( file-open-fails ) + return leaf::new_error( error_code::file_open_error, e_file_name{fn} ); +---- ++ +As each E-type object is passed to LEAF, it is immediately moved to the `handle_some` (or <>, or <>) scope highest in the call stack that has available storage for that type, where it is uniquely associated with a specific <> (in this case, the value returned by `new_error`). Objects of E-types for which none of these scopes have available storage are discarded. + +Return Value: :: +If the `<>` object `r` returned by the `try_block` function indicates success, `handle_some` returns `r`. ++ +Otherwise, `handle_some` considers each of the `handler...` functions, in order, until it finds one that matches the reported error. The first matching handler is invoked and the `result` it returns is forwarded to the caller of `handle_some`; if no match is found, `handle_some` returns `r`. ++ +If the value `handle_some` is returning indicates success, all stored E-objects are discarded. Otherwise, each stored E-object is moved to appropriate storage available in `handle_some` (or <>, or <>) scopes higher up the call stack (each moved object retains its unique association with its specific `error_id` value). E-objects for which such storage is not available are discarded. + +Handler Matching Procedure: :: ++ +A `handler` matches the reported failure iff `handle_some` is able to produce values to pass as its arguments. As soon as it is determined that an argument value can not be produced, the current `handler` is dropped and the matching procedure continues with the next `handler`, if any. ++ +If `err` is the specific <> stored in the `result` returned by the `try_block`, each argument value `a`~i~ to be passed to the `handler` currently under consideration is produced as follows: ++ +-- +* If `a`~i~ is taken as `A`~i~ `const &` or by value: +** If an E-object of type `A`~i~, associated with `err`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. +** If `A`~i~ is of the predicate type `<>`, if an object of type `E`, associated with `err`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with a reference to the stored object; otherwise the handler is dropped. The handler is also dropped if the expression `a`~i~`()` evaluates to `false`. +* If `a`~i~ is of type `A`~i~ `const pass:[*]`, `handle_some` is always able to produce it: if an E-object of type `A`~i~, associated with `err`, is currently stored in the `handle_some` scope, `a`~i~ is initialized with the address of the stored object, otherwise it is initialized with `0`. +* If `a`~i~ is of type `error_info const &` or `diagnostic_info const &` or `verbose_diagnostic_info const &`, `handle_some` is always able to produce it. +* It is illegal to pass to `handle_some` a `handler` that takes any other argument types. +-- + +TIP: Because `handle_some` can always produce arguments of type `error_info const &`, `diagnostic_info const &` and `verbose_diagnostic_info const &`, a handler that only takes arguments of these types will match any error. + +''' + +[[handle_all]] +=== `handle_all` + +[source,c++] +---- +namespace boost { namespace leaf { + + template + typename std::remove_reference()().value())>::type + handle_all( TryBlock && try_block, Handler && ... handler ); + +} } +---- + +Requirements: :: + +`handle_all` has the same requirements as <>, with the following change: ++ +-- +* If `T` is the type returned by the `TryBlock`, all `handler...` arguments must be functions that return `T` or a type that converts implicitly to `T` (note: `handle_some` handlers are required to return `result`). +-- +And the following addition: +* At least one `handler` function (usually the last) must match any error. This is enforced at compile-time. + +Effects: :: + +The `handle_all` function works the same as <>, but because it is guaranteed to be able to match any error communicated by the `try_block` with a `handler`, it doesn't need to return a `result`. + +.Example +[source,c++] +---- +int main() +{ + return leaf::handle_all( + [ ]() -> leaf::result + { + //do work, bail out returning leaf::error_id for any failure + .... + + std::cout << "Success!"; + return 0; + }, + [ ] + { + std::cerr << "Error!"; + return 1; + } ); +} +---- + +''' + +[[match]] +=== `match` + +[source,c++] +---- +namespace boost { namespace leaf { + + template ::type... V> + struct match + { + using type = typename deduced-type::type; + type const & value; + + explicit match( E const & e ) noexcept; + + bool operator()() const noexcept; + }; + +} } +---- + +NOTE: The `match` template is useful only as argument to a handler function passed to <> (or `handle_all` or `try_`). + +Effects: :: + +* If `E` defines an accessible data member `value`: +** The type of the parameter pack `V...` is deduced as `decltype(std::declval().value)`; +** The `match` constructor initializes the `value` reference with `e.value`. + +* Otherwise: +** The type of the parameter pack `V...` is deduced as `E`; +** The `match` constructor initializes the `value` reference with `e`. + +The `match` template is a predicate function type: `operator()` returns `true` iff the expression `value == V~i~` is `true` for at least one of `V...` values. + +.Example 1: +[source,c++] +---- +struct error_code { int value; }; + +error_code e = {42}; + +match m1(e); +assert(!m1()); + +match m2(e); +assert(m2()); + +match m3(e); +assert(m3()); + +match m4(e); +assert(!m4()); +---- + +.Example 2: +[source,c++] +---- +enum error_code { e1=1, e2, e3 }; + +error_code e = e2; + +match m1(e); +assert(!m1()); + +match m2(e); +assert(m2()); + +match m3(e); +assert(m3()); + +match m4(e); +assert(!m4()); +---- + +''' + +[[capture_result]] +=== `capture_result` + +.#include +include::{sourcedir}/synopses/capture_result.adoc[] + +The `capture_result` function can be used to transport E-objects <> when `f` is invoked in one thread, to a different thread which handles errors through <> or <>. + +Requirements: :: +* `F` must be a function type that returns a `<>`; +* Each of the `E...` types must be an <>. + +Returns: :: A function object `fw` of unspecified type, which acts as a wrapper for `f`, taking the same argument types and returning the same `result` type. ++ +When the caller invokes `fw` (presumably from a worker thread) with arguments `a...` of types `A...`: ++ +-- +* Like <>, `fw` reserves storage in its scope for objects of the `E...` types, then +* invokes `f(std::forward(a...))`, which in general will call other functions as needed (any <> objects of the `E...` types are captured in the reserved storage). +-- ++ +If the returned `result` indicates success, it is forwarded to the caller; any E-objects stored in the `fw` scope are discarded. ++ +Otherwise, all stored E-objects are moved to dynamically-allocated memory, which is used to initialize a `result` object in <>. This object is returned to the caller of `fw`. ++ +The returned `result` can safely cross thread boundaries (presumably sent back to the main thread). When it is returned from the `try_block` function passed to <> or <>, its contents are <> (in that thread), and then error handling proceeds as usual. + +''' + +[[LEAF_ERROR]] +=== `LEAF_ERROR` + +.#include +[source,c++] +---- +#define LEAF_ERROR(...) <> +---- + +Effects: :: `LEAF_ERROR(e...)` is equivalent to `leaf::<>(e...)`, except the current source location is automatically passed to `new_error` in a `<>` object (in addition to all `e...` objects). + +''' + +[[LEAF_AUTO]] +=== `LEAF_AUTO` + +.#include +[source,c++] +---- +#define LEAF_AUTO(v,r)\ + auto _r_##v = r;\ + if( !_r_##v )\ + return _r_##v.error();\ + auto & v = *_r_##v +---- + +`LEAF_AUTO` is useful when calling a function that returns `result` (other than `result`), if the desired behavior is to forward any errors to the caller verbatim. + +Example: + +.Compute two int values, return their sum as a float, using LEAF_AUTO: +[source,c++] +---- +leaf::result compute_value(); + +leaf::result add_values() +{ + LEAF_AUTO(v1, compute_value()); + LEAF_AUTO(v2, compute_value()); + return v1 + v2; +} +---- + +Of course, we could write `add_value` without using `LEAF_AUTO`. This is equivalent: + +.Compute two int values, return their sum as a float, without LEAF_AUTO: +---- +leaf::result add_values() +{ + auto v1 = compute_value(); + if( !v1 ) + return v1.error(); + + auto v2 = compute_value(); + if( !v2 ) + return v2.error(); + + return *v1 + *v2; +} +---- + +''' + +[[LEAF_CHECK]] +=== `LEAF_CHECK` + +.#include +[source,c++] +---- +#define LEAF_CHECK(r)\ + {\ + auto _r = r;\ + if(!_r)\ + return _r.error();\ + } +---- + +`LEAF_CHECK` is useful when calling a function that returns `result`, if the desired behavior is to forward any errors to the caller verbatim. + +Example: + +.Try to send a message, then compute a value, report errors using LEAF_CHECK: +[source,c++] +---- +leaf::result send_message( char const * msg ); + +leaf::result compute_value(); + +leaf::result say_hello_and_compute_value() +{ + LEAF_CHECK(send_message("Hello!")); + return compute_value(); +} +---- + +Equivalent implementation without `LEAF_CHECK`: + +.Try to send a message, then compute a value, report errors without LEAF_CHECK: +---- +leaf::result add_values() +{ + auto r = send_message("Hello!"); + if( !r ) + return r.error(); + + return compute_value(); +} +---- + +== Exception Handling API + [[exception]] -==== `exception` +=== `exception` [source,c++] .#include @@ -1888,127 +1566,264 @@ namespace boost { namespace leaf { Requirements: :: * `Ex` must derive from `std::exception`. -* For all of E, `<>::value` is `true`. +* For each `E`~i~ int `E...`, `<>::value` is `true`. -Returns: :: An object of unspecified type which derives publicly from `Ex` *and* from class `<>` such that: +Returns: :: An object of unspecified type which derives publicly from `Ex` *and* from class <> such that: * its `Ex` subobject is initialized by `std::forward(ex)`; -* its `error` subojbect is initialized by `std::forward(e)...`. +* its `error_id` subojbect is initialized by `<>(std::forward(e)...`). -TIP: If thrown, the returned object can be caught as `Ex &` or as `leaf::<>`. +TIP: If thrown, the returned object can be caught as `Ex &` or as `leaf::<> &`. NOTE: To automatically capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` with the returned object, use <> instead of `leaf::exception`. ''' -[[get_error]] -==== `get_error` +[[try_]] +=== `try_` -.#include [source,c++] ---- namespace boost { namespace leaf { - error get_error( std::exception const & ex ) noexcept; + template + decltype(std::declval()()) + try_( TryBlock && try_block, Handler && ... handler ); } } ---- -Returns: :: -* If `auto e = dynamic_cast> const *>(&ex)` succeeds, returns `*e`. -* Othrewise, it returns an unspecified `leaf::error` value, which is "temporarily" associated with any and all currently unhandled exceptions. +Requirements: :: + +`try_` has the same requirements as <>, with the following change: + -IMPORTANT: A successful call to `<>` breaks this association. +-- +* The `try_block` is not required to -- and usually does not -- return a `result` (because, presumably, all failures are communicated by throwing exceptions). +-- +And the following addition: +* Each of the `handler...` functions may take arguments, either by value or by `const &`, of the predicate type `<>`, where `Ex` is a type that derives from `std::exception`. -This function is designed to augment exceptions with additional error objects in exception-neutral contexts, when it is known that at least some of the intercepted exceptions are not thrown using `<>`, and therefore do not derive from `leaf::error`. Example: +Effects: :: -[source,c++] ----- -try -{ - f(); -} -catch( std::exception const & ex ) -{ - leaf::get_error(ex).propagate( e_this{x}, e_that{y} ); - throw; -} ----- +`try_` works similarly to <> -- the difference is that `try_` catches exceptions thrown by the `try_block` function with a `catch(std::exception const &)` or, if that fails, `catch(...)`. -If it is known that all intercepted exceptions are thrown using `leaf::exception`, the following would be more optimal: +Return Value: :: -[source,c++] ----- -try -{ - f(); -} -catch( leaf::error e ) -{ - e.propagate( e_this{x}, e_that{y} ); - throw; -} ----- +* If the `try_block` succeeds, `try_` forwards the return value to the caller; +* Otherwise, it attempts to match the <> communicated with the caught exception with a `handler`. If that succeeds, `try_` forwards the return value from the matched `handler` to the caller; +* Otherwise, `try_` re-throws the caught exception. + +Handler Matching Procedure: :: + +Because each E-object stored in the `try_` scope (see <>) is uniquely associated with a specific <>, `try_` needs to extract an `error_id` value from the caught exception in order to access any currently stored E-object (`handle_some` and `handle_all` do not have this challenge, since the <> they need is readily available in the `<>` object communicating the failure). ++ +When throwing, users are encouraged to pass the exception object through the <> function template -- and `throw` the object it returns. This guarantees that the thrown exception transports a unique `error_id` value, just like `result` does. ++ +However, this isn't possible when we don't control the `throw` site, for example if the exception is thrown by a standard function. In this case, E-objects communicated to LEAF are associated with the `error_id` value returned by <>, which is a preview of sorts, of the `error_id` value that would be returned by the next call to <>. ++ +Similarly, if the exception object caught by `try_` does not transports an `error_id` value, E-objects are looked up using the `error_id` value returned by <> just before `try_` goes through its `handler`-matching search. If imperfect, this approach provides the needed association. ++ +NOTE: <> implements the procedure described above. ++ +With the `error_id` value thus obtained, the `handler`-matching procedure works the same as in <>, with the following addition to the way each argument value `a`~i~ to be passed to the `handler` currently under consideration is produced: + +* If `a`~i~ is taken as `A`~i~ `const &` or by value: +** If `A`~i~ is of the predicate type `<>`, `a`~i~ is initialized with the caught `std::exception const &`. The handler is dropped if the expression `a`~i~`()` evaluates to `false`. ''' -[[peek-exception]] -==== `peek` +[[catch_]] +=== `catch_` -.#include [source,c++] ---- namespace boost { namespace leaf { - template - P const * peek( expect const & exp, std::exception const & ex ) noexcept; + template + struct catch_ + { + std::exception const & value; + + explicit catch_( std::exception const & ex ) noexcept; + + bool operator()() const noexcept; + }; } } ---- -Effects: :: As if: `return leaf::<>

( exp, <>(ex) );` +NOTE: The `catch_` template is useful only as argument to a handler function passed to <>. + +Effects: :: + +The `catch_` constructor initializes the `value` reference with `ex`. + +The `catch_` template is a predicate function type: `operator()` returns `true` iff for at least one of `Ex`~i~ in `Ex...`, the expression `dynamic_cast(&value) != 0` is `true`. + +.Example: +[source,c++] +---- +struct exception1: std::exception { }; +struct exception2: std::exception { }; +struct exception3: std::exception { }; + +exception2 x; + +catch_ c1(x); +assert(!c1()); + +catch_ c2(x); +assert(c2()); + +catch_ c3(x); +assert(c3()); + +catch_ c4(x); +assert(!c4()); +---- ''' -[[handle_exception]] -==== `handle_exception` +[[get_error_id]] +=== `get_error_id` -.#include [source,c++] ---- namespace boost { namespace leaf { - template - void handle_exception( expect & exp, std::exception const & ex, F && ... f ); + error_id get_error_id( std::exception const & ex ) noexcept; } } ---- -Effects: :: Equivalent to: `if( !<>( exp, <>(ex), std::forward(f)...) ) throw;` +Returns: :: If the dynamic type of `ex` derives from <>, returns the `error_id` slice of `ex`. Otherwise, returns the result of <>. -IMPORTANT: In case the dynamic type of `ex` does not derive from `leaf::error` and the call to `handle_error` succeeds, the association between the `leaf::error` value returned by `<>` and the currently unhandled exceptions is broken. +NOTE: Please read the documentation for <> to understand why `get_error_id` is needed. + +TIP: Whenever possible, pass exceptions you throw throught the <> function template (or use <>). This ensures that the thrown exceptions derive from <>, which is always preferable to the <> hack. ''' -[[diagnostic_output-exception]] -==== `diagnostic_output` +[[exception_to_result]] +=== `exception_to_result` -.#include +include::{sourcedir}/synopses/exception_to_result.adoc[] + +This function can be used to catch exceptions from a lower-level library and conevrt them to `<>`. + +Returns: :: If `f` returns `T`, `exception_to_result` returns `result`. + +Effects: :: Invokes `f` in a `try` block, catching each individual exception type `Ex`, by value, proceeding as follows: ++ [source,c++] ---- -namespace boost { namespace leaf { - - template - void diagnostic_output( std::ostream & os, expect const & exp, std::exception const & ex ); - -} } +catch( Ex ex ) +{ + return leaf::new_error(std::move(ex)); +} +---- ++ +A final catch-all is added, as follows: ++ +[source,c++] +---- +catch(...) +{ + return leaf::new_error(std::current_exception()); +} ---- -Effects: :: Equivalent to: `<>( os, exp, <>(ex) );` +[WARNING] +-- +* Catching by value slices each individual exception object. +* The order of the types in the `Ex...` list is significant. +* `std::current_exception()` likely allocates memory dynamically. +* Handlers passed to <> / <> should take the converted-to-result exception objects by `const &` (whereas, in case exceptions are handled directly by <> handlers, you would use <> instead). +-- + +Example: + +[source,c++] +---- +int compute_answer_throws(); + +//Call compute_answer, convert exceptions to result +leaf::result compute_answer() +{ + return leaf::catch_exceptions< + ex_type1, + ex_type2>( compute_answer_throws() ); +} +---- + +Later, the `ex_type1` and `ex_type2` exceptions can be processed by <> / <>: + +[source,c++] +---- +return leaf::handle_some( + + [ ] -> leaf::result + { + LEAF_AUTO(answer, compute_answer()); + //Use answer + .... + return { }; + }, + + [ ]( ex_type1 const & ex1 ) + { + //Handle ex_type1 + .... + return { }; + }, + + [ ]( ex_type2 const & ex2 ) + { + //Handle ex_type2 + .... + return { }; + }, + + [ ]( std::exception_ptr const & p ) + { + //Handle any other exception from compute_answer. + .... + return { }; + } ); +---- + +''' + +[[capture_exception]] +=== `capture_exception` + +.#include +include::{sourcedir}/synopses/capture_exception.adoc[] + +The `capture_exception` function can be used to transport E-objects <> when `f` is invoked in one thread, to a different thread which handles errors through <>. + +Requirements: :: +* `F` must be a function type; +* Each of the `E...` types must be an <>. + +Returns: :: A function object `fw` of unspecified type, which acts as a wrapper for `f`, taking the same argument types and returning the same type as `f`. ++ +When the caller invokes `fw` (presumably from a worker thread) with arguments `a...` of types `A...`: ++ +-- +* Like <>, `fw` reserves storage in its scope for objects of the `E...` types, then +* invokes `f(std::forward(a...))`, which in general will call other functions as needed (any <> objects of the `E...` types are captured in the reserved storage); +-- ++ +Except if `f` throws, any E-objects stored in the `fw` scope are discarded and the returned value is forwarded to the caller. ++ +Otherwise, `fw` throws another exception object (of unspecified type) which holds all stored `E` objects and the original exception via `std::exception_ptr`. ++ +If this new exception is thrown by the `try_block` function passed to <> (presumably after it was transported to the main thread), its contents are <> (in that thread), and then error handling proceeds as usual. ''' [[LEAF_EXCEPTION]] -==== `LEAF_EXCEPTION` +=== `LEAF_EXCEPTION` [source,c++] .#include @@ -2021,7 +1836,7 @@ Effects: :: This is a variadic macro which forwards its arguments to the functio ''' [[LEAF_THROW]] -==== `LEAF_THROW` +=== `LEAF_THROW` [source,c++] .#include @@ -2031,99 +1846,13 @@ Effects: :: This is a variadic macro which forwards its arguments to the functio Effects: :: Throws the exception object returned by <>. -''' - -[[diagnostic_output_current_exception]] -=== Diagnostic Output for the Current Exception -include::synopses/diagnostic_output_current_exception.adoc[] - -Effects: :: This function prints a developer-friendly (but not user-friendly) diagnostic information about the current exception to `os`. - -Example: :: -+ -[source,c++] ----- -int main() -{ - leaf::expect exp; - - try - { - f(); - return 0; - } - catch(...) - { - leaf::diagnostic_output_current_exception(std::cerr,exp); - return 1; - } -} ----- -+ -[.text-right] -<> | `<>` - -''' - -=== Transporting of Exceptions between Threads - -include::synopses/exception_capture.adoc[] - -This header defines functions that can be used to transport exceptions and associated error objects from one thread to another. - -''' - -[[capture_exception]] -==== `capture_exception` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - <> capture_exception( F && f ) noexcept; - -} } ----- - -Requirements: :: `F` must be a function type. - -Returns: :: A function of unspecified type which wraps `f` and, when called, forwards all of the arguments to `f` and returns its return value to the caller, except if `f` throws, in which case the specified `E...` types and the exception object are captured and transported in another exception object. - -NOTE: The `capture_exception` function is designed for use with `<>`, to effectively transport error objects across thread boundaries (for example see <>). - -''' - -[[get]] -==== `get` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - decltype(std::declval().get()) get( Future && f ); - -} } ----- - -Requirements: :: `Future` must be a `std::future` or other similar type used to recover future values by a member function `get()`. - -Returns: :: `f.get()`. - -Throws: :: Any exception thrown by `f.get()`. If the future function was launched using `<>`, all `E...` type objects captured in the worker thread are transported to the calling thread. - -IMPORTANT: To store and to access the transported error objects, the calling thread must provide suitable `<>` object(s). - [[techniques]] == Programming Techniques [[technique_preload]] -=== Preloading Errors +=== Preloading Error Objects -Consider the following exception type, designed for use without LEAF: +Consider the following exception type: [source,c++] ---- @@ -2168,6 +1897,7 @@ Using LEAF, a file name may be associated with any exception after it has been t [source,c++] ---- +class file_open_error: public std::exception { }; class file_read_error: public std::exception { }; struct e_file_name { std::string value; }; struct e_errno { int value; }; @@ -2199,32 +1929,29 @@ void process_file( char const * name ) [.text-right] `<>` | <> -The key is the call to `<>`: it gets the file name ready to be associated with any exception that escapes `process_file`. This is fully automatic, and works regardless of whether the exception is thrown later in the same function, or by `read_file`, or by some third-party function we call. +The key is the call to `<>`: it ensures tha the passed `e_file_name` will be associated with any exception that escapes `process_file`. This is fully automatic, and works regardless of whether the exception is thrown later in the same function, or by `read_file`, or by some third-party function we call. -Now, the `try...catch` that handles exceptions thrown by `process_file` may look like this: +Now, exceptions thrown by `process_file` may be handled like this: [source,c++] ---- -leaf::expect exp; -try -{ - process_file("example.txt"); -} -catch( file_io_error & e ) -{ - std::cerr << "I/O error!\n"; +leaf::try_( - leaf::handle_exception( exp, e, - [ ]( e_file_name const & fn, e_errno const & errn ) - { - std::cerr << "File name: " << fn.value << ", errno=" << errn << "\n"; - } - ); -} + [ ] + { + process_file("example.txt"); + }, + + [ ]( leaf::catch_, e_file_name const & fn, e_errno const & errn ) + { + std::cerr << + "I/O error!" << std::endl << + "File name: " << fn.value << ", errno=" << errn << std::endl; + } ); ---- [.text-right] -`<>` | <> +<> | <> NOTE: This technique works exactly the same way when errors are reported using `leaf::<>` rather than by throwing exceptions. @@ -2270,9 +1997,6 @@ void read_file(FILE * f) { } ---- -[.text-right] -`<>` - Ideally, associating `e_errno` with each exception should be automated. One way to achieve this is to not call `fread` directly, but wrap it in another function which checks for `ferror` and associates the `e_errno` with the exception it throws. <> describes how to solve a very similar problem without a wrapper function, but that technique does not work for `e_errno` because `<>` would capture `errno` before a `fread` call was attempted, at which point `errno` is probably `0` -- or, worse, leftover from a previous I/O failure. @@ -2283,7 +2007,7 @@ The solution is to use `<>`, so we don't have to remember to includ ---- void read_file(FILE * f) { - auto propagate = leaf::defer( [ ] { return e_errno{errno} } ); + auto propagate = leaf::defer([ ]{ return e_errno{errno}; }); .... size_t nr1=fread(buf1,1,count1,f); @@ -2304,88 +2028,18 @@ void read_file(FILE * f) { [.text-right] <> | `<>` -This works similarly to `preload`, except that capturing of the `errno` is deferred until the destructor of the `propagate` object is called, which calls the passed lambda function to obtain the `errno`. +This works similarly to `preload`, except that the capturing of the `errno` is deferred until the destructor of the `propagate` object is called, which calls the passed lambda function to obtain the `errno`. -''' - -[[technique_disparate_error_types]] -=== Working with Disparate Error Codes - -Because most libraries define their own mechanism for reporting errors, programmers often need to use multiple incompatible error-reporting interfaces in the same program. This led to the introduction of `boost::error_code` which later became `std::error_code`. Each `std::error_code` object is assigned an `error_category`. Libraries that communicate errors in terms of `std::error_code` define their own `error_category`. For libraries that do not, the user can "easily" define a custom `error_category` and still translate domain-specific error codes to `std::error_code`. - -But let`s take a step back and consider _why_ do we have to express every error in terms of the same static type, `std::error_code`? We need this translation because the {CPP} static type-checking system makes it difficult to write functions that may return error objects of the disparate static types used by different libraries. Outside of this limitation, it would be preferable to be able to write functions that can communicate errors in terms of arbitrary {CPP} types, as needed. - -To drive this point further, consider the real world problem of mixing `boost::error_code` and `std::error_code` in the same program. In theory, both systems are designed to be able to express one error code in terms of the other. In practice, describing a _generic_ system for error categorization in terms of another _generic_ system for error categorization is not trivial. - -Ideally, functions should be able to communicate different error types without having to translate between them, and {CPP} does offer a mechanism that does just that, it's called exception handling. And it is not a coincidence that the attempt to bind {CPP} exception types with the interface of each function, A.K.A. exception specifications, was so problematic that it had to be abandoned (while I am an outspoken proponent of exception handling, I do acknowledge that in practice, often for good reasons, exception handling may not be available or permitted). - -LEAF solves this problem without using exception handling: a scope that is able to handle either `std::error_code` or `boost::error_code` would look like this: - -[source,c++] ----- -{ - leaf::expect exp; - - if( leaf::result r = f() ) - { - //Success - } - else - { - bool matched = leaf::handle_error( exp, r, - - [ ]( std::error_code const & e ) - { - //Handle std::error_code - }, - - [ ]( boost::error_code const & e ) - { - //Handle boost::error_code - } - ); - } -} ----- - -[.text-right] -`<>` | `<>` | <> - -And here is a function which is able to report either `std::error_code` or `boost::error_code`: - -[source,c++] ----- -leaf::result f() -{ - if( std::error_code ec = g1() ) - { - //Success - } - else - return leaf::error(ec); - - if( boost::error_code ec = g2() ) - { - //Success - } - else - return leaf::error(ec); -} ----- - -[.text-right] -`<>` | `<>` - -Of course, under refactoring `f` can be changed to return any other error type as needed, without requiring a change in its static interface. With LEAF, such changes become transparent to any error-neutral function that calls and forwards errors from `f` to its caller; only the error handling scope needs to be updated to handle the new error types returned by `f`. +NOTE: This technique works exactly the same way when errors are reported using `leaf::<>` rather than by throwing exceptions. ''' [[technique_augment_in_catch]] === Augmenting Exceptions in a `catch` -What makes `<>` and `<>` useful (see <> and <>) is that they automatically include error objects with any exception or error reported by a function. +What makes `<>` and `<>` useful (see <> and <>) is that they automatically include E-objects with any exception or error reported by a function. -But what if we need to include some error object conditionally? When using exception handling, it would be nice to be able to do this in a `catch` statement which selectively augments passing exceptions. +But what if we need to include some E-object conditionally? When using exception handling, it would be nice to be able to do this in a `catch` statement which selectively augments passing exceptions. LEAF supports the following approach, assuming all exceptions derive from `std::exception`: @@ -2400,28 +2054,205 @@ try catch( std::exception const & e ) { if( condition ) - leaf::get_error(e).propagate( e_this{....}, e_that{....} ); + leaf::get_error_id(e).propagate( e_this{....}, e_that{....} ); throw; } ---- +[.text-right] +<> | <> + +The reason we need to use <> is that not all exception types derive from `leaf::<>`. If the caught exception has a `leaf::error_id` subobject, `get_error_id` will return that `leaf::error_id` slice. + +But if the caught exception doesn't have a `leaf::error_id` subobject, `get_error_id` returns an unspecified `leaf::error_id`, which is temporarily associated with any and all current exceptions, until successfully handled by <>. While this association is imperfect (because it does not pertain to a specific exception object) it is the best that can be done in this case. + +[NOTE] +-- +The following approach is roughly equivalent: +[source,c++] +---- +try +{ + .... + function_that_throws(); + .... +} +catch( leaf::error_id err ) +{ + if( condition ) + err.propagate( e_this{....}, e_that{....} ); + throw; +} +catch( std::exception const & e ) +{ + if( condition ) + leaf::next_error().propagate( e_this{....}, e_that{....} ); + throw; +} +---- +[.text-right] +<> | <> +-- + +''' + +[[technique_disparate_error_types]] +=== Working with Disparate Error Types + +Because most libraries define their own mechanism for reporting errors, programmers often need to use multiple incompatible error-reporting interfaces in the same program. This led to the introduction of `boost::system::error_code` which later became `std::error_code`. Each `std::error_code` object is assigned an `error_category`. Libraries that communicate errors in terms of `std::error_code` define their own `error_category`. For libraries that do not, the user can "easily" define a custom `error_category` and still translate domain-specific error codes to `std::error_code`. + +But let's take a step back and consider _why_ did we want to express every error in terms of the same static type, `std::error_code` in the first place? We need this translation because the {CPP} static type-checking system makes it difficult to write functions that may return error objects of the disparate static types used by different libraries. Outside of this limitation, it would be preferable to be able to write functions that can communicate errors in terms of arbitrary {CPP} types, as needed. + +To drive this point further, consider the real world problem of mixing `boost::system::error_code` and `std::error_code` in the same program. In theory, both systems are designed to be able to express one error code in terms of the other. In practice, describing a _generic_ system for error categorization in terms of another _generic_ system for error categorization is not trivial. + +Ideally, functions should be able to communicate different error types without having to translate between them, and {CPP} does offer a mechanism that does just that, it's called exception handling. And it is not a coincidence that the attempt to bind {CPP} exception types with the interface of each function, A.K.A. exception specifications, was so problematic that it had to be abandoned (while I am an outspoken proponent of exception handling, I do acknowledge that in practice, often for good reasons, exception handling may not be available or permitted). + +LEAF solves this problem without using exception handling: a scope that is able to handle either `std::error_code` or `boost::system::error_code` would look like this: + +[source,c++] +---- +return handle_some( + + [ ]() -> leaf::result + { + //Call operations which may report std::error_code and boost::system::error_code. + //Return errors via result. + }, + + [ ]( std::error_code const & e ) + { + //Handle std::error_code + }, + + [ ]( boost::system::error_code const & e ) + { + //Handle boost::system::error_code + } ); +---- + +[.text-right] +<> | <> + +And here is a function which, using LEAF, forwards either `std::error_code` or `boost::system::error_code` objects reported by lower level functions: + +[source,c++] +---- +leaf::result f() +{ + if( std::error_code ec = g1() ) + { + //Success + } + else + return leaf::new_error(ec); + + if( boost::system::error_code ec = g2() ) + { + //Success + } + else + return leaf::new_error(ec); +} +---- [.text-right] -<> | `<>` +<> | <> -The reason we need to use `<>` is that not all exception types derive from `leaf::<>`. If the caught exception has a `leaf::error` subobject, `get_error` will return that `leaf::error` value. Also, such exceptions can be intercepted by `catch( error e )` if needed. +''' -But if the caught exception doesn't have a `leaf::error` subobject, `get_error` returns an unspecified `leaf::error` value, which is temporarily associated with any and all current exceptions, until successfully handled by `<>`. While this association is imperfect (because it does not pertain to a specific exception object) it is the best that can be done in this case. +[[technique_exception_to_result]] +=== Converting Exceptions to `result` + +It is sometimes necessary to catch exceptions thrown by lower-level library fuction, and report the error through different means, to a higher-level library which may not use exception handling. + +Suppose we have an exception type hierarchy and a function `compute_answer_throws`: + +[source,c++] +---- +class error_base: public virtual std::exception { }; +class error_a: public virtual error_base { }; +class error_b: public virtual error_base { }; +class error_c: public virtual error_base { }; + +int compute_answer_throws() +{ + switch( rand()%4 ) + { + default: return 42; + case 1: throw error_a(); + case 2: throw error_b(); + case 3: throw error_c(); + } +} +---- + +We can write a simple wrapper using <>, which calls `compute_answer_throws` and switches to `result` for error handling: + +[source,c++] +---- +leaf::result compute_answer() noexcept +{ + return leaf::exception_to_result( + [ ] + { + return compute_answer_throws(); + } ); +} +---- + +(As a demonstration, `compute_answer` specifically converts exceptions of type `error_a` or `error_b`, while it leaves `error_c` to be captured by `std::exception_ptr`). + +Here is a simple function which prints successfully computed answers, forwarding any error (originally reported by throwing an exception) to its caller: + +[source,c++] +---- +leaf::result print_answer() noexcept +{ + LEAF_AUTO(answer, compute_answer()); + std::cout << "Answer: " << answer << std::endl; + return { }; +} +---- + +Finally, here is a scope that handles the errors (which used to be exception objects): + +[source,c++] +---- +leaf::handle_all( + + [ ]() -> leaf::result + { + LEAF_CHECK(print_answer()); + return { }; + }, + + [ ]( error_a const & e ) + { + std::cerr << "Error A!" << std::endl; + }, + + [ ]( error_b const & e ) + { + std::cerr << "Error B!" << std::endl; + }, + + [ ] + { + std::cerr << "Unknown error!" << std::endl; + } ); +---- + +NOTE: The complete program illustrating this technique is available https://github.com/zajo/leaf/blob/master/example/exception_to_result.cpp?ts=3[here]. ''' [[technique_preload_in_c_callbacks]] -=== Using `next_error_value` in C-callbacks +=== Using `next_error` in (Lua) C-callbacks Communicating information pertaining to a failure detected in a C callback is tricky, because C callbacks are limited to a specific static signature, which may not use {CPP} types. LEAF makes this easy. As an example, we'll write a program that uses Lua and reports a failure from a {CPP} function registered as a C callback, called from a Lua program. The failure will be propagated from {CPP}, through the Lua interpreter (written in C), back to the {CPP} function which called it. -C/{CPP} functions designed to be called from a Lua program must use the following signature: +C/{CPP} functions designed to be invoked from a Lua program must use the following signature: [source,c] ---- @@ -2461,7 +2292,15 @@ enum do_work_error_code ec1=1, ec2 }; + +namespace boost { namespace leaf { + + template<> struct is_error_type: std::true_type { }; + +} } ---- +[.text-right] +<> We're now ready to define the `do_work` callback function: @@ -2477,17 +2316,17 @@ int do_work( lua_State * L ) noexcept } else { - leaf::next_error_value().propagate(ec1); //<3> + leaf::next_error().propagate(ec1); //<3> return luaL_error(L,"do_work_error"); //<4> } } ---- [.text-right] -<> | `<>` +<> | <> <1> "Sometimes" `do_work` fails. <2> In case of success, push the result on the Lua stack, return back to Lua. -<3> Associate an `do_work_error_code` object with the *next* `leaf::error` object we will definitely return from the `call_lua` function (below)... +<3> Associate an `do_work_error_code` object with the *next* `leaf::error_id` object we will definitely return from the `call_lua` function... <4> ...once control reaches it, after we tell the Lua interpreter to abort the program. Now we'll write the function that calls the Lua interpreter to execute the Lua function `call_do_work`, which in turn calls `do_work`. We'll return `<>`, so that our caller can get the answer in case of success, or an error: @@ -2501,7 +2340,7 @@ leaf::result call_lua( lua_State * L ) { auto propagate = leaf::preload( e_lua_error_message{lua_tostring(L,1)} ); //<2> lua_pop(L,1); - return leaf::error( e_lua_pcall_error{err} ); + return leaf::new_error( e_lua_pcall_error{err} ); } else { @@ -2512,13 +2351,13 @@ leaf::result call_lua( lua_State * L ) } ---- [.text-right] -`<>` | <> | `<>` +<> | <> | <> <1> Ask the Lua interpreter to call the global Lua function `call_do_work`. -<2> Something went wrong with the call, so we'll return a `leaf::<>`. If this is a `do_work` failure, the `do_work_error_code` object prepared in `do_work` will become associated with this `leaf::error` value. If not, we will still need to communicate that the `lua_pcall` failed with an error code and an error message. +<2> Something went wrong with the call, so we'll return a <>. If this is a `do_work` failure, the `do_work_error_code` object prepared in `do_work` will become associated with this `leaf::error_id`. If not, we will still need to communicate that the `lua_pcall` failed with an error code and an error message. <3> Success! Just return the int answer. -Finally, here is the `main` function which handles all failures: +Finally, here is the `main` function which exercises `call_lua`, each time handling any failure: [source,c++] ---- @@ -2526,37 +2365,42 @@ int main() noexcept { std::shared_ptr L=init_lua_state(); - leaf::expect exp; //<1> - for( int i=0; i!=10; ++i ) - if( leaf::result r = call_lua(&*L) ) - std::cout << "do_work succeeded, answer=" << *r << '\n'; //<2> - else - { - bool matched = handle_error( exp, r, + { + leaf::handle_all( - [ ]( do_work_error_code e ) //<3> - { - std::cout << "Got do_work_error_code = " << e << "!\n"; - }, + [&]() -> leaf::result + { + LEAF_AUTO(answer, call_lua(&*L)); + std::cout << "do_work succeeded, answer=" << answer << '\n'; <1> + return { }; + }, - [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) //<4> - { - std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; - } - ); - assert(matched); - } - return 0; -} + [ ]( do_work_error_code e ) <2> + { + std::cout << "Got do_work_error_code = " << e << "!\n"; + }, + + [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) <3> + { + std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; + }, + + [ ]( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + } ); + } ---- [.text-right] -`<>` | `<>` | <> +<> | <> -<1> Tell LEAF what error objects are expected. -<2> If the call to `call_lua` succeeded, just print the answer. -<3> Handle `e_do_work` failures. -<4> Handle all other `lua_pcall` failures. +<1> If the call to `call_lua` succeeded, just print the answer. +<2> Handle `do_work` failures. +<3> Handle all other `lua_pcall` failures. [NOTE] -- @@ -2568,17 +2412,15 @@ Remarkably, the Lua interpreter is {CPP} exception-safe, even though it is writt ''' [[technique_transport]] -=== Transporting Error Objects between Threads +=== Transporting Error Objects Between Threads -With LEAF, error objects use automatic storage duration, stored inside `<>` instances. When using concurrency, we need a mechanism to detach error objects from a worker thread and transport them to another thread where errors are handled. +With LEAF, <> use automatic storage duration, stored inside the scope of <>, <> or <> functions. When using concurrency, we need a mechanism to detach E-objects from a worker thread and transport them to another thread where errors are handled. LEAF offers two interfaces for this purpose, one using `result`, and another designed for programs that use exception handling. [[technique_transport-result]] ==== Using `result` -Without exceptions, transporting error objects between threads is as easy as calling `<>`, passing the `<>` object whose contents needs to be transported, and a `<>` which may be in either value state or error state. This gets us a new `<>` object which can be safely sent across thread boundaries. - Let's assume we have a `task` which produces a result but could also fail: [source,c++] @@ -2586,54 +2428,54 @@ Let's assume we have a `task` which produces a result but could also fail: leaf::result task(); ---- -To prepare the returned `result` to be sent across the thread boundary, when we launch the asynchronous task, we wrap it in a lambda function that captures its result: +To prepare the returned `result` to be sent across the thread boundary, when we launch the asynchronous task, we wrap it in а call to <>, specifying which E-types we want captured: [source,c++] ---- std::future> launch_task() { - return std::async( std::launch::async, [ ] - { - leaf::expect exp; - return capture(exp,task()); - } ); + return std::async( + std::launch::async, + leaf::capture_result(&task)); } ---- [.text-right] -`<>` | `<>` | <> +<> | <> That's it! Later when we `get` the `std::future`, we can process the returned `result` as if it was generated locally: [source,c++] ---- -.... -leaf::expect exp; +//std::future> fut; +fut.wait(); -if( leaf::result r = fut.get() ) -{ - //Success! Use *r to access task_result. -} -else -{ - handle_error( exp, r, +return leaf::handle_some( - [ ] ( E1 e1, E2 e2 ) - { - //Deal with E1, E2 - }, + [&]() -> leaf::result + { + LEAF_AUTO(r, fut.get()); + //Success! + return { } + }, - [ ] ( E3 e3 ) - { - //Deal with E3 - } + [ ]( E1 e1, E2 e2 ) + { + //Deal with E1, E2 + .... + return { }; + }, - ); -} + [ ]( E3 e3 ) + { + //Deal with E3 + .... + return { }; + } ); ---- [.text-right] -`<>` | `<>` | <> +<> | <> NOTE: Follow this link to see a complete example program: https://github.com/zajo/leaf/blob/master/example/capture_result.cpp?ts=3[capture_result.cpp]. @@ -2642,64 +2484,58 @@ NOTE: Follow this link to see a complete example program: https://github.com/zaj [[technique_transport-exceptions]] ==== Using Exception Handling -When using exception handling, we need to capture the exception using `std::exception_ptr`, then capture the current error objects in an `<>` and wrap both into another exception. In the main thread we unwrap and throw the original exception. - -This, of course, is done automatically by LEAF. Let's assume we have a `task` which produces a `task_result` and throws on errors: +Let's assume we have a `task` which produces a result but could also throw: [source,c++] ---- task_result task(); ---- -When we launch the asynchronous task, we wrap it in a simple lambda function which calls `<>`, specifying which error types we need transported: +To prepare exceptions thrown by the `task` function to be sent across the thread boundary, when we launch the asynchronous task, we wrap it in а call to <>, specifying which E-types we want captured: [source,c++] ---- std::future launch_task() { - return std::async( std::launch::async, - leaf::capture_exception( [ ] - { - return task(); - } ) ); + return std::async( + std::launch::async, + leaf::capture_exception(&task)); } ---- [.text-right] -<> +<> -Later, instead of using `std::future::get`, we use `leaf::<>`, then catch exceptions as if the function was called locally: +That's it! Later when we `get` the `std::future`, we can catch exceptions as if they're thrown locally: [source,c++] ---- -.... -leaf::expect exp; +//std::future fut; +fut.wait(); -try -{ - task_result r = leaf::get(fut); - //Success! -} -catch( my_exception & e ) -{ - handle_exception( exp, e, +try_( - [ ] ( E1 e1, E2 e2 ) - { - //Deal with E1, E2 - }, + [&] + { + auto r = fut.get(); + //Success! + }, - [ ] ( E3 e3 ) - { - //Deal with E3 - } + [ ]( E1 e1, E2 e2 ) + { + //Deal with E1, E2 + .... + }, - ); -} + [ ]( E3 e3 ) + { + //Deal with E3 + .... + } ); ---- [.text-right] -`<>` | <> | <> +<> | <> NOTE: Follow this link to see a complete example program: https://github.com/zajo/leaf/blob/master/example/capture_eh.cpp?ts=3[capture_eh.cpp]. @@ -2723,28 +2559,30 @@ Stronger: authors of _error-initiating_ functions may not even reason about what The same reasoning applies to _error-neutral_ functions, but in this case there is the additional problem that the errors they need to communicate, in general, are initiated by functions multiple levels removed from them in the call chain, functions which usually are -- and should be treated as -- implementation details. The _error-neutral_ function should not be coupled with any error object type used by _error-initiating_ functions, for the same reason it should not be coupled with any other aspect of their interface. -Finally, _error-handling_ functions, by definition, have the full context they need to deal with at least some, if not all, failures. In this scope it is an absolute necessity that the author knows exactly what information must be communicated by lower level functions in order to recover from each error condition. Specifically, none of this necessary information can be treated as implementation details; the coupling which is to be avoided by _error-neutral_ functions is unavoidable and even desirable here. +Finally, _error-handling_ functions, by definition, have the full context they need to deal with at least some, if not all, failures. In this scope it is an absolute necessity that the author knows exactly what information must be communicated by lower level functions in order to recover from each error condition. Specifically, none of this necessary information can be treated as implementation details; here, the coupling which is to be avoided by _error-neutral_ functions is unavoidable, in fact desirable. We're now ready to define our Design goals: :: * *Error-initiating* functions should be able to communicate all information available to them that is relevant to the failure being reported. -* *Error-neutral* functions should not interfere or be coupled with error-related information that passes through them. They should be able to augment it with any additional information available to them, which may be relevant to any error they forward to the caller. +* *Error-neutral* functions should not interfere or be coupled with error-related information that passes through them. They should be able to augment it with any additional information available to them that may be relevant to any error they forward to the caller. * *Error-handling* functions should be able to access all the information communicated by _error-initiating_ or _error-neutral_ functions that is needed in their domain in order to deal with failures. The difficulty in reaching these design goals is in that they seem to require that all error objects be allocated dynamically (the Boost Exception library meets these design goals at the cost of dynamic memory allocation). -As it turns out, dynamic memory allocation is not necessary with the following +As it turns out, dynamic memory allocation is not necessary due to the following -Adjustment: :: -* *Error-handling* functions should specify which of the information _error-initiating_ and _error-neutral_ functions are [.underline]#able# to communicate is [.underline]#actually needed# in order to deal with failures in a particular program domain. Ideally, no resources should be [.line-through]#used# wasted storing or communicating information which is not currently needed, even if it is relevant to the failure. +Fact: :: +* *Error-handling* functions already "know" which of the information _error-initiating_ and _error-neutral_ functions are [.underline]#able# to communicate is [.underline]#actually needed# in order to deal with failures in a particular program domain. Ideally, no resources should be [.line-through]#used# wasted storing or communicating information which is not currently needed to handle errors, even if it is relevant to the failure. -The `leaf::<>` class template implements this idea: it provides local storage for error objects of the `E...` types. Users instantiate this template in _error-handling_ functions, knowing which types of error objects are needed. When an `expect` object is created, `thread_local` pointers of the `E...` types are set to point to the corresponding storage within it. Later on, _error-initiating_ or _error-neutral_ functions wanting to communicate an error object of type `E` access the corresponding `thread_local` pointer to see if there is currently storage available for this type: +The LEAF functions <>, <> and <> implement this idea. Users provide error-handling lambda funcitons which, naturally, take the arguments types they need. LEAF simply provides space needed to store these types, using automatic storage duration, before they are passed to a handler. -* If the pointer is not null, storage is available and the object is moved into that storage, exactly once -- regardless of how many levels of function calls must unwind before an appropriate _error-handling_ function is reached (that's the function where the `expect` object resides). -* If the pointer is null, storage is not available and the error object is discarded, since there is no use for it -- saving resources. +When this space is created, `thread_local` pointers of the required error types are set to point to the corresponding storage within the scope of <>, <> or <>. Later on, _error-initiating_ or _error-neutral_ functions wanting to communicate an error object of type `E` access the corresponding `thread_local` pointer to see if there is currently storage available for this type: -This almost works, except we need to make sure that _error-handling_ functions are protected from accessing stale error objects stored in `expect` objects, left there from previous failures, which would be a serious logic error. To this end, each failure is assigned a unique serial number, which is transported inside `leaf::<>` objects that communicate failures. Each of the `E...` objects stored in an `expect` object is assigned the same unique identifier, permanently associating it with a particular failure. +* If the pointer is not null, storage is available and the object is moved into that storage, exactly once -- regardless of how many levels of function calls must unwind before an appropriate _error-handling_ function is reached. +* If the pointer is null, storage is not available and the error object is discarded, since no error-handling function makes any use of it -- saving resources. + +This almost works, except we need to make sure that _error-handling_ functions are protected from being passed stale error objects stored in an error-handling scope, left there from previous failures, which would be a serious logic error. To this end, each failure is assigned a unique <>, which is transported inside `leaf::<>` objects that communicate failures. Each of the `E...` objects stored in error-handling scopes is assigned the same unique identifier, permanently associating it with a particular error. Lastly, in _error-handling_ functions it makes sense to be able to not only recognize individual error conditions, but match specific error-handling code with the complete set of error objects that is required in each case. @@ -2754,126 +2592,103 @@ In terms of {CPP} exception handling, it would be nice to be able to say somethi ---- try { - T r = process_file(); + auto r = process_file(); //Success, use r: .... } -catch( file_read_error & e ) + +catch( file_read_error const &, e_file_name const & fn, e_errno const & err ) { std::cerr << - "Could not read " << e.get() << - ", errno=" << e.get() << std::endl; + "Could not read " << fn << ", errno=" << err << std::endl; } -catch( file_read_error & e ) + +catch( file_read_error const &, e_errno const & err ) { std::cerr << - "File read error, errno=" << e.get() << std::endl; + "File read error, errno=" << err << std::endl; } -catch( file_read_error<> & e ) + +catch( file_read_error const & ) { std::cerr << "File read error!" << std::endl; } ---- -That is to say, it is desirable to be able to dispatch error handling based not only on the kind of failure being handled, but also based on the kind of error objects available. Unfortunately this syntax is not possible and, even if it were, not all programs use exceptions to handle errors. +That is to say, it is desirable to be able to dispatch error handling based not only on the kind of failure being handled, but also based on the kind of E-objects available. Unfortunately this syntax is not possible and, even if it were, not all programs use exceptions to handle errors. -Below is the same snippet, expressed in terms of LEAF: +LEAF achieves the same effect using a slightly different syntax: [source,c++] ---- -leaf::expect exp; +leaf::try_( -try -{ - T r = process_file(); //Throws in case of failure, error objects stored inside exp. + [ ] + { + auto r = process_file(); //Throws in case of failure, E-objects stored inside the try_ scope - //Success, use r: - .... -} -catch( file_read_error & e ) -{ - //Match available error objects stored in exp, - //associated with the specific failure communicated by e. - leaf::handle_exception( exp, e, + //Success, use r: + .... + } - [ ]( e_file_name const & file_name, e_errno const & errno_ ) - { - std::cerr << - "Could not read " << file_name.value << - ", errno=" << errno_ << std::endl; - }, + [ ]( leaf::catch_, e_file_name const & fn, e_errno const & err ) + { + std::cerr << + "Could not read " << fn << ", errno=" << err << std::endl; + }, - [ ]( e_errno const & errno_ ) - { - std::cerr << - "File read error, errno=" << errno_ << std::endl; - }, + [ ]( leaf::catch_, e_errno const & err ) + { + std::cerr << + "File read error, errno=" << err << std::endl; + }, - [ ] - { - std::cerr << "File read error!" << std::endl; - } - - ); -} + [ ]( leaf::catch_ ) + { + std::cerr << "File read error!" << std::endl; + } ); ---- [.text-right] -`<>` | <> +<> | <> Of course LEAF works without exception handling as well. Below is the same snippet, written using `<>`: [source,c++] ---- -leaf::expect exp; +return leaf::handle_some( -if( leaf::result r = process_file() ) //In case of failure error objects are stored inside exp. -{ - //Success, use r.value(): - .... -} -else -{ - //e_what is used to dispatch between error conditions: - switch( *leaf::peek(exp,r) ) + [ ]() -> leaf::result { - case file_read_error: - { - //Match available error objects stored in exp, - //associated with the specific failure communicated by r. - bool handled = leaf::handle_error( exp, r, + LEAF_AUTO(r, process_file()); //In case of errors, E-objects are stored inside the handle_some scope - [ ]( e_file_name const & file_name, e_errno const & errno_ ) - { - std::cerr << - "Could not read " << file_name.value << - ", errno=" << errno_ << std::endl; - }, + //Success, use r: + .... - [ ]( e_errno const & errno_ ) - { - std::cerr << - "File read error, errno=" << errno_ << std::endl; - }, + return { }; + } - [ ] - { - std::cerr << "File read error!" << std::endl; - } + [ ]( leaf::match, e_file_name const & fn, e_errno const & err ) + { + std::cerr << + "Could not read " << fn << ", errno=" << err << std::endl; + }, - ); - if( handled ) - break; - } - //fallthrough: - default: - return r; //Error not handled, forward to the caller. -} + [ ]( leaf::match, e_errno const & err ) + { + std::cerr << + "File read error, errno=" << err << std::endl; + }, + + [ ]( leaf::match ) + { + std::cerr << "File read error!" << std::endl; + } ); ---- [.text-right] -`<>` | `<>` | <> | <> +<> | <> | <> NOTE: Please post questions and feedback on the Boost Developers Mailing List (LEAF is not part of Boost). @@ -3014,11 +2829,11 @@ struct config_error result read_config(); ---- -This looks nice, until we realize what the `config_error` type means for the automatic mapping API we wanted to define: an enum can not represent a struct, and therefore we can not assume that all error conditions can be fully specified by an enum; it must support arbitrary static types. +This looks nice, until we realize what the `config_error` type means for the automatic mapping API we wanted to define: an `enum` can not represent a `struct`. It is a fact that we can not assume that all error conditions can be fully specified by an `enum`; an error handling library must be able to transport arbitrary static types efficiently. -Conclusion: :: Transporting error objects statically in return values works great if the failure is handled in the immediate caller of the function that reports it, but most error objects must be communicated across multiple layers of function calls and APIs, which leads to excessive physical coupling between these interfaces. +Conclusion: :: Transporting error objects in return values works great if the failure is handled by the immediate caller of the function that reports it, but most error objects must be communicated across multiple layers of function calls and APIs, which leads to excessive physical coupling between these interfaces. -NOTE: While the `leaf::<>>` class template does have value-or-error semantics, it does not carry the actual error objects. Instead, they are forwarded directly to the appropriate error-handling scope and their types do not participate in function signatures. +NOTE: While the `leaf::<>` class template does have value-or-error semantics, it does not carry the actual error objects. Instead, they are forwarded directly to the appropriate error-handling scope and their types do not participate in function signatures. == Alternatives to LEAF @@ -3136,33 +2951,29 @@ https://www.boost.org/doc/libs/release/libs/exception/doc/get_error_info.html[`b | [source,c++,options="nowrap"] ---- -leaf::expect exp; -try -{ - f(); -} -catch( my_exception & e ) -{ - leaf::handle_exception( exp, e, - [ ]( my_info const & x ) - { - //my_info is available in e. - } ); -} +leaf::try_( + [ ] + { + f(); + } + [ ]( leaf::catch_, my_info const & x ) + { + //my_info is available with e. + } ); ---- [.text-right] -`<>` \| <> \| <> +<> | <> |==== -.Error object propagation +.Propagation of <> [cols="1a,1a",options="header",stripes=none] |==== | Boost Exception | LEAF | All supplied https://www.boost.org/doc/libs/release/libs/exception/doc/error_info.html[`boost::error_info`] objects are allocated dynamically, stored in the https://www.boost.org/doc/libs/release/libs/exception/doc/exception.html[`boost::exception`] object, and propagated. -| User-defined error objects are stored statically in `leaf::<>` objects but only if their types are used in the instantiation the `expect` class template; otherwise they are discarded. +| User-defined error objects are stored statically in the scope of <>, but only if their types are used to handle errors; otherwise they are discarded. |==== -.Transporting of error objects across thread boundaries +.Transporting of <> across thread boundaries [cols="1a,1a",options="header",stripes=none] |==== | Boost Exception | LEAF @@ -3180,9 +2991,9 @@ catch( my_exception & e ) [WARNING] ==== -The fact that Boost Exception stores all supplied `boost::error_info` objects while LEAF discards them if they aren't needed affects the completeness of the message printed by `leaf::<>` compared to the string returned by https://www.boost.org/doc/libs/release/libs/exception/doc/diagnostic_information.html[`boost::diagnostic_information`]. +The fact that Boost Exception stores all supplied `boost::error_info` objects -- while LEAF discards them if they aren't needed -- affects the completeness of the message we get when we print `leaf::<>` objects, compared to the string returned by https://www.boost.org/doc/libs/release/libs/exception/doc/diagnostic_information.html[`boost::diagnostic_information`]. -By default, the `leaf::<>` includes only error objects of types used in the instantiation of the `<>` class template. If the user requires a complete `diagnostic_output` message, the solution is to use <> with `expect`. In this case, before unused error objects are discarded by LEAF, they are converted to string and stored in the `e_unexpected_diagnostic_output` object. Note that this allocates memory dynamically. +If the user requires a complete diagnostic message, the solution is to use `leaf::<>`. In this case, before unused error objects are discarded by LEAF, they are converted to string and captured. Note that this allocates memory dynamically. ==== ''' @@ -3203,14 +3014,14 @@ The idea is to use `result<>` to communicate failures which can be fully specifi Another way to describe this design is that `result<>` is used when it suffices to return an error object of some static type `EC`, while `outcome<>` can also transport a polymorphic error object, using the pointer type `EP`. -NOTE: In the default configuration of `outcome` the additional information -- or the additional polymorphic object -- is an exception object held by `std::exception_ptr`. This targets the use case when an exception thrown by a lower-level library function needs to be transported through some intermediate contexts that are not exception-safe, to a higher-level context able to handle it. +NOTE: In the default configuration of `outcome` the additional information -- or the additional polymorphic object -- is an exception object held by `std::exception_ptr`. This targets the use case when an exception thrown by a lower-level library function needs to be transported through some intermediate contexts that are not exception-safe, to a higher-level context able to handle it. LEAF supports this case more efficiently, see <>. Similar reasoning drives the design of LEAF as well. The difference is that while both libraries recognize the need to transport "something else" in addition to an "error code", LEAF provides an efficient solution to this problem, while Outcome shifts this burden to the user. The `leaf::result<>` template deletes both `EC` and `EP`, which decouples it from the type of the error objects that are transported in case of a failure. This enables lower-level functions to freely communicate anything and everything they "know" about the failure: error code, even multiple error codes, file names, request IDs, etc. At the same time, the higher-level error-handling functions control which of this information is needed in a specific client program and which is not. This is ideal, because: * Authors of lower-level library functions lack context to determine which of the information that is both relevant to the error _and_ naturally available to them needs to be communicated in order for a particular client program to recover from that error; -* Authors of higher-level error-handling functions can easily and confidently make this determination, and instantiate the `<>` class template with the error types they do in fact need; LEAF automatically and efficiently discards error objects of all other types that error-reporting functions attempt to communicate, saving resources. +* Authors of higher-level error-handling functions easily and confidently make this determination, which they communicate naturally to LEAF, by simply writing the necessary error handlers. LEAF automatically and efficiently transports the needed E-objects while discarding the ones handlers don't use, saving resources. TIP: The LEAF examples include an adaptation of the program from the https://ned14.github.io/outcome/tutorial/result/[Boost Outcome `result<>` tutorial]. You can https://github.com/zajo/leaf/blob/master/example/print_half.cpp?ts=3[view it on GitHub]. @@ -3237,7 +3048,7 @@ The analysis above is clear and precise, but LEAF and Boost Outcome tackle the i * The Boost Outcome design asserts that the "cleanest" solution based on type-erasure is suboptimal ("almost certainly requires the use of `malloc`pass:[]"), and instead provides a system for injecting custom converters into the `outcome::convert` namespace, used to translate between library-specific and program-wide error types. -* The LEAF design asserts that coupling the signatures of <> functions with the static types of the error objects they need to forward to the caller is not practical, and instead transports error objects directly to error-handling contexts where they are stored statically (without the use of `malloc`). +* The LEAF design asserts that coupling the signatures of <> functions with the static types of the error objects they need to forward to the caller <>, and instead transports error objects directly to error-handling scopes where they are stored statically (without the use of `malloc`). [[distribution]] == Distribution @@ -3285,4 +3096,6 @@ meson test == Acknowledgements -Thanks to Peter Dimov for the countless library design discussions. Ivo Belchev, Sean Palmer, Jason King, Vinnie Falco, Glen Fernandes, Nir Friedman -- thanks for your feedback. \ No newline at end of file +Special thanks to Peter Dimov. + +Ivo Belchev, Sean Palmer, Jason King, Vinnie Falco, Glen Fernandes, Nir Friedman -- thanks for the valuable feedback. \ No newline at end of file diff --git a/doc/synopses/capture_exception.adoc b/doc/synopses/capture_exception.adoc new file mode 100644 index 0000000..5246755 --- /dev/null +++ b/doc/synopses/capture_exception.adoc @@ -0,0 +1,10 @@ +[source,c++] +.#include +---- +namespace boost { namespace leaf { + + template + <> capture_exception( F && f ) noexcept; + +} } +---- diff --git a/doc/synopses/capture_result.adoc b/doc/synopses/capture_result.adoc new file mode 100644 index 0000000..41409a3 --- /dev/null +++ b/doc/synopses/capture_result.adoc @@ -0,0 +1,10 @@ +[source,c++] +.#include +---- +namespace boost { namespace leaf { + + template + <> capture_result( F && f ) noexcept; + +} } +---- diff --git a/doc/synopses/common.adoc b/doc/synopses/common.adoc index 15ded96..53df5eb 100644 --- a/doc/synopses/common.adoc +++ b/doc/synopses/common.adoc @@ -3,54 +3,20 @@ ---- namespace boost { namespace leaf { - struct e_api_function { char const * value; }; - - struct e_file_name { std::string value; }; - - struct e_errno - { - int value; - friend std::ostream & operator<<( std::ostream & os, e_errno const & err ); - }; + struct e_api_function { .... }; + struct e_file_name { .... }; + struct e_errno { .... }; + struct e_at_line { .... }; + struct e_type_info_name { .... }; + struct e_source_location { .... }; namespace windows { - struct e_LastError - { - unsigned value; - }; + struct e_LastError { .... }; } - struct e_at_line { int value; }; - - struct e_type_info_name { char const * value; }; - - struct e_source_location - { - char const * const file; - int const line; - char const * const function; - - friend std::ostream & operator<<( std::ostream & os, e_source_location const & x ); - }; - - struct e_unexpected - { - char const * (*first_type)(); - int count; - - friend std::ostream & operator<<( std::ostream & os, e_unexpected const & x ); - }; - - struct e_unexpected_diagnostic_output - { - <> - - friend std::ostream & operator<<( std::ostream & os, e_unexpected_diagnostic_output const & x ); - }; - } } ---- [.text-right] -<> | <> | <> | <> | <> | <> | <> | <> | <> +<> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/diagnostic_output_current_exception.adoc b/doc/synopses/diagnostic_output_current_exception.adoc deleted file mode 100644 index 067cd2d..0000000 --- a/doc/synopses/diagnostic_output_current_exception.adoc +++ /dev/null @@ -1,11 +0,0 @@ -.#include -[source,c++] ----- - -namespace boost { namespace leaf { - - template - void diagnostic_output_current_exception( std::ostream & os, expect const & exp ); - -} } ----- diff --git a/doc/synopses/error.adoc b/doc/synopses/error.adoc index e0ef34b..3daf9b2 100644 --- a/doc/synopses/error.adoc +++ b/doc/synopses/error.adoc @@ -3,26 +3,28 @@ ---- namespace boost { namespace leaf { - class error + class error_id; + + template + error_id new_error( E && ... e ) noexcept; + + error_id next_error() noexcept; + + error_id last_error() noexcept; + + class error_id { public: template - explicit error( E && ... e ) noexcept: + error_id propagate( E && ... e ) const noexcept; - template - error propagate( E && ... e ) const noexcept; + friend bool operator==( error_id const & e1, error_id const & e2 ) noexcept; + friend bool operator!=( error_id const & e1, error_id const & e2 ) noexcept; - friend bool operator==( error const & e1, error const & e2 ) noexcept; - friend bool operator!=( error const & e1, error const & e2 ) noexcept; - - friend std::ostream & operator<<( std::ostream & os, error const & e ) + friend std::ostream & operator<<( std::ostream & os, error_id const & e ) }; - error next_error_value() noexcept; - - error last_error_value() noexcept; - template struct is_error_type { @@ -33,4 +35,4 @@ namespace boost { namespace leaf { ---- [.text-right] -`<>` | <> | <> | <> | <> | <> | <> | <> +<> | <> | <> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/error_capture.adoc b/doc/synopses/error_capture.adoc deleted file mode 100644 index 713468b..0000000 --- a/doc/synopses/error_capture.adoc +++ /dev/null @@ -1,30 +0,0 @@ -.#include -[source,c++] ----- - -namespace boost { namespace leaf { - - class error_capture - { - public: - - error_capture() noexcept; - - explicit operator bool() const noexcept; - - error unload() noexcept; - }; - - template - <> handle_error( error_capture const & ec, F && ... f ) noexcept; - - template - P const * peek( error_capture const & ec ) noexcept; - - void diagnostic_output( std::ostream & os, error_capture const & ec ); - -} } ----- - -[.text-right] -`<>` | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/exception.adoc b/doc/synopses/exception.adoc deleted file mode 100644 index 3f3a06e..0000000 --- a/doc/synopses/exception.adoc +++ /dev/null @@ -1,28 +0,0 @@ -[source,c++] -.#include ----- -namespace boost { namespace leaf { - - template - <> exception( Ex && ex, E... && e ) noexcept; - - error get_error( std::exception const & ex ) noexcept; - - template - P const * peek( expect const & exp, std::exception const & ex ) noexcept; - - template - void handle_exception( expect & exp, std::exception const & ex, F && ... f ); - - template - void diagnostic_output( std::ostream & os, expect const & exp, std::exception const & ex ); - -} } - -#define LEAF_EXCEPTION(...) <> - -#define LEAF_THROW(...) throw LEAF_EXCEPTION(__VA_ARGS__) ----- - -[.text-right] -<> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/exception_capture.adoc b/doc/synopses/exception_capture.adoc deleted file mode 100644 index 48b92de..0000000 --- a/doc/synopses/exception_capture.adoc +++ /dev/null @@ -1,17 +0,0 @@ -[source,c++] -.#include ----- -namespace boost { namespace leaf { - - template - <> capture_exception( F && f ) noexcept; - - template - decltype(std::declval().get()) get( Future && f ); - -} } ----- - -[.text-right] -<> | <> - diff --git a/doc/synopses/exception_to_result.adoc b/doc/synopses/exception_to_result.adoc new file mode 100644 index 0000000..5917a9c --- /dev/null +++ b/doc/synopses/exception_to_result.adoc @@ -0,0 +1,10 @@ +[source,c++] +.#include +---- +namespace boost { namespace leaf { + + template + <> exception_to_result( F && f ) noexcept; + +} } +---- diff --git a/doc/synopses/expect.adoc b/doc/synopses/expect.adoc deleted file mode 100644 index cee0162..0000000 --- a/doc/synopses/expect.adoc +++ /dev/null @@ -1,40 +0,0 @@ -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - class expect - { - public: - - expect() noexcept; - ~expect() noexcept; - }; - - template - error_capture capture( expect & exp, error const & e ); - - template - struct uhnandled_error - { - static constexpr R value( error const & ) noexcept; - }; - - template - <> handle_error( expect const & exp, error const & e, F && ... f ) noexcept; - - template - P const * peek( expect const & exp, error const & e ) noexcept; - - template - void diagnostic_output( std::ostream & os, expect const & exp ); - - template - void diagnostic_output( std::ostream & os, expect const & exp, error const & e ); - -} } ----- - -[.text-right] -<> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/handle.adoc b/doc/synopses/handle.adoc new file mode 100644 index 0000000..5f90629 --- /dev/null +++ b/doc/synopses/handle.adoc @@ -0,0 +1,21 @@ +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + typename std::remove_reference()().value())>::type + handle_all( TryBlock && try_block, Handler && ... handler ); + + template + decltype(std::declval()()) + handle_some( TryBlock && try_block, Handler && ... handler ); + + template ::type... V> + struct match; + +} } +---- + +[.text-right] +<> | <> | <> \ No newline at end of file diff --git a/doc/synopses/result.adoc b/doc/synopses/result.adoc index 2fa660a..2402785 100644 --- a/doc/synopses/result.adoc +++ b/doc/synopses/result.adoc @@ -9,44 +9,48 @@ namespace boost { namespace leaf { public: result() noexcept; - result( T const & v ); result( T && v ) noexcept; - result( leaf::error const & e ) noexcept; - result( leaf::error_capture const & ec ) noexcept; + result( T const & v ); + result( error_id const & err ) noexcept; + + result( result && r ) noexcept; + result( result const & r ); + + template + result( result && r ) noexcept + + template + result( result const & r ) + + result & operator=( result && r ) noexcept; + result & operator=( result const & r ); + + template + result & operator=( result && r ) noexcept + + template + result & operator=( result const & r ) explicit operator bool() const noexcept; T const & value() const; T & value(); + T const & operator*() const; T & operator*(); template - leaf::error error( E && ... e ) noexcept; + error_id error( E && ... e ) noexcept; }; - template - result capture( expect & exp, result const & r ); - - template - <> handle_error( expect const & exp, result const & r, F && ... f ) noexcept; - - template - P const * peek( expect const &, result const & ) noexcept; - - template - void diagnostic_output( std::ostream & os, expect const & exp, result const & r ); - struct bad_result: std::exception { }; } } -#define LEAF_ERROR(...) <> - -#define LEAF_AUTO(v,r) auto _r_##v = r; if( !_r_##v ) return _r_##v.error(); auto & v = *_r_##v - -#define LEAF_CHECK(r) {auto _r = r; if( !_r ) return _r.error();} +#define LEAF_ERROR(...) .... +#define LEAF_AUTO(v,r) .... +#define LEAF_CHECK(r) .... ---- [.text-right] -`<>` | <> | <> | <> | <> | <> | <> | <> | <> | <> | <> | <> | <> +<> | <> | <> | <> | <> | <> | <> | <> | <> | <> diff --git a/doc/synopses/throw.adoc b/doc/synopses/throw.adoc new file mode 100644 index 0000000..4e96d32 --- /dev/null +++ b/doc/synopses/throw.adoc @@ -0,0 +1,17 @@ +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + <> exception( Ex && ex, E && ... e ) noexcept; + +} } + +#define LEAF_EXCEPTION(...) .... + +#define LEAF_THROW(...) .... +---- + +[.text-right] +<> | <> | <> diff --git a/doc/synopses/try.adoc b/doc/synopses/try.adoc new file mode 100644 index 0000000..9943451 --- /dev/null +++ b/doc/synopses/try.adoc @@ -0,0 +1,21 @@ +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + decltype(std::declval()()) try_( TryBlock && try_block, Handler && ... handler ); + + template ::type... V> + struct match; + + template + struct catch_; + + error_id get_error_id( std::exception const & ex ) noexcept; + +} } +---- + +[.text-right] +<> | <> | <> | <> diff --git a/example/capture_eh.cpp b/example/capture_eh.cpp index a778602..eb0e636 100644 --- a/example/capture_eh.cpp +++ b/example/capture_eh.cpp @@ -1,14 +1,15 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -//This is a simple program that demonstrates the use of LEAF to transport e-objects between threads, -//when using exception handling to report failures. See capture_result.cpp for the variant that doesn't -//use exception handling. +// This is a simple program that demonstrates the use of LEAF to transport e-objects between threads, +// without using exception handling. See capture_eh.cpp for the exception-handling variant. -#include +#include +#include +#include #include #include #include @@ -18,84 +19,72 @@ namespace leaf = boost::leaf; -//Define several e-types. +// Define several e-types. struct e_thread_id { std::thread::id value; }; struct e_failure_info1 { std::string value; }; struct e_failure_info2 { int value; }; -struct e_failure_info3 { long value; }; -struct e_failure_info4 { float value; }; -//A type that represents a successfully returned result from a task. +// A type that represents a successfully returned result from a task. struct task_result { }; -//An exception type thrown in case of task failure. -struct failure : virtual std::exception { }; - -//This is a test task which succeeds or fails depending on its argument. In case of success, it returns -//task_result, in case of error it throws failure. -task_result task( bool succeed ) + // This is our task function. It produces objects of type task_result, but it may fail... +task_result task() { + bool succeed = (rand()%4) !=0; //...at random. if( succeed ) - return task_result(); //Simulate successful result. + return task_result { }; else - throw leaf::exception( - failure(), + throw leaf::exception( std::exception(), e_thread_id{std::this_thread::get_id()}, e_failure_info1{"info"}, - e_failure_info2{42}, - e_failure_info4{42} ); -} - -//Launch the specified number of asynchronous tasks. In case an asynchronous task throws, its e-objects -//(of the type list used to instantiate leaf::capture_exception) are captured and wrapped in a different -//exception, which transports it to the main thread. The original exception is then recovered by leaf::get. -template -std::vector> launch_async_tasks( int thread_count ) -{ - std::vector> fut; - std::generate_n( std::inserter(fut,fut.end()), thread_count, [ ] - { - return std::async( std::launch::async, - leaf::capture_exception( [ ] //returns a wrapper function for the lambda... - { - return task((rand()%4)!=0); //...which transports the E... objects. - } ) ); - } ); - return fut; -} + e_failure_info2{42} ); +}; int main() { - //Launch tasks, transport the specified e-types. For demonstration, note that the task provides - //failure_info4 which we don't care about, and that we say we could use failure_info3, but which the - //task doesn't provide. So, we'll only get failed_thread_id, failure_info1 and failure_info2. - auto fut = launch_async_tasks(42); + int const task_count = 42; - //Collect results or deal with failures. + // Container to collect the generated std::future objects. + std::vector> fut; + + // Launch the tasks, but rather than launching the task function directly, we launch the + // wrapper function returned by leaf::capture_result. It captures the specified error object + // types and automatically transports them in the leaf::result it returns. + std::generate_n( std::inserter(fut,fut.end()), task_count, + [ ] + { + return std::async( + std::launch::async, + leaf::capture_exception(&task) ); + } ); + + // Wait on the futures, get the task results, handle errors. for( auto & f : fut ) { f.wait(); - //Storage for e-objects. - leaf::expect exp; + leaf::try_( + [&] + { + task_result r = f.get(); - try - { - //Instead of calling future::get we pass the future object to leaf::get. In case the future finished with an exception, - //this will rethrow that exception, after dropping any captured e-objects into exp. - task_result r = leaf::get(f); + // Success! Use r to access task_result. + std::cout << "Success!" << std::endl; + (void) r; - //Success! - std::cout << "Success!" << std::endl; - } - catch( failure const & e ) - { - //Failure! Handle the error, print failure info. - handle_exception( exp, e, - [ ] ( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid ) - { - std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl; - } ); - } + }, + + [ ]( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid ) + { + std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl; + }, + + [ ]( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + } ); } } diff --git a/example/capture_result.cpp b/example/capture_result.cpp index 0fabf15..de10976 100644 --- a/example/capture_result.cpp +++ b/example/capture_result.cpp @@ -1,13 +1,15 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -//This is a simple program that demonstrates the use of LEAF to transport e-objects between threads, -//without using exception handling. See capture_eh.cpp for the exception-handling variant. +// This is a simple program that demonstrates the use of LEAF to transport e-objects between threads, +// without using exception handling. See capture_eh.cpp for the exception-handling variant. + +#include +#include -#include #include #include #include @@ -17,76 +19,72 @@ namespace leaf = boost::leaf; -//Define several e-types. +// Define several e-types. struct e_thread_id { std::thread::id value; }; struct e_failure_info1 { std::string value; }; struct e_failure_info2 { int value; }; -struct e_failure_info3 { long value; }; -struct e_failure_info4 { float value; }; -//A type that represents a successfully returned result from a task. +// A type that represents a successfully returned result from a task. struct task_result { }; -//This is a test task which succeeds or fails depending on its argument. -leaf::result task( bool succeed ) + // This is our task function. It produces objects of type task_result, but it may fail... +leaf::result task() { + bool succeed = (rand()%4) !=0; //...at random. if( succeed ) - return task_result(); //Simulate successful result. + return task_result { }; else - return leaf::error( + return leaf::new_error( e_thread_id{std::this_thread::get_id()}, e_failure_info1{"info"}, - e_failure_info2{42}, - e_failure_info4{42} ); -} - -//Launch the specified number of asynchronous tasks. In case an asynchronous task fails, its e-objects -//(initially stored in exp) are captured in a leaf::result, which transports them to the main thread. -template -std::vector>> launch_async_tasks( int thread_count ) -{ - std::vector>> fut; - std::generate_n( std::inserter(fut,fut.end()), thread_count, [ ] - { - return std::async( std::launch::async, [ ] - { - leaf::expect exp; - return capture(exp,task((rand()%4)!=0)); - } ); - } ); - return fut; -} + e_failure_info2{42} ); +}; int main() { - //Launch tasks, transport the specified e-types. For demonstration, note that the task provides - //failure_info4 which we don't care about, and that we say we could use failure_info3, but which - //the task doesn't provide. So, we'll only get failed_thread_id, failure_info1 and failure_info2. - auto fut = launch_async_tasks(42); + int const task_count = 42; - //Collect results or deal with failures. + // Container to collect the generated std::future objects. + std::vector>> fut; + + // Launch the tasks, but rather than launching the task function directly, we launch the + // wrapper function returned by leaf::capture_result. It captures the specified error object + // types and automatically transports them in the leaf::result it returns. + std::generate_n( std::inserter(fut,fut.end()), task_count, + [ ] + { + return std::async( + std::launch::async, + leaf::capture_result(&task) ); + } ); + + // Wait on the futures, get the task results, handle errors. for( auto & f : fut ) { f.wait(); - //Storage for e-objects. - leaf::expect exp; + leaf::handle_all( + [&]() -> leaf::result + { + LEAF_AUTO(r,f.get()); - //Get the task result, check for success. - if( leaf::result r = f.get() ) - { - //Success! Use *r to access task_result. - std::cout << "Success!" << std::endl; - } - else - { - //Failure! Handle error, print failure info. - bool matched = handle_error( exp, r, - [ ] ( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid ) - { - std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl; - } ); - assert(matched); - } + // Success! Use r to access task_result. + std::cout << "Success!" << std::endl; + (void) r; + return { }; + }, + + [ ]( e_failure_info1 const & v1, e_failure_info2 const & v2, e_thread_id const & tid ) + { + std::cerr << "Error in thread " << tid.value << "! failure_info1: " << v1.value << ", failure_info2: " << v2.value << std::endl; + }, + + [ ]( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + } ); } } diff --git a/example/exception_to_result.cpp b/example/exception_to_result.cpp new file mode 100644 index 0000000..1f6b3cc --- /dev/null +++ b/example/exception_to_result.cpp @@ -0,0 +1,112 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example demonstrates how to transport exceptions thrown by a low level function +// through an intermediate context that is not exception-safe, to be handled in a high level +// function which may or may not be exception-safe. + +// An real-world example for this use case is when a C API (which may not throw) is implemented +// using a C++ library that throws exceptions. As demonstrated below, these exception objects are +// intercepted and reported by leaf::result<>. + +#include +#include + +namespace leaf = boost::leaf; + + +class error_base: public virtual std::exception { }; +class error_a: public virtual error_base { }; +class error_b: public virtual error_base { }; +class error_c: public virtual error_base { }; + + +// Lower-level library function which throws exceptions. +int compute_answer_throws() +{ + switch( rand()%4 ) + { + default: return 42; + case 1: throw error_a(); + case 2: throw error_b(); + case 3: throw error_c(); + } +} + + +// Calls compute_answer_throws, switches to result for error handling. +leaf::result compute_answer() noexcept +{ + // Convert exceptions of types error_a and error_b to be communicated by leaf::result. + // Any other exception will be communicated as a std::exception_ptr. + return leaf::exception_to_result( + [ ] + { + return compute_answer_throws(); + } ); +} + + +// Print the answer if the call to compute_answer is successful. +leaf::result print_answer() noexcept +{ + LEAF_AUTO( answer, compute_answer()); + std::cout << "Answer: " << answer << std::endl; + return { }; +} + + +int main() +{ + // Exercise print_answer a few times and handle errors. Note that the exception objects that + // compute_answer_throws throws are handled as regular LEAF error objects... + for( int i=0; i!=42; ++i ) + { + leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_CHECK(print_answer()); + return { }; + }, + + [ ]( error_a const & e ) + { + std::cerr << "Error A!" << std::endl; + }, + + [ ]( error_b const & e ) + { + std::cerr << "Error B!" << std::endl; + }, + + //...except for error_c errors, which (for demonstration) are captured as exceptions + // into std::exception_ptr as "unknown" exception. Presumably this should not + // happen, therefore at this point we treat this situation a logic error: we print + // diagnostic information and bail out. + [ ]( std::exception_ptr const * ep ) + { + std::cerr << "Got unknown error!" << std::endl; + + // Why do we take std::exception_ptr as a pointer? Because handle_all requires + // that the last handler matches any error -- taken as a pointer, if there isn't a + // std::exception_ptr associated with the error, the handler will still be matched + //(with 0 passed for ep). Had we taken it by value or by const &, the program + // would not compile. + if( ep ) + leaf::try_( + [&] + { + std::rethrow_exception(*ep); + }, + [ ]( leaf::error_info const & unmatched ) + { + std::cerr << unmatched << std::endl; + } ); + } ); + } + + return 0; +} diff --git a/example/lua_callback_eh.cpp b/example/lua_callback_eh.cpp index fd4d9ca..8d11d28 100644 --- a/example/lua_callback_eh.cpp +++ b/example/lua_callback_eh.cpp @@ -1,12 +1,12 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -//This is a simple program that shows how to propagate leaf::error objects out -//of a C-callback, and converting them to leaf::result as soon as control -//reaches C++. +// This is a simple program that shows how to propagate error objects out +// of a C-callback, and converting them to leaf::result as soon as control +// reaches C++. extern "C" { #include "lua.h" @@ -18,8 +18,6 @@ extern "C" { namespace leaf = boost::leaf; -struct lua_failure: std::exception { }; - enum do_work_error_code { ec1=1, @@ -33,40 +31,40 @@ struct e_lua_pcall_error { int value; }; struct e_lua_error_message { std::string value; }; -//This is a C callback function with a specific signature, made accessible to programs -//written in Lua. +// This is a C callback function with a specific signature, made accessible to programs +// written in Lua. -//If it succeeds, it returns an int answer, by pushing it onto the Lua stack. But "sometimes" -//it fails, in which case it calls luaL_error. This causes the Lua interpreter to abort and pop -//back into the C++ code which called it (see call_lua below). +// If it succeeds, it returns an int answer, by pushing it onto the Lua stack. But "sometimes" +// it fails, in which case it calls luaL_error. This causes the Lua interpreter to abort and pop +// back into the C++ code which called it (see call_lua below). int do_work( lua_State * L ) { bool success=rand()%2; if( success ) { - lua_pushnumber(L,42); //Return 42 to the calling Lua program. + lua_pushnumber(L,42); // Return 42 to the calling Lua program. return 1; } else { - //Remarkably, the Lua interpreter is exception-safe. So, just throw. - throw leaf::exception(lua_failure(),ec1); + // Remarkably, the Lua interpreter is exception-safe. So, just throw. + throw leaf::exception(std::exception(),ec1); } } std::shared_ptr init_lua_state() { - //Create a new lua_State, we'll use std::shared_ptr for automatic cleanup. + // Create a new lua_State, we'll use std::shared_ptr for automatic cleanup. std::shared_ptr L(lua_open(),&lua_close); - //Register the do_work function (above) as a C callback, under the global - //Lua name "do_work". With this, calls from Lua programs to do_work - //will land in the do_work C function we've registered. + // Register the do_work function (above) as a C callback, under the global + // Lua name "do_work". With this, calls from Lua programs to do_work + // will land in the do_work C function we've registered. lua_register( &*L, "do_work", &do_work ); - //Pass some Lua code as a C string literal to Lua. This creates a global Lua - //function called "call_do_work", which we will later ask Lua to execute. + // Pass some Lua code as a C string literal to Lua. This creates a global Lua + // function called "call_do_work", which we will later ask Lua to execute. luaL_dostring( &*L, "\ \n function call_do_work()\ \n return do_work()\ @@ -76,27 +74,27 @@ std::shared_ptr init_lua_state() } -//Here we will ask Lua to execute the function call_do_work, which is written -//in Lua, and returns the value from do_work, which is written in C++ and -//registered with the Lua interpreter as a C callback. +// Here we will ask Lua to execute the function call_do_work, which is written +// in Lua, and returns the value from do_work, which is written in C++ and +// registered with the Lua interpreter as a C callback. -//If do_work succeeds, we return the resulting int answer. If it fails, we'll -//communicate that failure to our caller. +// If do_work succeeds, we return the resulting int answer. +// If it fails, we'll communicate that failure to our caller. int call_lua( lua_State * L ) { - //Ask the Lua interpreter to call the global Lua function call_do_work. + // Ask the Lua interpreter to call the global Lua function call_do_work. lua_getfield( L, LUA_GLOBALSINDEX, "call_do_work" ); if( int err=lua_pcall(L,0,1,0) ) { - //Something went wrong with the call, so we'll throw lua_failure. - //This is definitely not a do_work failure, because it throws on error. + // Something went wrong with the call, so we'll throw std::exception. + // This is definitely not a do_work failure, because it throws on error. auto propagate = leaf::preload( e_lua_error_message{lua_tostring(L,1)} ); lua_pop(L,1); - throw leaf::exception( lua_failure(), e_lua_pcall_error{err} ); + throw leaf::exception( std::exception(), e_lua_pcall_error{err} ); } else { - //Success! Just return the int answer. + // Success! Just return the int answer. int answer=lua_tonumber(L,-1); lua_pop(L,1); return answer; @@ -106,29 +104,35 @@ int call_lua( lua_State * L ) int main() noexcept { std::shared_ptr L=init_lua_state(); - leaf::expect exp; + for( int i=0; i!=10; ++i ) - try - { - int r = call_lua(&*L); - std::cout << "do_work succeeded, answer=" << r << '\n'; - } - catch( lua_failure const & e ) - { - handle_exception( exp, e, + { + leaf::try_( - //Handle do_work failures: - [ ]( do_work_error_code const & e ) - { - std::cout << "Got do_work_error_code = " << e << "!\n"; - }, + [&] + { + int answer = call_lua(&*L); + std::cout << "do_work succeeded, answer=" << answer << '\n'; + }, + + [ ]( do_work_error_code e ) + { + std::cout << "Got do_work_error_code = " << e << "!\n"; + }, + + [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) + { + std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; + }, + + [ ]( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + } ); + } - //Handle all other lua_pcall failures: - [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) - { - std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; - } - ); - } return 0; } diff --git a/example/lua_callback_result.cpp b/example/lua_callback_result.cpp index 3c63fdb..e776740 100644 --- a/example/lua_callback_result.cpp +++ b/example/lua_callback_result.cpp @@ -1,12 +1,12 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -//This is a simple program that shows how to propagate leaf::error objects out -//of a C-callback, and converting them to leaf::result as soon as control -//reaches C++. +// This is a simple program that shows how to propagate error objects out +// of a C-callback, and converting them to leaf::result as soon as control +// reaches C++. extern "C" { #include "lua.h" @@ -31,25 +31,25 @@ struct e_lua_pcall_error { int value; }; struct e_lua_error_message { std::string value; }; -//This is a C callback function with a specific signature, made accessible to programs -//written in Lua. +// This is a C callback function with a specific signature, made accessible to programs +// written in Lua. -//If it succeeds, it returns an int answer, by pushing it onto the Lua stack. But "sometimes" -//it fails, in which case it calls luaL_error. This causes the Lua interpreter to abort and pop -//back into the C++ code which called it (see call_lua below). +// If it succeeds, it returns an int answer, by pushing it onto the Lua stack. But "sometimes" +// it fails, in which case it calls luaL_error. This causes the Lua interpreter to abort and pop +// back into the C++ code which called it (see call_lua below). int do_work( lua_State * L ) noexcept { bool success=rand()%2; if( success ) { - lua_pushnumber(L,42); //Return 42 to the calling Lua program. + lua_pushnumber(L,42); // Return 42 to the calling Lua program. return 1; } else { - //Associate an do_work_error_code object with the *next* leaf::error object we will - //definitely return from the call_lua function... - leaf::next_error_value().propagate(ec1); + // Associate an do_work_error_code object with the *next* leaf::error_id object we will + // definitely return from the call_lua function... + leaf::next_error().propagate(ec1); //...once control reaches it, after we tell the Lua interpreter to abort the program. return luaL_error(L,"do_work_error"); @@ -59,16 +59,16 @@ int do_work( lua_State * L ) noexcept std::shared_ptr init_lua_state() noexcept { - //Create a new lua_State, we'll use std::shared_ptr for automatic cleanup. + // Create a new lua_State, we'll use std::shared_ptr for automatic cleanup. std::shared_ptr L(lua_open(),&lua_close); - //Register the do_work function (above) as a C callback, under the global - //Lua name "do_work". With this, calls from Lua programs to do_work - //will land in the do_work C function we've registered. + // Register the do_work function (above) as a C callback, under the global + // Lua name "do_work". With this, calls from Lua programs to do_work + // will land in the do_work C function we've registered. lua_register( &*L, "do_work", &do_work ); - //Pass some Lua code as a C string literal to Lua. This creates a global Lua - //function called "call_do_work", which we will later ask Lua to execute. + // Pass some Lua code as a C string literal to Lua. This creates a global Lua + // function called "call_do_work", which we will later ask Lua to execute. luaL_dostring( &*L, "\ \n function call_do_work()\ \n return do_work()\ @@ -78,30 +78,30 @@ std::shared_ptr init_lua_state() noexcept } -//Here we will ask Lua to execute the function call_do_work, which is written -//in Lua, and returns the value from do_work, which is written in C++ and -//registered with the Lua interpreter as a C callback. +// Here we will ask Lua to execute the function call_do_work, which is written +// in Lua, and returns the value from do_work, which is written in C++ and +// registered with the Lua interpreter as a C callback. -//If do_work succeeds, we return the resulting int answer in leaf::result. -//If it fails, we'll communicate that failure to our caller. +// If do_work succeeds, we return the resulting int answer in leaf::result. +// If it fails, we'll communicate that failure to our caller. leaf::result call_lua( lua_State * L ) { - //Ask the Lua interpreter to call the global Lua function call_do_work. + // Ask the Lua interpreter to call the global Lua function call_do_work. lua_getfield( L, LUA_GLOBALSINDEX, "call_do_work" ); if( int err=lua_pcall(L,0,1,0) ) { - //Something went wrong with the call, so we'll return a leaf::error. - //If this is a do_work failure, the do_work_error_code object prepared in - //do_work will become associated with this leaf::error value. If not, - //we will still need to communicate that the lua_pcall failed with an - //error code and an error message. + // Something went wrong with the call, so we'll return a new_error. + // If this is a do_work failure, the do_work_error_code object prepared in + // do_work will become associated with this leaf::error_id value. If not, + // we will still need to communicate that the lua_pcall failed with an + // error code and an error message. auto propagate = leaf::preload( e_lua_error_message{lua_tostring(L,1)} ); lua_pop(L,1); - return leaf::error( e_lua_pcall_error{err} ); + return leaf::new_error( e_lua_pcall_error{err} ); } else { - //Success! Just return the int answer. + // Success! Just return the int answer. int answer=lua_tonumber(L,-1); lua_pop(L,1); return answer; @@ -112,28 +112,35 @@ int main() noexcept { std::shared_ptr L=init_lua_state(); - leaf::expect exp; - for( int i=0; i!=10; ++i ) - if( leaf::result r = call_lua(&*L) ) - std::cout << "do_work succeeded, answer=" << *r << '\n'; - else - { - bool matched = handle_error( exp, r, + { + leaf::handle_all( - //Handle e_do_work failures: - [ ]( do_work_error_code e ) - { - std::cout << "Got do_work_error_code = " << e << "!\n"; - }, + [&]() -> leaf::result + { + LEAF_AUTO(answer, call_lua(&*L)); + std::cout << "do_work succeeded, answer=" << answer << '\n'; + return { }; + }, + + [ ]( do_work_error_code e ) + { + std::cout << "Got do_work_error_code = " << e << "!\n"; + }, + + [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) + { + std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; + }, + + [ ]( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + } ); + } - //Handle all other lua_pcall failures: - [ ]( e_lua_pcall_error const & err, e_lua_error_message const & msg ) - { - std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; - } - ); - assert(matched); - } return 0; } diff --git a/example/print_file_eh.cpp b/example/print_file_eh.cpp index 75aeb7e..f08d09f 100644 --- a/example/print_file_eh.cpp +++ b/example/print_file_eh.cpp @@ -1,14 +1,15 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -//This example demonstrates the basic use of LEAF to augment error conditions with -//additional information when using exceptions to report failures. See print_file_result.cpp -//for the variant that doesn't use exceptions. +// This is a short but complete program that reads a text file in a buffer and prints it to std::cout, +// using LEAF to handle errors. It uses exception handling. -#include +#include +#include +#include #include #include #include @@ -16,13 +17,7 @@ namespace leaf = boost::leaf; -//We could define our own e-types, but for this example the ones defined in -// are a perfect match. -using leaf::e_file_name; -using leaf::e_errno; - - -//Exception type hierarchy. +// Exception type hierarchy. struct print_file_error : virtual std::exception { }; struct command_line_error : virtual print_file_error { }; struct bad_command_line : virtual command_line_error { }; @@ -34,152 +29,162 @@ struct input_file_read_error : virtual input_file_error { }; struct input_eof_error : virtual input_file_error { }; +// We will handle all failures in our main function, but first, here are the declarations of the functions it calls, each +// communicating failures by throwing exceptions + +// Parse the command line, return the file name. +char const * parse_command_line( int argc, char const * argv[ ] ); + +// Open a file for reading. +std::shared_ptr file_open( char const * file_name ); + +// Return the size of the file. +int file_size( FILE & f ); + +// Read size bytes from f into buf. +void file_read( FILE & f, void * buf, int size ); + + +int main( int argc, char const * argv[ ] ) +{ + // Configure std::cout to throw on error. + std::cout.exceptions(std::ostream::failbit | std::ostream::badbit); + + return leaf::try_( + [&] + { + char const * file_name = parse_command_line(argc,argv); + + auto propagate = leaf::preload( leaf::e_file_name{file_name} ); + + std::shared_ptr f = file_open( file_name ); + + int s = file_size(*f); + + std::string buffer( 1 + s, '\0' ); + file_read(*f,&buffer[0],buffer.size()-1); + + auto propagate2 = leaf::defer([ ] { return leaf::e_errno{errno}; } ); + std::cout << buffer; + std::cout.flush(); + + return 0; + }, + + // Each of the lambdas below is an error handler. LEAF will consider them, in order, and call the first one that matches + // the available error information. + + // This handler will be called if the error includes: + // - a caught exception of type input_file_open_error, and + // - an object of type leaf::e_errno that has .value equal to ENOENT, and + // - an object of type leaf::e_file_name. + [ ]( leaf::catch_, leaf::match, leaf::e_file_name const & fn ) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 1; + }, + + // This handler will be called if the error includes: + // - a caught exception of type input_file_open_error, and + // - an object of type leaf::e_errno (regardless of its .value), and + // - an object of type leaf::e_file_name. + [ ]( leaf::catch_, leaf::e_errno const & errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 2; + }, + + // This handler will be called if the error includes: + // - a caught exception of type input_error, and + // - an object of type leaf::e_errno (regardless of its .value), and + // - an object of type leaf::e_file_name. + [ ]( leaf::catch_, leaf::e_errno const & errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; + return 3; + }, + + // This handler will be called if the error includes: + // - a caught exception of type std::ostream::failure, and + // - an object of type leaf::e_errno (regardless of its .value), + [ ]( leaf::catch_, leaf::e_errno const & errn ) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 4; + }, + + // This handler will be called if the error includes a caught exception of type bad_command_line. + [ ]( leaf::catch_ ) + { + std::cout << "Bad command line argument" << std::endl; + return 5; + }, + + // This last handler matches any error: it prints diagnostic information to help debug logic errors in the program, since it + // failed to match an appropriate error handler to the error condition it encountered. In this program this handler will + // never be called. + [ ]( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 6; + } ); +} + + +// Implementations of the functions called by main: + + +// Parse the command line, return the file name. +char const * parse_command_line( int argc, char const * argv[ ] ) +{ + if( argc==2 ) + return argv[1]; + else + throw leaf::exception(bad_command_line()); +} + + +// Open a file for reading. std::shared_ptr file_open( char const * file_name ) { if( FILE * f = fopen(file_name,"rb") ) return std::shared_ptr(f,&fclose); else - throw leaf::exception( input_file_open_error(), e_file_name{file_name}, e_errno{errno} ); + throw leaf::exception(input_file_open_error(), leaf::e_errno{errno}); } +// Return the size of the file. int file_size( FILE & f ) { - //All exceptions escaping this function will automatically propagate errno. - auto propagate = leaf::defer( [ ] { return e_errno{errno}; } ); + // All exceptions escaping this function will automatically propagate errno. + auto propagate = leaf::defer( [ ] { return leaf::e_errno{errno}; } ); if( fseek(&f,0,SEEK_END) ) - throw input_file_size_error(); + throw leaf::exception(input_file_size_error()); int s = ftell(&f); if( s==-1L ) - throw input_file_size_error(); + throw leaf::exception(input_file_size_error()); if( fseek(&f,0,SEEK_SET) ) - throw input_file_size_error(); + throw leaf::exception(input_file_size_error()); return s; } +// Read size bytes from f into buf. void file_read( FILE & f, void * buf, int size ) { int n = fread(buf,1,size,&f); if( ferror(&f) ) - throw leaf::exception( input_file_read_error(), e_errno{errno} ); + throw leaf::exception( input_file_read_error(), leaf::e_errno{errno} ); if( n!=size ) - throw input_eof_error(); -} - - -void print_file( char const * file_name ) -{ - std::shared_ptr f = file_open( file_name ); - - auto propagate1 = leaf::preload( e_file_name{file_name} ); - - std::string buffer( 1+file_size(*f), '\0' ); - file_read(*f,&buffer[0],buffer.size()-1); - - auto propagate2 = leaf::defer([ ] { return e_errno{errno}; } ); - std::cout << buffer; - std::cout.flush(); -} - - -char const * parse_command_line( int argc, char const * argv[ ] ) -{ - if( argc!=2 ) - throw bad_command_line(); - return argv[1]; -} - - -int main( int argc, char const * argv[ ] ) -{ - //Configure std::cout to throw on error. - std::cout.exceptions ( std::ostream::failbit | std::ostream::badbit ); - - //We expect e_file_name and e_errno objects to be associated with errors - //handled in this function. They will be stored inside of exp. - leaf::expect exp; - - try - { - print_file(parse_command_line(argc,argv)); - return 0; - } - catch( bad_command_line const & ) - { - std::cout << "Bad command line argument" << std::endl; - return 1; - } - catch( input_file_open_error const & e ) - { - //handle_exception takes a list of functions (in this case only one). It attempts to - //match each function (in order) to objects currently available in exp, which - //are associated with the error value stored in e. If no function can be matched, - //handle_exception returns false. Otherwise the matched function is invoked with - //the corresponding available error objects. - leaf::handle_exception( exp, e, - - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - if( errn.value==ENOENT ) - std::cerr << "File not found: " << fn.value << std::endl; - else - std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; - } - - ); - return 2; - } - catch( input_error const & e ) - { - //In this case handle_exception is given 3 functions. It will first check if both - //e_file_name and e_errno, associated with e, are avialable in exp; if not, it will - //next check if just e_errno is available; and if not, the last function (which - //takes no arguments) will always match to print a generic error message. - leaf::handle_exception( exp, e, - - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - std::cerr << "Input error, " << fn.value << ", errno=" << errn << std::endl; - }, - - [ ] ( e_errno const & errn ) - { - std::cerr << "Input error, errno=" << errn << std::endl; - }, - - [ ] - { - std::cerr << "Input error" << std::endl; - } - - ); - return 3; - } - catch( std::ostream::failure const & e ) - { - //Report failure to write to std::cout, print the relevant errno. - leaf::handle_exception( exp, e, - - [ ] ( e_errno const & errn ) - { - std::cerr << "Output error, errno=" << errn << std::endl; - } - - ); - return 4; - } - catch(...) - { - //This catch-all case helps diagnose logic errors (presumably, missing catch). - std::cerr << "Unknown error, cryptic information follows." << std::endl; - leaf::diagnostic_output_current_exception(std::cerr,exp); - return 5; - } + throw leaf::exception(input_eof_error()); } diff --git a/example/print_file_result.cpp b/example/print_file_result.cpp index 76832d1..39f7f0b 100644 --- a/example/print_file_result.cpp +++ b/example/print_file_result.cpp @@ -1,207 +1,196 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -//This example demonstrates the basic use of LEAF to augment error conditions with -//additional information when using result to report failures. See print_file_eh.cpp -//for the variant that uses exception handling. +// This is a short but complete program that reads a text file in a buffer and prints it to std::cout, +// using LEAF to handle errors. It does not use exception handling. -#include +#include +#include #include #include #include namespace leaf = boost::leaf; - -//We could define our own e-types, but for this example the ones -//defined in are a perfect match. -using leaf::e_file_name; -using leaf::e_errno; - - -//Error codes +// First, we need an enum to define our error codes: enum error_code { - input_file_open_error=1, + bad_command_line = 1, + input_file_open_error, input_file_size_error, input_file_read_error, input_eof_error, cout_error }; + +// To enable LEAF to work with our error_code enum, we need to specialize the is_error_type template: namespace boost { namespace leaf { template<> struct is_error_type: std::true_type { }; } } +// We will handle all failures in our main function, but first, here are the declarations of the functions it calls, each +// communicating failures using leaf::result: + +// Parse the command line, return the file name. +leaf::result parse_command_line( int argc, char const * argv[ ] ); + +// Open a file for reading. +leaf::result> file_open( char const * file_name ); + +// Return the size of the file. +leaf::result file_size( FILE & f ); + +// Read size bytes from f into buf. +leaf::result file_read( FILE & f, void * buf, int size ); + + +// The main function, which handles all errors. +int main( int argc, char const * argv[ ] ) +{ + return leaf::handle_all( + + [&]() -> leaf::result + { + LEAF_AUTO(file_name, parse_command_line(argc,argv)); + + auto propagate = leaf::preload( leaf::e_file_name{file_name} ); + + LEAF_AUTO(f, file_open(file_name)); + + LEAF_AUTO(s, file_size(*f)); + + std::string buffer( 1 + s, '\0' ); + LEAF_CHECK(file_read(*f, &buffer[0], buffer.size()-1)); + + auto propagate2 = leaf::defer([ ] { return leaf::e_errno{errno}; } ); + std::cout << buffer; + std::cout.flush(); + if( std::cout.fail() ) + return leaf::new_error( cout_error ); + + return 0; + }, + + // Each of the lambdas below is an error handler. LEAF will consider them, in order, and call the first one that matches + // the available error information. + + // This handler will be called if the error includes: + // - an object of type error_code equal to input_file_open_error, and + // - an object of type leaf::e_errno that has .value equal to ENOENT, and + // - an object of type leaf::e_file_name. + [ ]( leaf::match, leaf::match, leaf::e_file_name const & fn ) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 1; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to input_file_open_error, and + // - an object of type leaf::e_errno (regardless of its .value), and + // - an object of type leaf::e_file_name. + [ ]( leaf::match, leaf::e_errno const & errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 2; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to any of input_file_size_error, input_file_read_error, input_eof_error, and + // - an object of type leaf::e_errno (regardless of its .value), and + // - an object of type leaf::e_file_name. + [ ]( leaf::match, leaf::e_errno const & errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; + return 3; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to cout_error, and + // - an object of type leaf::e_errno (regardless of its .value), + [ ]( leaf::match, leaf::e_errno const & errn ) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 4; + }, + + // This handler will be called if the error includes an object of type error_code equal to bad_command_line. + [ ]( leaf::match ) + { + std::cout << "Bad command line argument" << std::endl; + return 5; + }, + + // This last handler matches any error: it prints diagnostic information to help debug logic errors in the program, since it + // failed to match an appropriate error handler to the error condition it encountered. In this program this handler will + // never be called. + [ ]( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 6; + } ); +} + + +// Implementations of the functions called by main: + + +// Parse the command line, return the file name. +leaf::result parse_command_line( int argc, char const * argv[ ] ) +{ + if( argc==2 ) + return argv[1]; + else + return leaf::new_error(bad_command_line); +} + + +// Open a file for reading. leaf::result> file_open( char const * file_name ) { if( FILE * f = fopen(file_name,"rb") ) return std::shared_ptr(f,&fclose); else - return leaf::error( input_file_open_error, e_file_name{file_name}, e_errno{errno} ); + return leaf::new_error(input_file_open_error, leaf::e_errno{errno}); } +// Return the size of the file. leaf::result file_size( FILE & f ) { - auto propagate = leaf::defer([ ] { return e_errno{errno}; } ); + // All exceptions escaping this function will automatically propagate errno. + auto propagate = leaf::defer([ ] { return leaf::e_errno{errno}; }); if( fseek(&f,0,SEEK_END) ) - return leaf::error( input_file_size_error ); + return leaf::new_error(input_file_size_error); int s = ftell(&f); if( s==-1L ) - return leaf::error( input_file_size_error ); + return leaf::new_error(input_file_size_error); if( fseek(&f,0,SEEK_SET) ) - return leaf::error( input_file_size_error ); + return leaf::new_error(input_file_size_error); return s; } +// Read size bytes from f into buf. leaf::result file_read( FILE & f, void * buf, int size ) { int n = fread(buf,1,size,&f); + if( ferror(&f) ) - return leaf::error( input_file_read_error, e_errno{errno} ); + return leaf::new_error(input_file_read_error, leaf::e_errno{errno}); if( n!=size ) - return leaf::error( input_eof_error ); + return leaf::new_error(input_eof_error); return { }; } - - -leaf::result print_file( char const * file_name ) -{ - LEAF_AUTO(f,file_open(file_name)); - - auto propagate = leaf::preload( e_file_name{file_name} ); - - LEAF_AUTO(s,file_size(*f)); - - std::string buffer( 1+s, '\0' ); - LEAF_CHECK(file_read(*f,&buffer[0],buffer.size()-1)); - - std::cout << buffer; - std::cout.flush(); - if( std::cout.fail() ) - return leaf::error( cout_error ); - - return { }; -} - - -char const * parse_command_line( int argc, char const * argv[ ] ) -{ - if( argc!=2 ) - return 0; - else - return argv[1]; -} - - -int main( int argc, char const * argv[ ] ) -{ - char const * fn = parse_command_line(argc,argv); - if( !fn ) - { - std::cout << "Bad command line argument" << std::endl; - return 1; - } - - //We expect error_code, e_file_name and e_errno objects to be associated - //with errors handled in this function. They will be stored inside of exp. - leaf::expect exp; - - if( auto r = print_file(fn) ) - { - return 0; //Success, we're done! - } - else - { - //Probe exp for the error_code object associated with the error stored in r. - switch( auto ec = *leaf::peek(exp,r) ) - { - case input_file_open_error: - { - //handle_error takes a list of functions (in this case only one). It attempts to - //match each function (in order) to objects currently available in exp, which - //are associated with the error value stored in r. If no function can be matched, - //handle_error returns false. Otherwise the matched function is invoked with - //the matching corresponding error objects. - bool matched = leaf::handle_error( exp, r, - - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - if( errn.value==ENOENT ) - std::cerr << "File not found: " << fn.value << std::endl; - else - std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; - } - - ); - assert(matched); - return 2; - } - - case input_file_size_error: - case input_file_read_error: - case input_eof_error: - { - //In this case handle_error is given 3 functions. It will first check if both - //e_file_name and e_errno, associated with r, are avialable in exp; if not, it will - //next check if just e_errno is available; and if not, the last function (which - //takes no arguments) will always match to print a generic error message. - bool matched = leaf::handle_error( exp, r, - - [ ] ( e_file_name const & fn, e_errno const & errn ) - { - std::cerr << "Failed to access " << fn.value << ", errno=" << errn << std::endl; - }, - - [ ] ( e_errno const & errn ) - { - std::cerr << "I/O error, errno=" << errn << std::endl; - }, - - [ ] - { - std::cerr << "I/O error" << std::endl; - } - - ); - assert(matched); - return 3; - } - - case cout_error: - { - //Report failure to write to std::cout, print the relevant errno. - bool matched = leaf::handle_error( exp, r, - - [ ] ( e_errno const & errn ) - { - std::cerr << "Output error, errno=" << errn << std::endl; - } - - ); - assert(matched); - return 4; - } - - //This catch-all case helps diagnose logic errors (presumably, missing case labels - //in the switch statement). - default: - { - std::cerr << "Unknown error code " << ec << ", cryptic information follows." << std::endl; //<7> - leaf::diagnostic_output(std::cerr,exp,r); - return 5; - } - } - } -} diff --git a/example/print_half.cpp b/example/print_half.cpp index 2bc2496..7d122d4 100644 --- a/example/print_half.cpp +++ b/example/print_half.cpp @@ -1,14 +1,13 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -//This program is an adaptation of the following Boost Outcome example: -//https://github.com/ned14/outcome/blob/master/doc/src/snippets/using_result.cpp +// This program is an adaptation of the following Boost Outcome example: +// https://github.com/ned14/outcome/blob/master/doc/src/snippets/using_result.cpp -#include -#include +#include #include #include #include @@ -30,18 +29,18 @@ namespace boost { namespace leaf { leaf::result convert(const std::string& str) noexcept { if (str.empty()) - return leaf::error(ConversionErrc::EmptyString); + return leaf::new_error(ConversionErrc::EmptyString); if (!std::all_of(str.begin(), str.end(), ::isdigit)) - return leaf::error(ConversionErrc::IllegalChar); + return leaf::new_error(ConversionErrc::IllegalChar); if (str.length() > 9) - return leaf::error(ConversionErrc::TooLong); + return leaf::new_error(ConversionErrc::TooLong); return atoi(str.c_str()); } -//Do not expect BigInt to actually work -- it's a stub. +// Do not static_store BigInt to actually work -- it's a stub. struct BigInt { static leaf::result fromString(const std::string& s) { return BigInt{s}; } @@ -50,61 +49,54 @@ struct BigInt friend std::ostream& operator<<(std::ostream& o, const BigInt&) { return o << "big int half"; } }; -//This function handles ConversionErrc::TooLong errors, forwards any other error to the caller. +// This function handles ConversionErrc::TooLong errors, forwards any other error to the caller. leaf::result print_half(const std::string& text) { - leaf::expect exp; - if (leaf::result r = convert(text)) - { - std::cout << (r.value() / 2) << std::endl; - return { }; - } - else - { - return leaf::handle_error( exp, r, - [&]( ConversionErrc ec ) -> leaf::result - { - if( ec != ConversionErrc::TooLong ) - return r.error(); - LEAF_AUTO(i, BigInt::fromString(text)); - std::cout << i.half() << std::endl; - return { }; - } ); - } + return leaf::handle_some( + [&]() -> leaf::result + { + LEAF_AUTO(r,convert(text)); + std::cout << r / 2 << std::endl; + return { }; + }, + [&]( leaf::match ) -> leaf::result + { + LEAF_AUTO(i, BigInt::fromString(text)); + std::cout << i.half() << std::endl; + return { }; + } ); } int main( int argc, char const * argv[ ] ) { - leaf::expect exp; - if( leaf::result r = print_half(argc<2 ? "" : argv[1]) ) - { - std::cout << "ok" << std::endl; - return 0; - } - else - { - return handle_error( exp, r, - [ ]( ConversionErrc ec ) - { - switch(ec) - { - case ConversionErrc::EmptyString: - std::cerr << "Empty string!" << std::endl; - break; - default: - assert(ec==ConversionErrc::IllegalChar); //Because print_half deals with ConversionErrc::TooLong. - std::cerr << "Illegal char!" << std::endl; - } - return 1; - }, - [&exp,&r] - { - //This will never execute in this program, but it would detect logic errors where an unknown error reaches main. - //In this case, we print diagnostic information. Consider using leaf::unexpected_diagnostic_output in the - //definition of exp. - std::cerr << "Unknown error, cryptic diagnostic information follows." << std::endl; - leaf::diagnostic_output(std::cerr,exp,r); - return 2; - } ); - } + return leaf::handle_all( + [&]() -> leaf::result + { + LEAF_CHECK( print_half(argc<2 ? "" : argv[1]) ); + std::cout << "ok" << std::endl; + return 0; + }, + + [ ]( leaf::match ) + { + std::cerr << "Empty string!" << std::endl; + return 1; + }, + + [ ]( leaf::match ) + { + std::cerr << "Illegal char!" << std::endl; + return 2; + }, + + [ ]( leaf::error_info const & unmatched ) + { + // This will never execute in this program, but it would detect logic errors where an unknown error reaches main. + // In this case, we print diagnostic information. + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 3; + } ); } diff --git a/example/return_exception.cpp b/example/return_exception.cpp deleted file mode 100644 index ae5d60f..0000000 --- a/example/return_exception.cpp +++ /dev/null @@ -1,120 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -//This example demonstrates how to transport exceptions thrown by a low level function -//through an intermediate context that is not exception-safe, to be handled in a high level -//function which may or may not be exception-safe. - -//An real-world example for this use case is when a C API (which may not throw) is implemented -//using a C++ library that throws exceptions. As demonstrated below, these exception objects are -//intercepted and reported by leaf::result<>. - -#include -#include -#include - -namespace leaf = boost::leaf; - -class error_base: public virtual std::exception { }; -class error_a: public virtual error_base { }; -class error_b: public virtual error_base { }; -class error_c: public virtual error_base { }; - -//Lower-level library function which throws exceptions. -int compute_answer_throws() -{ - switch( rand()%4 ) - { - default: return 42; - case 1: throw error_a(); - case 2: throw error_b(); - case 3: throw error_c(); - } -} - -//A wrapper function that provides a noexcept interface to compute_answer_throws (above), -//by transporting different exception types using leaf::result. -leaf::result compute_answer() noexcept -{ - //We define special handling for error_a and error_b: exception objects of these types - //will be returned directly as LEAF errors... - try - { - return compute_answer_throws(); - } - catch( error_a const & e ) - { - return leaf::error(e); - } - catch( error_b const & e ) - { - return leaf::error(e); - } - catch(...) - { - //...but "unknown" exceptions are still captured and transported using std::exception_ptr. - //This is less efficient, because it will likely allocate memory dynamically. - return leaf::error(std::current_exception()); - } -} - -//Intermediate caller of compute_answer. -leaf::result print_answer() noexcept -{ - if( leaf::result r = compute_answer() ) - { - std::cout << "Answer: " << r.value() << std::endl; - return { }; - } - else - return r.error(); -} - -int main() -{ - //Exercise print_answer a few times and handle errors. Note that the exception objects that - //compute_answer_throws throws will land in the exp object, rather than arrive as exceptions... - for( int i=0; i!=10; ++i ) - { - leaf::expect exp; - if( leaf::result r = print_answer() ) - continue; - else - { - leaf::handle_error( exp, r, - - [ ]( error_a const & e ) - { - std::cerr << "Error A!" << std::endl; - }, - - [ ]( error_b const & e ) - { - std::cerr << "Error B!" << std::endl; - }, - - //...except for error_c errors, which (for demonstration) are captured as exceptions into - //std::exception_ptr, together with any other unknow exception. Presumably this should - //never happen, therefore at this point we treat this situation a a logic error: we print - //diagnostic information and bail out. - [&exp]( std::exception_ptr const & ep ) - { - assert(ep); - try - { - std::rethrow_exception(ep); - } - catch(...) - { - leaf::diagnostic_output_current_exception(std::cerr,exp); - } - } - - ); - } - } - return 0; -} diff --git a/include/boost/leaf/all.hpp b/include/boost/leaf/all.hpp index 1e2bd5f..e8e6222 100644 --- a/include/boost/leaf/all.hpp +++ b/include/boost/leaf/all.hpp @@ -1,20 +1,21 @@ #ifndef BOOST_LEAF_3382F652FCDD11E8AAD1CCB3642D5A5F #define BOOST_LEAF_3382F652FCDD11E8AAD1CCB3642D5A5F -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#include +#include #include -#include -#include #include -#include -#include -#include +#include +#include #include #include +#include +#include #endif diff --git a/include/boost/leaf/capture_exception.hpp b/include/boost/leaf/capture_exception.hpp new file mode 100644 index 0000000..717e843 --- /dev/null +++ b/include/boost/leaf/capture_exception.hpp @@ -0,0 +1,134 @@ +#ifndef BOOST_LEAF_BC24FB98B2DE11E884419CF5AD35F1A2 +#define BOOST_LEAF_BC24FB98B2DE11E884419CF5AD35F1A2 + +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +namespace boost { namespace leaf { + + namespace leaf_detail + { + class captured_exception_impl: + public captured_exception, + public error_id + { + std::exception_ptr ex_; + std::shared_ptr ds_; + bool had_error_; + void (*print_captured_types_)(std::ostream &); + + public: + + captured_exception_impl( std::exception_ptr && ex, std::shared_ptr && ds, bool had_error, void (*print_captured_types)(std::ostream &) ) noexcept: + error_id(ds->error()), + ex_(std::move(ex)), + ds_(std::move(ds)), + had_error_(had_error), + print_captured_types_(print_captured_types) + { + assert(ex_); + assert(print_captured_types_!=0); + } + + [[noreturn]] void unload_and_rethrow_original_exception() + { + std::shared_ptr ds; ds.swap(ds_); + assert(ds); + if( had_error_ ) + { + error_id id = ds->unload(); + assert(id==*this); + (void) id; + } + else + ds->unload(next_error()); + std::rethrow_exception(ex_); + } + + void print( std::ostream & os ) const + { + print_captured_types_(os); + } + }; + + //////////////////////////////////////// + + template + struct print_types; + + template + struct print_types + { + static void print( std::ostream & os ) + { + os << '\t' << type() << std::endl; + print_types::print(os); + } + }; + + template <> + struct print_types<> + { + static void print( std::ostream & ) + { + } + }; + + //////////////////////////////////////// + + template + struct exception_trap; + + template class L, class... A, class... E> + struct exception_trap,E...> + { + F f_; + + explicit exception_trap( F && f ) noexcept: + f_(std::forward(f)) + { + } + + decltype(std::declval()(std::declval()...)) operator()( A... a ) const + { + static_store ss; + ss.set_reset(true); + try + { + return f_(std::forward(a)...); + } + catch( captured_exception const & ) + { + throw; + } + catch( error_id const & id ) + { + throw captured_exception_impl( std::current_exception(), std::make_shared>(id,std::move(ss)), true, &print_types::print ); + } + catch(...) + { + throw captured_exception_impl( std::current_exception(), std::make_shared>(new_error(),std::move(ss)), false, &print_types::print ); + } + } + }; + + } // leaf_detail + + template + leaf_detail::exception_trap::mp_args,E...> capture_exception( F && f ) noexcept + { + using namespace leaf_detail; + return exception_trap::mp_args,E...>(std::move(f)); + } + +} } + +#endif diff --git a/include/boost/leaf/capture_result.hpp b/include/boost/leaf/capture_result.hpp new file mode 100644 index 0000000..2a0b7f5 --- /dev/null +++ b/include/boost/leaf/capture_result.hpp @@ -0,0 +1,65 @@ +#ifndef BOOST_LEAF_2416C558123711E9B9D9691F8C7F4AFC +#define BOOST_LEAF_2416C558123711E9B9D9691F8C7F4AFC + +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +namespace boost { namespace leaf { + + template + result::result( std::shared_ptr && cap ) noexcept: + cap_(std::move(cap)), + which_(leaf_detail::result_variant::cap) + { + } + + inline result::result( std::shared_ptr && cap ) noexcept: + base(std::move(cap)) + { + } + + namespace leaf_detail + { + template + struct result_trap; + + template class L, class... A, class... E> + struct result_trap,E...> + { + F f_; + + public: + + explicit result_trap( F && f ) noexcept: + f_(std::move(f)) + { + } + + decltype(std::declval()(std::declval()...)) operator()( A ... a ) const + { + static_store ss; + ss.set_reset(true); + if( auto r = f_(std::forward(a)...) ) + return r; + else + return decltype(r)( std::make_shared>(r.error(),std::move(ss)) ); + } + }; + } + + template + leaf_detail::result_trap::mp_args,E...> capture_result( F && f ) noexcept + { + using namespace leaf_detail; + return result_trap::mp_args,E...>(std::move(f)); + } + +} } + +#endif diff --git a/include/boost/leaf/common.hpp b/include/boost/leaf/common.hpp index 50b4afe..8dfe6e5 100644 --- a/include/boost/leaf/common.hpp +++ b/include/boost/leaf/common.hpp @@ -1,19 +1,19 @@ #ifndef EBA7EF10B6F311E8AAD493990D39171A #define EBA7EF10B6F311E8AAD493990D39171A -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include #include -#include #include #include #ifdef _WIN32 #include +#include #ifdef min #undef min #endif diff --git a/include/boost/leaf/detail/captured_exception.hpp b/include/boost/leaf/detail/captured_exception.hpp new file mode 100644 index 0000000..c37f0da --- /dev/null +++ b/include/boost/leaf/detail/captured_exception.hpp @@ -0,0 +1,39 @@ +#ifndef BOOST_LEAF_E32F3CCC139011E995085E318C7F4AFC +#define BOOST_LEAF_E32F3CCC139011E995085E318C7F4AFC + +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +namespace boost { namespace leaf { + + namespace leaf_detail + { + class captured_exception: + public std::exception + { + protected: + + captured_exception() noexcept + { + } + + ~captured_exception() noexcept + { + } + + public: + + [[noreturn]] virtual void unload_and_rethrow_original_exception() = 0; + virtual void print( std::ostream & ) const = 0; + }; + } + +} } + +#endif diff --git a/include/boost/leaf/detail/demangle.hpp b/include/boost/leaf/detail/demangle.hpp index d07eac6..42033a4 100644 --- a/include/boost/leaf/detail/demangle.hpp +++ b/include/boost/leaf/detail/demangle.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_E7999136FCD911E8B7F308A7642D5A5F #define BOOST_LEAF_E7999136FCD911E8B7F308A7642D5A5F -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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) // core::demangle // diff --git a/include/boost/leaf/detail/dynamic_store.hpp b/include/boost/leaf/detail/dynamic_store.hpp new file mode 100644 index 0000000..f3b7bfd --- /dev/null +++ b/include/boost/leaf/detail/dynamic_store.hpp @@ -0,0 +1,40 @@ +#ifndef BOOST_LEAF_6CCC5F56124B11E9B6C4CB8C8C7F4AFC +#define BOOST_LEAF_6CCC5F56124B11E9B6C4CB8C8C7F4AFC + +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +namespace boost { namespace leaf { + + class error_id; + + namespace leaf_detail + { + class dynamic_store + { + protected: + + dynamic_store() noexcept + { + } + + ~dynamic_store() noexcept + { + } + + public: + + virtual error_id const & error() const noexcept = 0; + virtual error_id unload() noexcept = 0; + virtual error_id unload( error_id const & ) noexcept = 0; + }; + } + +} } + +#endif diff --git a/include/boost/leaf/detail/dynamic_store_impl.hpp b/include/boost/leaf/detail/dynamic_store_impl.hpp new file mode 100644 index 0000000..86bf29f --- /dev/null +++ b/include/boost/leaf/detail/dynamic_store_impl.hpp @@ -0,0 +1,83 @@ +#ifndef BOOST_LEAF_C86E4C4ED0F011E8BB777EB8A659E189 +#define BOOST_LEAF_C86E4C4ED0F011E8BB777EB8A659E189 + +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +namespace boost { namespace leaf { + + namespace leaf_detail + { + namespace dynamic_store_internal + { + template + struct tuple_for_each + { + static void unload( error_id const & id, Tuple && tup ) noexcept + { + tuple_for_each::unload(id,std::move(tup)); + auto && opt = std::get(std::move(tup)); + if( opt.has_value() ) + (void) id.propagate(std::move(opt).value()); + } + + static void print( std::ostream & os, Tuple const & tup ) + { + typedef typename std::tuple_element::type ith_type; + tuple_for_each::print(os,tup); + auto & opt = std::get(tup); + if( opt.has_value() && diagnostic::print(os,opt.value()) ) + os << std::endl; + } + }; + + template + struct tuple_for_each<0, Tuple> + { + static void unload( error_id const &, Tuple && ) noexcept { } + static void print( std::ostream &, Tuple const & ) noexcept { } + }; + } + + template + class dynamic_store_impl: + public dynamic_store + { + error_id id_; + std::tuple...> s_; + + error_id const & error() const noexcept + { + return id_; + } + + error_id unload() noexcept + { + return unload(id_); + } + + error_id unload( error_id const & id ) noexcept + { + dynamic_store_internal::tuple_for_each::unload(id,std::move(s_)); + return id; + } + + public: + + dynamic_store_impl( error_id const & id, static_store && ss ) noexcept: + id_(id), + s_(std::make_tuple( std::get,decltype(ss.s_)>::value>(std::move(ss.s_)).extract_optional(id)... )) + { + } + }; + } // leaf_detail + +} } + +#endif diff --git a/include/boost/leaf/detail/function_traits.hpp b/include/boost/leaf/detail/function_traits.hpp index 91f9a4a..b633d01 100644 --- a/include/boost/leaf/detail/function_traits.hpp +++ b/include/boost/leaf/detail/function_traits.hpp @@ -1,14 +1,14 @@ #ifndef BOOST_LEAF_14440B9CF07011E88377FD4B0ABE8030 #define BOOST_LEAF_14440B9CF07011E88377FD4B0ABE8030 -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 { @@ -17,10 +17,6 @@ namespace boost { namespace leaf { template using void_t = void; - template struct mp_list { }; - template struct mp_rest { }; - template struct mp_rest> { using type = mp_list; }; - template struct function_traits { @@ -30,11 +26,15 @@ namespace boost { namespace leaf { struct function_traits> { private: + typedef function_traits tr; + public: + typedef typename tr::return_type return_type; static constexpr int arity = tr::arity - 1; - using mp_args = typename mp_rest::type; + + using mp_args = typename leaf_detail_mp11::mp_rest; template struct arg: @@ -49,14 +49,13 @@ namespace boost { namespace leaf { typedef R return_type; static constexpr int arity = sizeof...(A); - using mp_args = mp_list; + using mp_args = leaf_detail_mp11::mp_list; template struct arg { static_assert(I < arity, "I out of range"); typedef typename std::tuple_element>::type type; - typedef typename std::remove_cv::type>::type type_; }; }; @@ -66,7 +65,7 @@ namespace boost { namespace leaf { template struct function_traits : function_traits { }; template struct function_traits : function_traits { }; template struct function_traits : function_traits { }; - } //namespace leaf_detail + } // namespace leaf_detail } } diff --git a/include/boost/leaf/detail/handle_error.hpp b/include/boost/leaf/detail/handle_error.hpp deleted file mode 100644 index a6049b1..0000000 --- a/include/boost/leaf/detail/handle_error.hpp +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef BOOST_LEAF_F55070940BFF11E9A3EB73FBF47612F3 -#define BOOST_LEAF_F55070940BFF11E9A3EB73FBF47612F3 - -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include - -namespace boost { namespace leaf { - - namespace leaf_detail - { - template ::value> - struct unhandled_error_base_helper; - - template - struct unhandled_error_base_helper - { - static constexpr R value( leaf::error const & ) noexcept { return static_cast(-1); } - }; - - template - struct unhandled_error_base_helper - { - static constexpr R value( leaf::error const & ) noexcept { return R(); } - }; - - template - struct unhandled_error_base: unhandled_error_base_helper - { - }; - - template <> - struct unhandled_error_base - { - static constexpr bool value( leaf::error const & ) noexcept { return false; } - }; - } - - template - struct uhnandled_error: leaf_detail::unhandled_error_base - { - }; - - namespace leaf_detail - { - template ::return_type> - struct handler_wrapper_base - { - typedef typename function_traits::return_type return_type; - - F f_; - - explicit handler_wrapper_base( F && f ) noexcept: - f_(std::forward(f)) - { - } - - template - return_type operator()( E && ... e ) const - { - return f_(std::forward(e)...); - } - }; - - template - struct handler_wrapper_base - { - typedef bool return_type; - - F f_; - - explicit handler_wrapper_base( F && f ) noexcept: - f_(std::forward(f)) - { - } - - template - return_type operator()( E && ... e ) const - { - f_(std::forward(e)...); - return true; - } - }; - - template - struct handler_wrapper - { - typedef void return_type; - }; - - template - struct handler_wrapper::return_type>>: handler_wrapper_base - { - explicit handler_wrapper( F && f ) noexcept: - handler_wrapper_base(std::forward(f)) - { - } - }; - - template - struct handler_pack_return_type_helper - { - struct type; - }; - - template - struct handler_pack_return_type_helper - { - typedef R type; - }; - - template - struct handler_pack_return_type; - - template - struct handler_pack_return_type - { - typedef typename handler_wrapper::return_type return_type; - }; - - template - struct handler_pack_return_type - { - typedef typename handler_pack_return_type_helper< - typename handler_wrapper::return_type, - typename handler_wrapper::return_type>::type return_type; - }; - - template - struct handler_pack_return_type - { - typedef typename handler_pack_return_type_helper< - typename handler_wrapper::return_type, - typename handler_pack_return_type::return_type>::type return_type; - }; - } //leaf_detail - -} } - -#endif diff --git a/include/boost/leaf/detail/mp11.hpp b/include/boost/leaf/detail/mp11.hpp new file mode 100644 index 0000000..8a19f8a --- /dev/null +++ b/include/boost/leaf/detail/mp11.hpp @@ -0,0 +1,211 @@ +#ifndef BOOST_LEAF_91843B04108711E9AA4E56D98C7F4AFC +#define BOOST_LEAF_91843B04108711E9AA4E56D98C7F4AFC + +// Copyright 2015-2017 Peter Dimov. +// Copyright 2019 Emil Dotchevski. +// +// 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 + +namespace boost { namespace leaf { namespace leaf_detail_mp11 { + +// mp_list +template struct mp_list +{ +}; + +// mp_identity +template struct mp_identity +{ + using type = T; +}; + +// mp_inherit +template struct mp_inherit: T... {}; + +// mp_if, mp_if_c +namespace detail +{ + +template struct mp_if_c_impl +{ +}; + +template struct mp_if_c_impl +{ + using type = T; +}; + +template struct mp_if_c_impl +{ + using type = E; +}; + +} // namespace detail + +template using mp_if_c = typename detail::mp_if_c_impl::type; +template using mp_if = typename detail::mp_if_c_impl(C::value), T, E...>::type; + +// mp_bool +template using mp_bool = std::integral_constant; + +using mp_true = mp_bool; +using mp_false = mp_bool; + +// mp_to_bool +template using mp_to_bool = mp_bool( T::value )>; + +// mp_not +template using mp_not = mp_bool< !T::value >; + +// mp_int +template using mp_int = std::integral_constant; + +// mp_size_t +template using mp_size_t = std::integral_constant; + +// mp_set_contains +namespace detail +{ + +template struct mp_set_contains_impl; + +template class L, class... T, class V> struct mp_set_contains_impl, V> +{ + using type = mp_to_bool, mp_inherit...> > >; +}; + +} // namespace detail + +template using mp_set_contains = typename detail::mp_set_contains_impl::type; + +// mp_set_push_back +namespace detail +{ + +template struct mp_set_push_back_impl; + +template class L, class... U> struct mp_set_push_back_impl> +{ + using type = L; +}; + +template class L, class... U, class T1, class... T> struct mp_set_push_back_impl, T1, T...> +{ + using S = mp_if, T1>, L, L>; + using type = typename mp_set_push_back_impl::type; +}; + +} // namespace detail + +template using mp_set_push_back = typename detail::mp_set_push_back_impl::type; + +// mp_unique +namespace detail +{ + +template struct mp_unique_impl; + +template class L, class... T> struct mp_unique_impl> +{ + using type = mp_set_push_back, T...>; +}; + +} // namespace detail + +template using mp_unique = typename detail::mp_unique_impl::type; + +// mp_append + +namespace detail +{ + +template struct mp_append_impl; + +template<> struct mp_append_impl<> +{ + using type = mp_list<>; +}; + +template class L, class... T> struct mp_append_impl> +{ + using type = L; +}; + +template class L1, class... T1, template class L2, class... T2, class... Lr> struct mp_append_impl, L2, Lr...> +{ + using type = typename mp_append_impl, Lr...>::type; +}; + +} + +template using mp_append = typename detail::mp_append_impl::type; + +// mp_front +namespace detail +{ + +template struct mp_front_impl +{ +// An error "no type named 'type'" here means that the argument to mp_front +// is either not a list, or is an empty list +}; + +template class L, class T1, class... T> struct mp_front_impl> +{ + using type = T1; +}; + +} // namespace detail + +template using mp_front = typename detail::mp_front_impl::type; + +// mp_pop_front +namespace detail +{ + +template struct mp_pop_front_impl +{ +// An error "no type named 'type'" here means that the argument to mp_pop_front +// is either not a list, or is an empty list +}; + +template class L, class T1, class... T> struct mp_pop_front_impl> +{ + using type = L; +}; + +} // namespace detail + +template using mp_pop_front = typename detail::mp_pop_front_impl::type; + +// mp_first +template using mp_first = mp_front; + +// mp_rest +template using mp_rest = mp_pop_front; + +// mp_remove_if +namespace detail +{ + +template class P> struct mp_remove_if_impl; + +template class L, class... T, template class P> struct mp_remove_if_impl, P> +{ + template using _f = mp_if, mp_list<>, mp_list>; + using type = mp_append, _f...>; +}; + +} // namespace detail + +template class P> using mp_remove_if = typename detail::mp_remove_if_impl::type; + +} } } + +#endif diff --git a/include/boost/leaf/detail/optional.hpp b/include/boost/leaf/detail/optional.hpp index 4508c54..701245f 100644 --- a/include/boost/leaf/detail/optional.hpp +++ b/include/boost/leaf/detail/optional.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_47258FCCB6B411E8A1F35AA00C39171A #define BOOST_LEAF_47258FCCB6B411E8A1F35AA00C39171A -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 @@ -89,6 +89,15 @@ namespace boost { namespace leaf { } } + template + T & emplace( A && ... a ) + { + reset(); + (void) new(&value_) T(std::forward(a)...); + has_value_=true; + return value_; + } + T & put( T const & v ) { reset(); @@ -105,39 +114,44 @@ namespace boost { namespace leaf { return value_; } - bool has_value() const noexcept + T const * has_value() const noexcept { - return has_value_; + return has_value_ ? &value_ : 0; + } + + T * has_value() noexcept + { + return has_value_ ? &value_ : 0; } T const & value() const & noexcept { - assert(has_value()); + assert(has_value()!=0); return value_; } T & value() & noexcept { - assert(has_value()); + assert(has_value()!=0); return value_; } T const && value() const && noexcept { - assert(has_value()); + assert(has_value()!=0); return value_; } T value() && noexcept { - assert(has_value()); + assert(has_value()!=0); T tmp(std::move(value_)); reset(); return tmp; } }; - } //leaf_detail + } // leaf_detail } } diff --git a/include/boost/leaf/detail/print.hpp b/include/boost/leaf/detail/print.hpp index cf7876a..e5872c9 100644 --- a/include/boost/leaf/detail/print.hpp +++ b/include/boost/leaf/detail/print.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_3BAB50A2B87E11E89EEB30600C39171A #define BOOST_LEAF_3BAB50A2B87E11E89EEB30600C39171A -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 @@ -51,29 +51,25 @@ namespace boost { namespace leaf { namespace leaf_detail { template - struct is_printable + struct is_printable: std::false_type { - static constexpr bool value=false; }; template - struct is_printable()<(), void())> + struct is_printable()<(), void())>: std::true_type { - static constexpr bool value=true; }; //////////////////////////////////////// template - struct has_printable_member_value + struct has_printable_member_value: std::false_type { - static constexpr bool value=false; }; template - struct has_printable_member_value()<().value, void())> + struct has_printable_member_value()<().value, void())>: std::true_type { - static constexpr bool value=true; }; //////////////////////////////////////// @@ -110,7 +106,16 @@ namespace boost { namespace leaf { return true; } }; - } //leaf_detail + + template <> + struct diagnostic + { + static bool print( std::ostream & os, std::exception_ptr const & ) + { + return false; + } + }; + } // leaf_detail } } diff --git a/include/boost/leaf/detail/print_exception_info.hpp b/include/boost/leaf/detail/print_exception_info.hpp new file mode 100644 index 0000000..0abdf67 --- /dev/null +++ b/include/boost/leaf/detail/print_exception_info.hpp @@ -0,0 +1,40 @@ +#ifndef BOOST_LEAF_E823AAD6151311E9A430DDBB67511AEB +#define BOOST_LEAF_E823AAD6151311E9A430DDBB67511AEB + +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +namespace boost { namespace leaf { + + namespace leaf_detail + { + void print_exception_info( std::ostream & os, std::exception const * ex, captured_exception const * cap ) + { + if( cap ) + { + os << "Detected exception_capture of the following error types:" << std::endl; + cap->print(os); + os << "Diagnostic Information about the original exception follows" << std::endl; + } + if( ex ) + { + assert(!dynamic_cast(ex)); + os << + "Exception dynamic type: " << leaf_detail::demangle(typeid(*ex).name()) << std::endl << + "std::exception::what(): " << ex->what() << std::endl; + } + else + os << "Unknown exception type (not a std::exception)" << std::endl; + } + } // namespace leaf_detail + +} } + +#endif diff --git a/include/boost/leaf/detail/static_store.hpp b/include/boost/leaf/detail/static_store.hpp new file mode 100644 index 0000000..bdfc748 --- /dev/null +++ b/include/boost/leaf/detail/static_store.hpp @@ -0,0 +1,513 @@ +#ifndef BOOST_LEAF_AFBBD676B2FF11E8984C7976AE35F1A2 +#define BOOST_LEAF_AFBBD676B2FF11E8984C7976AE35F1A2 + +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +namespace boost { namespace leaf { + + namespace leaf_detail + { + template ::value> + struct match_type; + + template + struct match_type + { + using type = decltype(E::value); + + static type const & get( E const & e ) noexcept + { + return e.value; + } + }; + + template + struct match_type + { + using type = E; + + static type const & get( E const & e ) noexcept + { + return e; + } + }; + + template + bool check_value_pack( T const & x, T const & v ) noexcept + { + return x==v; + } + + template + bool check_value_pack( T const & x, T const & v1, VRest const & ... v_rest ) noexcept + { + return x==v1 || check_value_pack(x,v_rest...); + } + + template + bool check_exception_pack( std::exception const * ex, Ex const * ) noexcept + { + return dynamic_cast(ex)!=0; + } + + template + bool check_exception_pack( std::exception const * ex, Ex const *, ExRest const * ... ex_rest ) noexcept + { + return dynamic_cast(ex)!=0 || check_exception_pack(ex, ex_rest...); + } + } + + template ::type... V> + struct match + { + using type = typename leaf_detail::match_type::type; + type const & value; + + explicit match( E const & e ): + value(leaf_detail::match_type::get(e)) + { + } + + bool operator()() const noexcept + { + return leaf_detail::check_value_pack(value,V...); + } + }; + + template + struct catch_ + { + std::exception const & value; + + explicit catch_( std::exception const & ex ): + value(ex) + { + } + + bool operator()() const noexcept + { + return leaf_detail::check_exception_pack(&value,static_cast(0)...); + } + }; + + namespace leaf_detail + { + namespace static_store_internal + { + template + struct tuple_for_each + { + static void reset( Tuple & tup ) noexcept + { + tuple_for_each::reset(tup); + std::get(tup).reset(); + } + }; + + template + struct tuple_for_each<0, Tuple> + { + static void reset( Tuple & ) noexcept { } + }; + + //////////////////////////////////////// + + class enable_any + { + protected: + + enable_any() noexcept + { + ++tl_unexpected_enabled_counter(); + } + + ~enable_any() noexcept + { + --tl_unexpected_enabled_counter(); + } + }; + + template + class static_store_slot: + public slot + { + public: + optional extract_optional( error_id const & id ) && noexcept + { + slot const & s = *this; + if( s.has_value() && s.value().id==id ) + return optional(std::move(*this).value().e); + else + return optional(); + } + }; + + template <> + class static_store_slot: + public slot, + enable_any + { + }; + + template <> + class static_store_slot: + public slot, + enable_any + { + }; + + //////////////////////////////////////// + + template + struct type_index; + + template + struct type_index + { + static const int value = 0; + }; + + template + struct type_index + { + static const int value = 1 + type_index::value; + }; + + template + struct tuple_type_index; + + template + struct tuple_type_index> + { + static const int value = type_index::value; + }; + + //////////////////////////////////////// + + template + struct check_one_argument + { + static bool check( SlotsTuple const & tup, error_info const & ei ) noexcept + { + auto & sl = std::get,SlotsTuple>::value>(tup); + return sl.has_value() && sl.value().id==ei.error(); + } + }; + + template + struct check_one_argument + { + static bool check( SlotsTuple const &, error_info const & ) noexcept + { + return true; + } + }; + + template ::type... V> + struct check_one_argument> + { + static bool check( SlotsTuple const & tup, error_info const & ei ) noexcept + { + auto & sl = std::get,SlotsTuple>::value>(tup); + if( sl.has_value() ) + { + auto const & v = sl.value(); + return v.id==ei.error() && match(v.e)(); + } + else + return false; + } + }; + + template + struct check_one_argument> + { + static bool check( SlotsTuple const &, error_info const & ei ) noexcept + { + if( std::exception const * ex = ei.exception() ) + return catch_(*ex)(); + else + return false; + } + }; + + template + struct check_one_argument + { + static constexpr bool check( SlotsTuple const &, error_info const & ) + { + return true; + } + }; + + template + struct check_arguments; + + template + struct check_arguments + { + static bool check( SlotsTuple const & tup, error_info const & ei ) noexcept + { + return check_one_argument::check(tup,ei) && check_arguments::check(tup,ei); + } + }; + + template + struct check_arguments + { + static constexpr bool check( SlotsTuple const &, error_info const & ) noexcept + { + return true; + } + }; + + //////////////////////////////////////// + + template + struct get_one_argument + { + template + static T const & get( StaticStore const & ss, error_info const & ei ) noexcept + { + T const * arg = ss.template peek(ei.error()); + assert(arg!=0); + return *arg; + } + }; + + template + struct get_one_argument + { + template + static T const * get( StaticStore const & ss, error_info const & ei ) noexcept + { + return ss.template peek(ei.error()); + } + }; + + template ::type... V> + struct get_one_argument> + { + template + static match get( StaticStore const & ss, error_info const & ei ) noexcept + { + E const * arg = ss.template peek(ei.error()); + assert(arg!=0); + return match(*arg); + } + }; + + template + struct get_one_argument> + { + template + static catch_ get( StaticStore const &, error_info const & ei ) noexcept + { + std::exception const * ex = ei.exception(); + assert(ex!=0); + return catch_(*ex); + } + }; + + template <> + struct get_one_argument + { + template + static error_info const & get( StaticStore const &, error_info const & ei ) noexcept + { + return ei; + } + }; + + template <> + struct get_one_argument + { + template + static diagnostic_info const & get( StaticStore const & ss, error_info const & ei ) noexcept + { + diagnostic_info const * uei = ss.template peek(ei.error()); + assert(uei!=0); + uei->set_error_info(ei); + return *uei; + } + }; + + template <> + struct get_one_argument + { + template + static verbose_diagnostic_info const & get( StaticStore const & ss, error_info const & ei ) noexcept + { + verbose_diagnostic_info const * vdi = ss.template peek(ei.error()); + assert(vdi!=0); + vdi->set_error_info(ei); + return *vdi; + } + }; + + //////////////////////////////////////// + + template struct argument_matches_any_error: std::false_type { }; + template struct argument_matches_any_error: is_error_type { }; + template <> struct argument_matches_any_error: std::true_type { }; + template <> struct argument_matches_any_error: std::true_type { }; + template <> struct argument_matches_any_error: std::true_type { }; + + template + struct handler_matches_any_error: std::false_type + { + }; + + template class L, class Car, class... Cdr> + struct handler_matches_any_error> + { + constexpr static bool value = argument_matches_any_error::value && handler_matches_any_error>::value; + }; + + template class L> + struct handler_matches_any_error>: std::true_type + { + }; + } + + template + class dynamic_store_impl; + + template + class static_store + { + template + friend class dynamic_store_impl; + + static_store( static_store const & ) = delete; + static_store & operator=( static_store const & ) = delete; + + std::tuple...> s_; + bool reset_; + + template + bool check_handler( error_info const & ei, leaf_detail_mp11::mp_list ) const noexcept + { + using namespace static_store_internal; + return check_arguments::type>::type...>::check(s_,ei); + } + + template + typename function_traits::return_type call_handler( error_info const & ei, F && f, leaf_detail_mp11::mp_list ) const + { + using namespace static_store_internal; + return std::forward(f)( get_one_argument::type>::type>::get(*this,ei)... ); + } + + public: + + constexpr explicit static_store() noexcept: + reset_(false) + { + } + + ~static_store() noexcept + { + if( reset_&& !std::uncaught_exception() ) + static_store_internal::tuple_for_each::reset(s_); + } + + void set_reset( bool r ) noexcept + { + reset_ = r; + } + + template + P const * peek( error_id const & id ) const noexcept + { + auto & opt = std::get::value>(s_); + if( opt.has_value() ) + { + auto & v = opt.value(); + if( v.id==id ) + return &v.e; + } + return 0; + } + + template + typename function_traits::return_type handle_error( error_info const & ei, F && f ) const + { + using namespace static_store_internal; + static_assert( handler_matches_any_error::mp_args>::value, "The last handler passed to handle_all must match any error." ); + return call_handler( ei, std::forward(f), typename function_traits::mp_args{ } ); + } + + template + typename function_traits::return_type handle_error( error_info const & ei, CarF && car_f, CdarF && cdar_f, CddrF && ... cddr_f ) const + { + using namespace static_store_internal; + if( handler_matches_any_error::mp_args>::value || check_handler( ei, typename function_traits::mp_args{ } ) ) + return call_handler( ei, std::forward(car_f), typename function_traits::mp_args{ } ); + else + return handle_error( ei, std::forward(cdar_f), std::forward(cddr_f)...); + } + }; + + // Static store deduction + + template struct translate_expect_deduction { typedef T type; }; + template struct translate_expect_deduction { typedef T type; }; + template struct translate_expect_deduction { typedef T type; }; + template struct translate_expect_deduction { typedef T type; }; + template ::type... V> struct translate_expect_deduction> { typedef E type; }; + template struct translate_expect_deduction> { typedef void type; }; + + template + struct translate_list_impl; + + template class L, class... T> + struct translate_list_impl> + { + using type = leaf_detail_mp11::mp_list::type...>; + }; + + template using translate_list = typename translate_list_impl::type; + + template struct does_not_participate_in_expect_deduction: std::false_type { }; + template <> struct does_not_participate_in_expect_deduction: std::true_type { }; + template <> struct does_not_participate_in_expect_deduction: std::true_type { }; + + template + struct handler_args_set + { + using type = + leaf_detail_mp11::mp_remove_if< + leaf_detail_mp11::mp_unique< + translate_list< + leaf_detail_mp11::mp_append< + typename function_traits::mp_args... + > + > + >, + does_not_participate_in_expect_deduction + >; + }; + + template + struct deduce_static_store; + + template class L, class... T> + struct deduce_static_store> + { + typedef static_store type; + }; + } // leaf_detail + +} } + +#endif diff --git a/include/boost/leaf/diagnostic_output_current_exception.hpp b/include/boost/leaf/diagnostic_output_current_exception.hpp deleted file mode 100644 index a863c7e..0000000 --- a/include/boost/leaf/diagnostic_output_current_exception.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef BOOST_LEAF_17228D24F83C11E8AAC53F8F652D5A5F -#define BOOST_LEAF_17228D24F83C11E8AAC53F8F652D5A5F - -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include - -namespace boost { namespace leaf { - - template - void diagnostic_output_current_exception( std::ostream & os, expect const & exp ) - { - os << "Current Exception Diagnostic Information:" << std::endl; - - try - { - throw; - } - catch( std::exception const & ex ) - { - os << - "Exception dynamic type: " << leaf_detail::demangle(typeid(ex).name()) << std::endl << - "std::exception::what(): " << ex.what() << std::endl; - } - catch( ... ) - { - os << "Unknown exception type (not a std::exception)" << std::endl; - } - - try - { - throw; - } - catch( leaf_detail::captured_exception const & e ) - { - diagnostic_output_(os,e); - } - catch( error const & e ) - { - diagnostic_output(os,exp,e); - } - catch( ... ) - { - diagnostic_output(os,exp); - } - } - -} } - -#endif diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 406e102..202e511 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -1,11 +1,11 @@ #ifndef BOOST_LEAF_BA049396D0D411E8B45DF7D4A759E189 #define BOOST_LEAF_BA049396D0D411E8B45DF7D4A759E189 -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 @@ -22,84 +22,21 @@ namespace boost { namespace system { class error_code; } } namespace boost { namespace leaf { - struct e_source_location + class error_id; + + error_id next_error() noexcept; + error_id last_error() noexcept; + + class error_id { - char const * const file; - int const line; - char const * const function; - - friend std::ostream & operator<<( std::ostream & os, e_source_location const & x ) - { - return os << leaf::type() << ": " << x.file << '(' << x.line << ") in function " << x.function; - } - }; - - struct e_unexpected - { - char const * (*first_type)() noexcept; - int count; - - friend std::ostream & operator<<( std::ostream & os, e_unexpected const & x ) - { - assert(x.first_type!=0); - assert(x.count>0); - if( x.count==1 ) - os << "Detected 1 attempt to communicate an unexpected error object of type "; - else - os << "Detected " << x.count << " attempts to communicate unexpected error objects, the first one of type "; - return os << x.first_type(); - } - }; - - struct e_unexpected_diagnostic_output - { - std::string value; - std::set already; - friend std::ostream & operator<<( std::ostream & os, e_unexpected_diagnostic_output const & x ) noexcept { return os; } - }; - - namespace leaf_detail - { - template - struct has_data_member_value - { - static constexpr bool value=false; - }; - - template - struct has_data_member_value().value, void())> - { - static constexpr bool value=std::is_member_object_pointer::value; - }; - } - - template - struct is_error_type - { - static constexpr bool value = leaf_detail::has_data_member_value::value || std::is_base_of::value; - }; - - template <> struct is_error_type: std::true_type { }; - template <> struct is_error_type: std::true_type { }; - template <> struct is_error_type: std::true_type { }; - template <> struct is_error_type: std::true_type { }; - template <> struct is_error_type: std::true_type { }; - - //////////////////////////////////////// - - class error; - - error next_error_value() noexcept; - error last_error_value() noexcept; - - class error - { - friend error leaf::next_error_value() noexcept; - friend error leaf::last_error_value() noexcept; + template + friend error_id new_error( E && ... ) noexcept; + friend error_id leaf::next_error() noexcept; + friend error_id leaf::last_error() noexcept; unsigned id_; - explicit error( unsigned id ) noexcept: + explicit error_id( unsigned id ) noexcept: id_(id) { } @@ -152,51 +89,342 @@ namespace boost { namespace leaf { public: - error() noexcept: - id_(id_factory::tl_instance().get()) - { - } - - template - explicit error( E && ... e ) noexcept: - id_(id_factory::tl_instance().get()) - { - propagate(std::forward(e)...); - } - - friend bool operator==( error const & e1, error const & e2 ) noexcept + friend bool operator==( error_id const & e1, error_id const & e2 ) noexcept { return e1.id_==e2.id_; } - friend bool operator!=( error const & e1, error const & e2 ) noexcept + friend bool operator!=( error_id const & e1, error_id const & e2 ) noexcept { return e1.id_!=e2.id_; } - friend std::ostream & operator<<( std::ostream & os, error const & e ) - { - os << e.id_; - return os; - } - - error propagate() const noexcept + error_id propagate() const noexcept { return *this; } + friend std::ostream & operator<<( std::ostream & os, error_id const & id ) + { + os << id.id_; + return os; + } + template - error propagate( E && ... ) const noexcept; + error_id propagate( E && ... ) const noexcept; }; - inline error next_error_value() noexcept + template + error_id new_error( E && ... e ) noexcept { - return error(error::id_factory::tl_instance().next_id()); + return error_id(error_id::id_factory::tl_instance().get()).propagate(std::forward(e)...); } - inline error last_error_value() noexcept + inline error_id next_error() noexcept { - return error(error::id_factory::tl_instance().last_id()); + return error_id(error_id::id_factory::tl_instance().next_id()); + } + + inline error_id last_error() noexcept + { + return error_id(error_id::id_factory::tl_instance().last_id()); + } + + //////////////////////////////////////// + + namespace leaf_detail + { + class slot_base + { + slot_base( slot_base const & ) = delete; + slot_base & operator=( slot_base const & ) = delete; + + virtual bool slot_print( std::ostream &, error_id const & ) const = 0; + + public: + + static void print( std::ostream & os, error_id const & id ) + { + for( slot_base const * p = first(); p; p=p->next_ ) + if( p->slot_print(os,id) ) + os << std::endl; + } + + protected: + + static slot_base const * & first() noexcept + { + static thread_local slot_base const * p = 0; + return p; + } + + slot_base const * const next_; + + slot_base() noexcept: + next_(first()) + { + first() = this; + } + + ~slot_base() noexcept + { + slot_base const * & p = first(); + assert(p==this); + p = next_; + } + }; + } + + //////////////////////////////////////// + + namespace leaf_detail { class captured_exception; } + + class error_info + { + error_info( error_info const & ) = delete; + error_info & operator=( error_info const & ) = delete; + + error_id const id_; + std::exception const * const ex_; + leaf_detail::captured_exception const * const cap_; + void (* const print_ex_)( std::ostream &, std::exception const *, leaf_detail::captured_exception const * ); + + public: + + explicit error_info( error_id const & id ) noexcept: + id_(id), + ex_(0), + cap_(0), + print_ex_(0) + { + } + + error_info( error_id const & id, std::exception const * ex, void (*print_ex)(std::ostream &, std::exception const *, leaf_detail::captured_exception const *) ) noexcept: + id_(id), + ex_(ex), + cap_(0), + print_ex_(print_ex) + { + assert(print_ex_!=0); + } + + error_info( error_id const & id, std::exception const * ex, leaf_detail::captured_exception const * cap, void (*print_ex)(std::ostream &, std::exception const *, leaf_detail::captured_exception const *) ) noexcept: + id_(id), + ex_(ex), + cap_(cap), + print_ex_(print_ex) + { + assert(print_ex_!=0); + } + + error_id const & error() const noexcept + { + return id_; + } + + bool exception_caught() const noexcept + { + return print_ex_!=0; + } + + std::exception const * exception() const noexcept + { + assert(exception_caught()); + return ex_; + } + + friend std::ostream & operator<<( std::ostream & os, error_info const & x ) + { + os << "leaf::error_id: " << x.id_ << std::endl; + if( x.print_ex_ ) + x.print_ex_(os,x.ex_,x.cap_); + leaf_detail::slot_base::print(os,x.id_); + return os; + } + }; + + //////////////////////////////////////// + + namespace leaf_detail + { + template + struct has_data_member_value + { + static constexpr bool value=false; + }; + + template + struct has_data_member_value().value, void())> + { + static constexpr bool value=std::is_member_object_pointer::value; + }; + + template + struct is_error_type_default + { + static constexpr bool value = has_data_member_value::value || std::is_base_of::value; + }; + + template <> struct is_error_type_default: std::true_type { }; + template <> struct is_error_type_default: std::true_type { }; + template <> struct is_error_type_default: std::true_type { }; + } + + template + struct is_error_type: leaf_detail::is_error_type_default + { + }; + + //////////////////////////////////////// + + struct e_source_location + { + char const * const file; + int const line; + char const * const function; + + friend std::ostream & operator<<( std::ostream & os, e_source_location const & x ) + { + return os << leaf::type() << ": " << x.file << '(' << x.line << ") in function " << x.function; + } + }; + + namespace leaf_detail + { + template <> struct is_error_type_default: std::true_type { }; + } + + //////////////////////////////////////// + + namespace leaf_detail + { + class monitor_base + { + monitor_base( monitor_base const & ) = delete; + monitor_base & operator=( monitor_base const & ) = delete; + + mutable error_info const * ei_; + + protected: + + monitor_base() noexcept: + ei_(0) + { + } + + monitor_base( monitor_base && x ) noexcept: + ei_(0) + { + x.ei_ = 0; + } + + void set_error_info( error_info const & ei ) const noexcept + { + ei_ = &ei; + } + + error_info const & get_error_info() const noexcept + { + assert(ei_!=0); + return *ei_; + } + }; + } + + class diagnostic_info: leaf_detail::monitor_base + { + public: + + char const * (*first_type)(); + int count; + + explicit diagnostic_info( char const * (*first_type)() ) noexcept: + first_type(first_type), + count(1) + { + } + + using monitor_base::set_error_info; + + friend std::ostream & operator<<( std::ostream & os, diagnostic_info const & x ) + { + assert(x.first_type!=0); + assert(x.count>0); + os << x.get_error_info() << "diagnostic_info: Detected "; + if( x.count==1 ) + os << "1 attempt to communicate an E-object"; + else + os << x.count << " attempts to communicate unexpected E-objects, the first one"; + return os << " of type " << x.first_type() << std::endl; + } + }; + + namespace leaf_detail + { + template <> struct is_error_type_default: std::true_type { }; + + template <> + struct diagnostic + { + static bool print( std::ostream & os, diagnostic_info const & ) noexcept + { + return false; + } + }; + } + + class verbose_diagnostic_info: leaf_detail::monitor_base + { + std::string s_; + std::set already_; + + public: + + verbose_diagnostic_info() noexcept + { + } + + using monitor_base::set_error_info; + + void reset() noexcept + { + s_.clear(); + already_.clear(); + } + + template + void add( E const & e ) + { + std::stringstream s; + if( leaf_detail::diagnostic::print(s,e) && already_.insert(&type).second ) + { + s << std::endl; + s_ += s.str(); + } + } + + friend std::ostream & operator<<( std::ostream & os, verbose_diagnostic_info const & x ) + { + os << + x.get_error_info() << + "verbose_diagnostic_info:" << std::endl << + x.s_; + return os; + } + }; + + namespace leaf_detail + { + template <> struct is_error_type_default: std::true_type { }; + + template <> + struct diagnostic + { + static bool print( std::ostream & os, verbose_diagnostic_info const & ) noexcept + { + return false; + } + }; } //////////////////////////////////////// @@ -204,10 +432,27 @@ namespace boost { namespace leaf { namespace leaf_detail { template - struct error_info + struct id_e_pair { - E v; - error e; + error_id id; + E e; + + explicit id_e_pair( error_id const & id ) noexcept: + id(id) + { + } + + id_e_pair( error_id const & id, E const & e ): + id(id), + e(e) + { + } + + id_e_pair( error_id const & id, E && e ) noexcept: + id(id), + e(std::forward(e)) + { + } }; inline int & tl_unexpected_enabled_counter() noexcept @@ -215,24 +460,37 @@ namespace boost { namespace leaf { static thread_local int c; return c; } + } + //////////////////////////////////////// + + namespace leaf_detail + { template class slot: - optional> + slot_base, + optional> { slot( slot const & ) = delete; slot & operator=( slot const & ) = delete; - typedef optional> base; + typedef optional> base; slot * prev_; static_assert(is_error_type::value,"Not an error type"); + + bool slot_print( std::ostream &, error_id const & ) const; + protected: + slot() noexcept; ~slot() noexcept; + public: + using base::put; using base::has_value; using base::value; using base::reset; + using base::emplace; }; template @@ -243,69 +501,61 @@ namespace boost { namespace leaf { } template - void put_unexpected( error_info const & ev ) noexcept + void put_unexpected( id_e_pair const & id_e ) noexcept { - if( slot * p = tl_slot_ptr() ) + if( slot * p = tl_slot_ptr() ) { if( p->has_value() ) { - auto & p_ev = p->value(); - if( p_ev.e==ev.e ) + auto & p_id_e = p->value(); + if( p_id_e.id==id_e.id ) { - ++p_ev.v.count; + ++p_id_e.e.count; return; } } - (void) p->put( error_info{e_unexpected{&type,1},ev.e} ); + (void) p->put( id_e_pair(id_e.id,diagnostic_info(&type)) ); } } template - void put_unexpected_diagnostic_output( error_info const & ev ) noexcept + void put_verbose_diagnostic_info( id_e_pair const & id_e ) noexcept { - if( slot * p = tl_slot_ptr() ) + if( slot * sl = tl_slot_ptr() ) { - std::stringstream s; - if( !diagnostic::print(s,ev.v) ) - return; - if( p->has_value() ) + if( auto * pv = sl->has_value() ) { - auto & p_ev = p->value(); - if( p_ev.e==ev.e ) + if( pv->id!=id_e.id ) { - if( p_ev.v.already.insert(&type).second ) - { - std::string & value = p_ev.v.value; - value += '\n'; - value += s.str(); - value += " {unexpected}"; - } - return; + pv->id = id_e.id; + pv->e.reset(); } + pv->e.add(id_e.e); } - (void) p->put( error_info{e_unexpected_diagnostic_output{s.str()+" {unexpected}"},ev.e} ); + else + sl->emplace(id_e.id).e.add(id_e.e); } } template - void no_expect_slot( error_info const & ev ) noexcept + void no_expect_slot( id_e_pair const & id_e ) noexcept { - put_unexpected(ev); - put_unexpected_diagnostic_output(ev); + put_unexpected(id_e); + put_verbose_diagnostic_info(id_e); } template - int put_slot( E && v, error const & e ) noexcept + int put_slot( E && e, error_id const & id ) noexcept { using T = typename std::remove_cv::type>::type; if( slot * p = tl_slot_ptr() ) - (void) p->put( error_info{std::forward(v),e} ); + (void) p->put( id_e_pair(id,std::forward(e)) ); else { int c = tl_unexpected_enabled_counter(); assert(c>=0); if( c ) - no_expect_slot( error_info{std::forward(v),e} ); + no_expect_slot( id_e_pair(id,std::forward(e)) ); } return 0; } @@ -323,7 +573,7 @@ namespace boost { namespace leaf { { if( prev_ ) { - optional> & p = *prev_; + optional> & p = *prev_; p = std::move(*this); } else @@ -336,28 +586,36 @@ namespace boost { namespace leaf { tl_slot_ptr() = prev_; } + template + bool slot::slot_print( std::ostream & os, error_id const & id ) const + { + if( tl_slot_ptr()==this ) + if( id_e_pair const * id_e = has_value() ) + if( id_e->id==id ) + return diagnostice)>::print(os,id_e->e); + return false; + } + template - error make_error( char const * file, int line, char const * function, E && ... e ) + error_id make_error( char const * file, int line, char const * function, E && ... e ) noexcept { assert(file&&*file); assert(line>0); assert(function&&*function); - e_source_location sl { file, line, function }; //Temp object MSVC workaround - return error( std::move(sl), std::forward(e)... ); + e_source_location sl { file, line, function }; // Temp object MSVC workaround + return error_id( std::move(sl), std::forward(e)... ); } - inline void diagnostic_output_prefix( std::ostream & os, leaf::error const * e ) + enum class result_variant { - if( e ) - os << "leaf::error serial number: " << *e << std::endl; - if( leaf_detail::slot const * unx = leaf_detail::tl_slot_ptr() ) - if( unx->has_value() ) - os << unx->value().v.value << std::endl; - } - } //leaf_detail + value, + err, + cap + }; + } // leaf_detail template - error error::propagate( E && ... e ) const noexcept + error_id error_id::propagate( E && ... e ) const noexcept { auto _ = { leaf_detail::put_slot(std::forward(e),*this)... }; (void) _; diff --git a/include/boost/leaf/error_capture.hpp b/include/boost/leaf/error_capture.hpp deleted file mode 100644 index 2483f5b..0000000 --- a/include/boost/leaf/error_capture.hpp +++ /dev/null @@ -1,315 +0,0 @@ -#ifndef BOOST_LEAF_C86E4C4ED0F011E8BB777EB8A659E189 -#define BOOST_LEAF_C86E4C4ED0F011E8BB777EB8A659E189 - -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include - -namespace boost { namespace leaf { - - class error_capture; - - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( error_capture const &, F && ... ) noexcept; - - template - P const * peek( error_capture const & ) noexcept; - - void diagnostic_output( std::ostream &, error_capture const & ); - - namespace leaf_detail - { - template - struct all_available; - - template - struct all_available - { - static bool check( error_capture const & cap ) noexcept - { - return peek(cap) && all_available::check(cap); - } - }; - - template <> - struct all_available<> - { - constexpr static bool check( error_capture const & ) noexcept { return true; } - }; - - //////////////////////////////////////// - - template - struct tuple_for_each_capture - { - static void const * dynamic_bind( Tuple const & tup, char const * (*type_id)() ) noexcept - { - assert(type_id!=0); - typedef typename std::tuple_element::type ith_type; - if( &type == type_id ) - return &std::get(tup); - return tuple_for_each_capture::dynamic_bind(tup,type_id); - } - - static void print( std::ostream & os, Tuple const & tup ) - { - typedef typename std::tuple_element::type ith_type; - tuple_for_each_capture::print(os,tup); - auto & opt = std::get(tup); - if( opt.has_value() && diagnostic::print(os,opt.value()) ) - os << std::endl; - } - - static void unload( error const & e, Tuple && tup ) noexcept - { - tuple_for_each_capture::unload(e,std::move(tup)); - auto && opt = std::get(std::move(tup)); - if( opt.has_value() ) - (void) e.propagate(std::move(opt).value()); - } - }; - - template - struct tuple_for_each_capture<0, Tuple> - { - static void const * dynamic_bind( Tuple const &, char const * (*)() ) noexcept { return 0; } - static void print( std::ostream &, Tuple const & ) noexcept { } - static void unload( error const &, Tuple && ) noexcept { } - }; - - } //leaf_detail - - //////////////////////////////////////// - - class error_capture - { - template - friend typename leaf_detail::handler_pack_return_type::return_type handle_error( error_capture const &, F && ... ) noexcept; - - template - friend P const * leaf::peek( error_capture const & ) noexcept; - - friend void leaf::diagnostic_output( std::ostream &, error_capture const & ); - - //////////////////////////////////////// - - class dynamic_store - { - mutable std::atomic refcount_; - - virtual void const * bind_( char const * (*)() ) const noexcept = 0; - - protected: - - dynamic_store() noexcept: - refcount_(0) - { - } - - public: - - virtual ~dynamic_store() noexcept - { - } - - void addref() const noexcept - { - ++refcount_; - } - - void release() const noexcept - { - if( !--refcount_ ) - delete this; - } - - template - leaf_detail::optional const * bind() const noexcept - { - if( void const * p = bind_(&type) ) - return static_cast const *>(p); - else - return 0; - } - - virtual void diagnostic_output_( std::ostream & ) const = 0; - virtual void unload_( error const & ) noexcept = 0; - }; - - //////////////////////////////////////// - - template - class dynamic_store_impl: - public dynamic_store - { - std::tuple...> s_; - public: - - explicit dynamic_store_impl( std::tuple...> && s ) noexcept: - s_(std::move(s)) - { - } - - void const * bind_( char const * (*type_id)() ) const noexcept - { - using namespace leaf_detail; - assert(type_id!=0); - return tuple_for_each_capture...>>::dynamic_bind(s_,type_id); - } - - void diagnostic_output_( std::ostream & os ) const - { - leaf_detail::diagnostic_output_prefix(os,0); - leaf_detail::tuple_for_each_capture::print(os,s_); - } - - void unload_( error const & e ) noexcept - { - leaf_detail::tuple_for_each_capture::unload(e,std::move(s_)); - } - }; - - //////////////////////////////////////// - - void free() noexcept - { - if( ds_ ) - { - ds_->release(); - ds_=0; - } - } - - template - std::pair::return_type> check_handler_( F && f, leaf_detail::mp_list ) const - { - using namespace leaf_detail; - typedef typename handler_wrapper::return_type return_type; - if( leaf_detail::all_available::type>::type...>::check(*this) ) - return std::make_pair(true, handler_wrapper(std::forward(f))( *leaf::peek::type>::type>(*this)... )); - else - return std::make_pair(false, return_type(uhnandled_error::value(e_))); - } - - template - std::pair::return_type> find_handler_( F && f ) const - { - return check_handler_( std::forward(f), typename leaf_detail::function_traits::mp_args{ } ); - } - - template - std::pair::return_type> find_handler_( CarF && car_f, CdrF && ... cdr_f ) const - { - using namespace leaf_detail; - auto r = check_handler_( std::forward(car_f), typename leaf_detail::function_traits::mp_args{ } ); - return r.first ? r : find_handler_(std::forward(cdr_f)...); - } - - dynamic_store * ds_; - error e_; - - protected: - - void set_error( error const & e ) - { - e_ = e; - } - - public: - - error_capture() noexcept: - ds_(0) - { - } - - template - error_capture( error const & e, std::tuple...> && s ) noexcept: - ds_(new dynamic_store_impl(std::move(s))), - e_(e) - { - ds_->addref(); - } - - ~error_capture() noexcept - { - free(); - } - - error_capture( error_capture const & x ) noexcept: - ds_(x.ds_), - e_(x.e_) - { - if( ds_ ) - ds_->addref(); - } - - error_capture( error_capture && x ) noexcept: - ds_(x.ds_), - e_(std::move(x.e_)) - { - x.ds_ = 0; - } - - error_capture & operator=( error_capture const & x ) noexcept - { - ds_ = x.ds_; - ds_->addref(); - e_ = x.e_; - return *this; - } - - error_capture & operator=( error_capture && x ) noexcept - { - ds_ = x.ds_; - x.ds_ = 0; - e_ = x.e_; - return *this; - } - - explicit operator bool() const noexcept - { - return ds_!=0; - } - - error unload() noexcept - { - if( ds_ ) - { - ds_->unload_(e_); - free(); - } - return e_; - } - }; - - //////////////////////////////////////// - - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( error_capture const & ec, F && ... f ) noexcept - { - return ec.find_handler_( std::forward(f)... ).second; - } - - template - P const * peek( error_capture const & e ) noexcept - { - if( e ) - if( auto * opt = e.ds_->bind

() ) - if( opt->has_value() ) - return &opt->value(); - return 0; - } - - inline void diagnostic_output( std::ostream & os, error_capture const & e ) - { - if( e ) - e.ds_->diagnostic_output_(os); - } - -} } - -#endif diff --git a/include/boost/leaf/exception.hpp b/include/boost/leaf/exception.hpp deleted file mode 100644 index e6c9f29..0000000 --- a/include/boost/leaf/exception.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef BOOST_LEAF_87F274C4D4BA11E89928D55AC82C3C47 -#define BOOST_LEAF_87F274C4D4BA11E89928D55AC82C3C47 - -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include - -namespace boost { namespace leaf { - - inline error get_error( std::exception const & ex ) noexcept - { - if( auto e = dynamic_cast(&ex) ) - return *e; - else - return next_error_value(); - } - - template - P const * peek( expect const & exp, std::exception const & ex ) noexcept - { - return peek

(exp,get_error(ex)); - } - - template - void handle_exception( expect & exp, std::exception const & ex, F && ... f ) - { - if( handle_error(exp,get_error(ex),f...) ) - (void) error(); - else - throw; - } - - template - void diagnostic_output( std::ostream & os, expect const & exp, std::exception const & ex ) - { - os << - "Exception dynamic type: " << leaf_detail::demangle(typeid(ex).name()) << std::endl << - "std::exception::what(): " << ex.what() << std::endl; - diagnostic_output(os,exp,get_error(ex)); - } - -} } - -#endif diff --git a/include/boost/leaf/exception_capture.hpp b/include/boost/leaf/exception_capture.hpp deleted file mode 100644 index 2098d35..0000000 --- a/include/boost/leaf/exception_capture.hpp +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef BOOST_LEAF_BC24FB98B2DE11E884419CF5AD35F1A2 -#define BOOST_LEAF_BC24FB98B2DE11E884419CF5AD35F1A2 - -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include - -namespace boost { namespace leaf { - - namespace leaf_detail - { - class captured_exception: - public std::exception, - error_capture - { - std::exception_ptr ex_; - bool has_error_; - - public: - - captured_exception( std::exception_ptr && ex, error_capture && cap, bool has_error ) noexcept: - error_capture(std::move(cap)), - ex_(std::move(ex)), - has_error_(has_error) - { - assert(ex_); - } - - [[noreturn]] - void rethrow_original_exception() - { - if( !has_error_ ) - { - set_error(next_error_value()); - has_error_ = true; - } - unload(); - std::rethrow_exception(ex_); - } - - friend void diagnostic_output_( std::ostream & os, captured_exception const & ce ) - { - diagnostic_output(os,static_cast(ce)); - } - }; - - //////////////////////////////////////// - - template - class exception_trap - { - F f_; - - public: - - explicit exception_trap( F && f ) noexcept: - f_(std::move(f)) - { - } - - template - decltype(std::declval()(std::declval()...)) operator()( A && ... a ) - { - expect exp; - try - { - return f_(std::forward(a)...); - } - catch( error const & e ) - { - throw captured_exception(std::current_exception(),capture(exp,e),true); - } - catch(...) - { - throw captured_exception(std::current_exception(),capture(exp,error()),false); - } - } - }; - } //leaf_detail - - template - leaf_detail::exception_trap capture_exception( F && f ) noexcept - { - return leaf_detail::exception_trap(std::move(f)); - } - - template - decltype(std::declval().get()) get( Future && f ) - { - try - { - return std::forward(f).get(); - } - catch( leaf_detail::captured_exception & ex ) - { - ex.rethrow_original_exception(); - } - } - -} } - -#endif diff --git a/include/boost/leaf/exception_to_result.hpp b/include/boost/leaf/exception_to_result.hpp new file mode 100644 index 0000000..86ba897 --- /dev/null +++ b/include/boost/leaf/exception_to_result.hpp @@ -0,0 +1,54 @@ +#ifndef BOOST_LEAF_017BF91412EB11E9926CDCED8B7F4AFC +#define BOOST_LEAF_017BF91412EB11E9926CDCED8B7F4AFC + +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +namespace boost { namespace leaf { + + namespace leaf_detail + { + template + leaf::result::return_type> catch_exceptions_helper( F && f, leaf_detail_mp11::mp_list<> ) + { + return std::forward(f)(); + } + + template + leaf::result::return_type> catch_exceptions_helper( F && f, leaf_detail_mp11::mp_list ) + { + try + { + return catch_exceptions_helper(std::forward(f),leaf_detail_mp11::mp_list{ }); + } + catch( Ex1 const & ex1 ) + { + return leaf::new_error(ex1); + } + } + } + + template + leaf::result::return_type> exception_to_result( F && f ) noexcept + { + try + { + return leaf_detail::catch_exceptions_helper(std::forward(f), leaf_detail_mp11::mp_list()); + } + catch(...) + { + return leaf::new_error(std::current_exception()); + } + }; + +} } + +#endif diff --git a/include/boost/leaf/expect.hpp b/include/boost/leaf/expect.hpp deleted file mode 100644 index 9b2b7fa..0000000 --- a/include/boost/leaf/expect.hpp +++ /dev/null @@ -1,286 +0,0 @@ -#ifndef BOOST_LEAF_AFBBD676B2FF11E8984C7976AE35F1A2 -#define BOOST_LEAF_AFBBD676B2FF11E8984C7976AE35F1A2 - -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include - -namespace boost { namespace leaf { - - class error_capture; - - namespace leaf_detail - { - template - class expect_slot: - public slot - { - }; - - class expect_slot_enable_unexpected - { - protected: - expect_slot_enable_unexpected() noexcept - { - ++tl_unexpected_enabled_counter(); - } - - ~expect_slot_enable_unexpected() noexcept - { - --tl_unexpected_enabled_counter(); - } - }; - - template <> - class expect_slot: - public slot, - expect_slot_enable_unexpected - { - }; - - template <> - class expect_slot: - public slot, - expect_slot_enable_unexpected - { - }; - - //////////////////////////////////////// - - template - struct type_index; - - template - struct type_index - { - static const int value = 0; - }; - - template - struct type_index - { - static const int value = 1 + type_index::value; - }; - - template - struct tuple_type_index; - - template - struct tuple_type_index> - { - static const int value = type_index::value; - }; - - //////////////////////////////////////// - - template - struct slots_subset; - - template - struct slots_subset - { - static bool have_values( SlotsTuple const & tup, error const & e ) noexcept - { - auto & sl = std::get::value>(tup); - return sl.has_value() && sl.value().e==e && slots_subset::have_values(tup,e); - } - }; - - template - struct slots_subset - { - static constexpr bool have_values( SlotsTuple const &, error const & ) noexcept { return true; } - }; - - //////////////////////////////////////// - - template - struct tuple_for_each_expect - { - static void print( std::ostream & os, Tuple const & tup ) - { - tuple_for_each_expect::print(os,tup); - auto & opt = std::get(tup); - if( opt.has_value() ) - { - auto & x = opt.value(); - if( diagnostic::print(os,x.v) ) - os << " {" << x.e << '}' << std::endl; - } - } - - static void print( std::ostream & os, Tuple const & tup, error const & e ) - { - tuple_for_each_expect::print(os,tup,e); - auto & opt = std::get(tup); - if( opt.has_value() ) - { - auto & x = opt.value(); - if( x.e==e && diagnostic::print(os,x.v) ) - os << std::endl; - } - } - - static void clear( Tuple & tup ) noexcept - { - tuple_for_each_expect::clear(tup); - std::get(tup).reset(); - } - }; - - template - struct tuple_for_each_expect<0, Tuple> - { - static void print( std::ostream &, Tuple const & ) noexcept { } - static void print( std::ostream &, Tuple const &, error const & ) noexcept { } - static void clear( Tuple & ) noexcept { } - }; - - //////////////////////////////////////// - - template - optional convert_optional( expect_slot && x, error const & e ) noexcept - { - if( x.has_value() && x.value().e==e ) - return optional(std::move(x).value().v); - else - return optional(); - } - - template - struct dependent_type - { - typedef leaf::error_capture error_capture; - }; - } //leaf_detail - - template - class expect; - - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( expect const &, error const &, F && ... ) noexcept; - - template - P const * peek( expect const &, error const & ) noexcept; - - template - void diagnostic_output( std::ostream &, expect const & ); - - template - void diagnostic_output( std::ostream &, expect const &, error const & ); - - template - typename leaf_detail::dependent_type>::error_capture capture( expect &, error const & ); - - template - class expect - { - friend class error; - - template - friend typename leaf_detail::handler_pack_return_type::return_type leaf::handle_error( expect const &, error const &, F && ... ) noexcept; - - template - friend P const * leaf::peek( expect const &, error const & ) noexcept; - - template - friend void leaf::diagnostic_output( std::ostream &, expect const & ); - - template - friend void leaf::diagnostic_output( std::ostream &, expect const &, error const & ); - - template - friend typename leaf_detail::dependent_type>::error_capture leaf::capture( expect &, error const & ); - - expect( expect const & ) = delete; - expect & operator=( expect const & ) = delete; - - std::tuple...> s_; - - template - std::pair::return_type> check_handler_( error const & e, F && f, leaf_detail::mp_list ) const - { - using namespace leaf_detail; - typedef typename handler_wrapper::return_type return_type; - if( slots_subset::type>::type>...>::have_values(s_,e) ) - return std::make_pair(true, handler_wrapper(std::forward(f))( *leaf::peek::type>::type>(*this,e)... )); - else - return std::make_pair(false, uhnandled_error::value(e)); - } - - template - std::pair::return_type> find_handler_( error const & e, F && f ) const - { - return check_handler_( e, std::forward(f), typename leaf_detail::function_traits::mp_args{ } ); - } - - template - std::pair::return_type> find_handler_( error const & e, CarF && car_f, CdrF && ... cdr_f ) const - { - using namespace leaf_detail; - auto r = check_handler_( e, std::forward(car_f), typename leaf_detail::function_traits::mp_args{ } ); - return r.first ? r : find_handler_(e,std::forward(cdr_f)...); - } - - public: - - constexpr expect() noexcept - { - } - - }; - - //////////////////////////////////////// - - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( expect const & exp, error const & e, F && ... f ) noexcept - { - return exp.find_handler_( e, std::forward(f)... ).second; - } - - template - P const * peek( expect const & exp, error const & e ) noexcept - { - auto & opt = std::get::value>(exp.s_); - if( opt.has_value() ) - { - auto & x = opt.value(); - if( x.e==e ) - return &x.v; - } - return 0; - } - - template - void diagnostic_output( std::ostream & os, expect const & exp ) - { - leaf_detail::diagnostic_output_prefix(os,0); - leaf_detail::tuple_for_each_expect::print(os,exp.s_); - } - - template - void diagnostic_output( std::ostream & os, expect const & exp, error const & e ) - { - leaf_detail::diagnostic_output_prefix(os,&e); - leaf_detail::tuple_for_each_expect::print(os,exp.s_,e); - } - - template - typename leaf_detail::dependent_type>::error_capture capture( expect & exp, error const & e ) - { - using namespace leaf_detail; - typename leaf_detail::dependent_type>::error_capture cap( - e, - std::make_tuple( - convert_optional( - std::move(std::get,decltype(exp.s_)>::value>(exp.s_)),e)...)); - return cap; - } - -} } - -#endif diff --git a/include/boost/leaf/handle.hpp b/include/boost/leaf/handle.hpp new file mode 100644 index 0000000..6722d70 --- /dev/null +++ b/include/boost/leaf/handle.hpp @@ -0,0 +1,84 @@ +#ifndef BOOST_LEAF_73685B76115611E9950D61678B7F4AFC +#define BOOST_LEAF_73685B76115611E9950D61678B7F4AFC + +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +namespace boost { namespace leaf { + + template + typename std::remove_reference()().value())>::type handle_all( TryBlock && try_block, Handler && ... handler ) + { + using namespace leaf_detail; + typename deduce_static_store::type>::type ss; + ss.set_reset(true); + if( auto r = std::forward(try_block)() ) + return *r; + else + return ss.handle_error(error_info(r.error()), std::forward(handler)...); + } + + namespace leaf_detail + { + template ::mp_args> + struct handler_wrapper; + + template class L, class... A> + struct handler_wrapper> + { + F f_; + explicit handler_wrapper( F && f ) noexcept: + f_(std::forward(f)) + { + } + R operator()( A... a ) const + { + return f_(a...); + } + }; + + template class L, class... A> + struct handler_wrapper, F, L> + { + F f_; + explicit handler_wrapper( F && f ) noexcept: + f_(std::forward(f)) + { + } + result operator()( A... a ) const + { + f_(a...); + return { }; + } + }; + } + + template + decltype(std::declval()()) handle_some( TryBlock && try_block, Handler && ... handler ) + { + using namespace leaf_detail; + using R = typename function_traits::return_type; + typename deduce_static_store::type>::type ss; + if( auto r = std::forward(try_block)() ) + { + ss.set_reset(true); + return r; + } + else if( auto rr = ss.handle_error(error_info(r.error()), handler_wrapper(std::forward(handler))..., [&r] { return r; } ) ) + { + ss.set_reset(true); + return rr; + } + else + return rr; + } + +} } + +#endif diff --git a/include/boost/leaf/preload.hpp b/include/boost/leaf/preload.hpp index ffde506..a3977e6 100644 --- a/include/boost/leaf/preload.hpp +++ b/include/boost/leaf/preload.hpp @@ -1,13 +1,14 @@ #ifndef BOOST_LEAF_25AF99F6DC6F11E8803DE9BC9723C688 #define BOOST_LEAF_25AF99F6DC6F11E8803DE9BC9723C688 -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 namespace boost { namespace leaf { @@ -16,19 +17,19 @@ namespace boost { namespace leaf { template struct tuple_for_each_preload { - static void trigger( Tuple & tup, error const & e ) noexcept + static void trigger( Tuple & tup, error_id const & id ) noexcept { - tuple_for_each_preload::trigger(tup,e); - std::get(tup).trigger(e); + tuple_for_each_preload::trigger(tup,id); + std::get(tup).trigger(id); } }; template struct tuple_for_each_preload<0, Tuple> { - static void trigger( Tuple const &, error const & ) noexcept { } + static void trigger( Tuple const &, error_id const & ) noexcept { } }; - } //leaf_detail + } // leaf_detail //////////////////////////////////////// @@ -38,20 +39,22 @@ namespace boost { namespace leaf { class preloaded_item { slot * s_; - E v_; + E e_; + public: - explicit preloaded_item( E && v ) noexcept: + + explicit preloaded_item( E && e ) noexcept: s_(tl_slot_ptr()), - v_(std::forward(v)) + e_(std::forward(e)) { } - void trigger( error e ) noexcept + void trigger( error_id const & id ) noexcept { if( s_ ) { - if( !s_->has_value() || s_->value().e!=e ) - s_->put( leaf_detail::error_info{std::move(v_),e} ); + if( !s_->has_value() || s_->value().id!=id ) + s_->put( leaf_detail::id_e_pair(id,std::move(e_)) ); } else { @@ -59,7 +62,7 @@ namespace boost { namespace leaf { int c = tl_unexpected_enabled_counter(); assert(c>=0); if( c ) - no_expect_slot( error_info{std::forward(v_),e} ); + no_expect_slot( id_e_pair(id,std::forward(e_)) ); } } }; @@ -70,21 +73,21 @@ namespace boost { namespace leaf { preloaded & operator=( preloaded const & ) = delete; std::tuple...> p_; - error e_; + error_id id_; bool moved_; public: explicit preloaded( E && ... e ) noexcept: p_(preloaded_item(std::forward(e))...), - e_(last_error_value()), + id_(last_error()), moved_(false) { } preloaded( preloaded && x ) noexcept: p_(std::move(x.p_)), - e_(std::move(x.e_)), + id_(std::move(x.id_)), moved_(false) { x.moved_ = true; @@ -94,17 +97,17 @@ namespace boost { namespace leaf { { if( moved_ ) return; - error const e = last_error_value(); - if( e==e_ ) + error_id const id = last_error(); + if( id==id_ ) { if( std::uncaught_exception() ) - leaf_detail::tuple_for_each_preload::trigger(p_,next_error_value()); + leaf_detail::tuple_for_each_preload::trigger(p_,next_error()); } else - leaf_detail::tuple_for_each_preload::trigger(p_,e); + leaf_detail::tuple_for_each_preload::trigger(p_,id); } }; - } //leaf_detail + } // leaf_detail template leaf_detail::preloaded preload( E && ... e ) noexcept @@ -131,12 +134,12 @@ namespace boost { namespace leaf { { } - void trigger( error e ) noexcept + void trigger( error_id const & id ) noexcept { if( s_ ) { - if( !s_->has_value() || s_->value().e!=e ) - s_->put( leaf_detail::error_info{f_(),e} ); + if( !s_->has_value() || s_->value().id!=id ) + s_->put( leaf_detail::id_e_pair(id,f_()) ); } else { @@ -144,7 +147,7 @@ namespace boost { namespace leaf { int c = tl_unexpected_enabled_counter(); assert(c>=0); if( c ) - no_expect_slot( error_info{std::forward(f_()),e} ); + no_expect_slot( id_e_pair(id,std::forward(f_())) ); } } }; @@ -154,21 +157,21 @@ namespace boost { namespace leaf { { deferred & operator=( deferred const & ) = delete; std::tuple...> d_; - error e_; + error_id id_; bool moved_; public: explicit deferred( F && ... f ) noexcept: d_(deferred_item(std::forward(f))...), - e_(last_error_value()), + id_(last_error()), moved_(false) { } deferred( deferred && x ) noexcept: d_(std::move(x.d_)), - e_(std::move(x.e_)), + id_(std::move(x.id_)), moved_(false) { x.moved_ = true; @@ -178,17 +181,17 @@ namespace boost { namespace leaf { { if( moved_ ) return; - error const e = last_error_value(); - if( e==e_ ) + error_id const id = last_error(); + if( id==id_ ) { if( std::uncaught_exception() ) - leaf_detail::tuple_for_each_preload::trigger(d_,next_error_value()); + leaf_detail::tuple_for_each_preload::trigger(d_,next_error()); } else - leaf_detail::tuple_for_each_preload::trigger(d_,e); + leaf_detail::tuple_for_each_preload::trigger(d_,id); } }; - } //leaf_detail + } // leaf_detail template leaf_detail::deferred defer( F && ... f ) noexcept diff --git a/include/boost/leaf/result.hpp b/include/boost/leaf/result.hpp index 3120714..99882b4 100644 --- a/include/boost/leaf/result.hpp +++ b/include/boost/leaf/result.hpp @@ -1,14 +1,15 @@ #ifndef BOOST_LEAF_2CD8E6B8CA8D11E8BD3B80D66CE5B91B #define BOOST_LEAF_2CD8E6B8CA8D11E8BD3B80D66CE5B91B -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#include -#include +#include +#include +#include #define LEAF_AUTO(v,r) auto _r_##v = r; if( !_r_##v ) return _r_##v.error(); auto & v = *_r_##v #define LEAF_CHECK(r) {auto _r = r; if( !_r ) return _r.error();} @@ -17,59 +18,12 @@ namespace boost { namespace leaf { class bad_result: public std::exception { }; - template - class expect; - - template - class result; - - namespace leaf_detail - { - template - struct unhandled_error_base> - { - static leaf::error value( leaf::error const & e ) noexcept { return e; } - }; - } - - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( expect const &, result const &, F && ... ) noexcept; - - template - P const * peek( expect const &, result const & ) noexcept; - - template - void diagnostic_output( std::ostream &, expect const &, result const & ); - - template - result capture( expect &, result const & ); - //////////////////////////////////////// - namespace leaf_detail - { - enum class result_variant - { - value, - err, - cap - }; - } - template class result { - template - friend typename leaf_detail::handler_pack_return_type::return_type leaf::handle_error( expect const &, result const &, F && ... ) noexcept; - - template - friend P const * leaf::peek( expect const &, result const & ) noexcept; - - template - friend void leaf::diagnostic_output( std::ostream &, expect const &, result const & ); - - template - friend result leaf::capture( expect &, result const & ); + using dynamic_store_ptr = std::shared_ptr; template friend class result; @@ -77,8 +31,8 @@ namespace boost { namespace leaf { union { T value_; - error err_; - error_capture cap_; + error_id err_; + dynamic_store_ptr cap_; }; leaf_detail::result_variant which_; @@ -91,11 +45,11 @@ namespace boost { namespace leaf { value_.~T(); break; case leaf_detail::result_variant::err: - err_.~error(); + err_.~error_id(); break; default: assert(which_==leaf_detail::result_variant::cap); - cap_.~error_capture(); + cap_.~dynamic_store_ptr(); } which_= (leaf_detail::result_variant)-1; } @@ -109,11 +63,11 @@ namespace boost { namespace leaf { (void) new(&value_) T(x.value_); break; case leaf_detail::result_variant::err: - (void) new(&err_) leaf::error(x.err_); + (void) new(&err_) error_id(x.err_); break; default: assert(x.which_==leaf_detail::result_variant::cap); - (void) new(&cap_) error_capture(x.cap_); + (void) new(&cap_) dynamic_store_ptr(x.cap_); }; which_ = x.which_; } @@ -125,36 +79,42 @@ namespace boost { namespace leaf { { case leaf_detail::result_variant:: value: (void) new(&value_) T(std::move(x.value_)); + which_ = x.which_; break; case leaf_detail::result_variant::err: - (void) new(&err_) leaf::error(std::move(x.err_)); + (void) new(&err_) error_id(std::move(x.err_)); + which_ = x.which_; break; default: assert(x.which_==leaf_detail::result_variant::cap); - (void) new(&cap_) error_capture(std::move(x.cap_)); + if( dynamic_store_ptr cap = std::move(x.cap_) ) + { + x.destroy(); + (void) new(&x.err_) error_id(cap->error()); + x.which_ = leaf_detail::result_variant::err; + (void) new(&cap_) dynamic_store_ptr(std::move(cap)); + } + else + (void) new(&cap_) dynamic_store_ptr(std::move(x.cap_)); + which_ = leaf_detail::result_variant::cap; }; - which_ = x.which_; } public: + typedef T value_type; + ~result() noexcept { destroy(); } - result( result const & x ) - { - copy_from(x); - } - result( result && x ) noexcept { move_from(std::move(x)); } - template - result( result const & x ) + result( result const & x ) { copy_from(x); } @@ -165,14 +125,14 @@ namespace boost { namespace leaf { move_from(std::move(x)); } - result() noexcept: - value_(T()), - which_(leaf_detail::result_variant::value) + template + result( result const & x ) { + copy_from(x); } - result( T const & v ): - value_(v), + result(): + value_(T()), which_(leaf_detail::result_variant::value) { } @@ -183,22 +143,25 @@ namespace boost { namespace leaf { { } - result( leaf::error const & e ) noexcept: - err_(e), + result( T const & v ): + value_(v), + which_(leaf_detail::result_variant::value) + { + } + + result( error_id const & id ) noexcept: + err_(id), which_(leaf_detail::result_variant::err) { } - result( leaf::error_capture const & cap ) noexcept: - cap_(cap), - which_(leaf_detail::result_variant::cap) - { - } + result( std::shared_ptr && ) noexcept; - result( leaf::error_capture && cap ) noexcept: - cap_(std::move(cap)), - which_(leaf_detail::result_variant::cap) + result & operator=( result && x ) noexcept { + destroy(); + move_from(std::move(x)); + return *this; } result & operator=( result const & x ) @@ -208,13 +171,22 @@ namespace boost { namespace leaf { return *this; } - result & operator=( result && x ) noexcept + template + result & operator=( result && x ) noexcept { destroy(); move_from(std::move(x)); return *this; } + template + result & operator=( result const & x ) + { + destroy(); + copy_from(x); + return *this; + } + explicit operator bool() const noexcept { return which_==leaf_detail::result_variant::value; @@ -247,19 +219,19 @@ namespace boost { namespace leaf { } template - leaf::error error( E && ... e ) noexcept + error_id error( E && ... e ) noexcept { switch( which_ ) { case leaf_detail::result_variant::value: - return leaf::error(std::forward(e)...); + return leaf::new_error(std::forward(e)...); case leaf_detail::result_variant::cap: - { - error_capture cap = cap_; - destroy(); - (void) new(&err_) leaf::error(cap.unload()); - which_ = leaf_detail::result_variant::err; - } + { + dynamic_store_ptr cap = cap_; + destroy(); + (void) new(&err_) error_id(cap->unload()); + which_ = leaf_detail::result_variant::err; + } default: assert(which_==leaf_detail::result_variant::err); return err_.propagate(std::forward(e)...); @@ -273,23 +245,11 @@ namespace boost { namespace leaf { class result: result { - template - friend typename leaf_detail::handler_pack_return_type::return_type leaf::handle_error( expect const &, result const &, F && ... ) noexcept; - - template - friend P const * leaf::peek( expect const &, result const & ) noexcept; - - template - friend void leaf::diagnostic_output( std::ostream &, expect const &, result const & ); - - template - friend result leaf::capture( expect &, result const & ); + typedef result base; template friend class result; - typedef result base; - result( result && rb ): base(std::move(rb)) { @@ -297,81 +257,35 @@ namespace boost { namespace leaf { public: + typedef void value_type; + ~result() noexcept { } - result() noexcept = default; + result() = default; - result( leaf::error const & e ) noexcept: - base(e) + result( error_id const & id ) noexcept: + base(id) { } - result( leaf::error_capture const & cap ) noexcept: - base(cap) + result( std::shared_ptr && ) noexcept; + + void value() const { + (void) base::value(); } - result( leaf::error_capture && cap ) noexcept: - base(std::move(cap)) + void operator*() const { + return value(); } using base::operator bool; using base::error; }; - //////////////////////////////////////// - - template - typename leaf_detail::handler_pack_return_type::return_type handle_error( expect const & exp, result const & r, F && ... f ) noexcept - { - assert(!r); - if( r.which_ == leaf_detail::result_variant::err ) - return handle_error(exp,r.err_,f...); - else - { - assert(r.which_==leaf_detail::result_variant::cap); - return handle_error(r.cap_,f...); - } - } - - template - P const * peek( expect const & exp, result const & r ) noexcept - { - assert(!r); - if( r.which_==leaf_detail::result_variant::err ) - return peek

(exp,r.err_); - else - { - assert(r.which_==leaf_detail::result_variant::cap); - return peek

(r.cap_); - } - } - - template - void diagnostic_output( std::ostream & os, expect const & exp, result const & r ) - { - assert(!r); - if( r.which_==leaf_detail::result_variant::err ) - return diagnostic_output(os,exp,r.err_); - else - { - assert(r.which_==leaf_detail::result_variant::cap); - return diagnostic_output(os,r.cap_); - } - } - - template - result capture( expect & exp, result const & r ) - { - if( r.which_==leaf_detail::result_variant::err ) - return capture(exp,r.err_); - else - return r; - } - } } #endif diff --git a/include/boost/leaf/detail/throw.hpp b/include/boost/leaf/throw.hpp similarity index 68% rename from include/boost/leaf/detail/throw.hpp rename to include/boost/leaf/throw.hpp index f89a5ab..f3c15f8 100644 --- a/include/boost/leaf/detail/throw.hpp +++ b/include/boost/leaf/throw.hpp @@ -1,13 +1,14 @@ #ifndef BOOST_LEAF_75F38740D98D11E881DDB244C82C3C47 #define BOOST_LEAF_75F38740D98D11E881DDB244C82C3C47 -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 #define LEAF_EXCEPTION(...) ::boost::leaf::leaf_detail::exception_(__FILE__,__LINE__,__FUNCTION__,__VA_ARGS__) @@ -15,16 +16,14 @@ namespace boost { namespace leaf { - //////////////////////////////////////// - namespace leaf_detail { - inline void enforce_std_exception( std::exception const & ) { } + inline void enforce_std_exception( std::exception const & ) noexcept { } template class exception: public Ex, - public error + public error_id { public: @@ -34,14 +33,14 @@ namespace boost { namespace leaf { template exception( Ex && ex, E && ... e ) noexcept: Ex(std::move(ex)), - error(std::forward(e)...) + error_id(new_error(std::forward(e)...)) { leaf_detail::enforce_std_exception(*this); } }; template - exception exception_( char const * file, int line, char const * function, Ex && ex, E && ... e ) + exception exception_( char const * file, int line, char const * function, Ex && ex, E && ... e ) noexcept { assert(file&&*file); assert(line>0); @@ -51,7 +50,7 @@ namespace boost { namespace leaf { } template - leaf_detail::exception exception( Ex && ex, E && ... e ) + leaf_detail::exception exception( Ex && ex, E && ... e ) noexcept { return leaf_detail::exception( std::forward(ex), std::forward(e)... ); } diff --git a/include/boost/leaf/try.hpp b/include/boost/leaf/try.hpp new file mode 100644 index 0000000..9ed77e8 --- /dev/null +++ b/include/boost/leaf/try.hpp @@ -0,0 +1,63 @@ +#ifndef BOOST_LEAF_87F274C4D4BA11E89928D55AC82C3C47 +#define BOOST_LEAF_87F274C4D4BA11E89928D55AC82C3C47 + +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +namespace boost { namespace leaf { + + inline error_id get_error_id( std::exception const & ex ) noexcept + { + if( auto id = dynamic_cast(&ex) ) + return *id; + else + return next_error(); + } + + template + decltype(std::declval()()) try_( TryBlock && try_block, Handler && ... handler ) + { + using namespace leaf_detail; + typename deduce_static_store::type>::type ss; + ss.set_reset(true); + try + { + return std::forward(try_block)(); + } + catch( captured_exception & cap ) + { + try + { + cap.unload_and_rethrow_original_exception(); + assert(0); + throw; + } + catch( std::exception const & ex ) + { + return ss.handle_error(error_info(get_error_id(ex),&ex,&cap,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); + } + catch( ... ) + { + return ss.handle_error(error_info(next_error(),0,&cap,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); + } + } + catch( std::exception const & ex ) + { + return ss.handle_error(error_info(get_error_id(ex),&ex,0,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); + } + catch( ... ) + { + return ss.handle_error(error_info(next_error(),0,0,&print_exception_info), std::forward(handler)..., [ ]() -> typename function_traits::return_type { throw; }); + } + } + +} } + +#endif diff --git a/meson.build b/meson.build index 18dd219..bf0091a 100644 --- a/meson.build +++ b/meson.build @@ -14,6 +14,7 @@ if not meson.is_subproject() '-Wno-unused-variable', '-Wno-non-virtual-dtor', '-Wno-dangling-else', + '-Wno-delete-non-virtual-dtor', language:'cpp' ) endif endif @@ -26,55 +27,50 @@ leaf = declare_dependency( include_directories: includes ) tests = [ '_hpp_all_test', + '_hpp_capture_exception_test', + '_hpp_capture_result_test', '_hpp_common_test', - '_hpp_error_capture_test', '_hpp_error_test', - '_hpp_exception_capture_test', - '_hpp_exception_test', - '_hpp_expect_test', + '_hpp_exception_to_result_test', + '_hpp_handle_test', '_hpp_preload_test', '_hpp_result_test', - 'basic_test', - 'defer_test.1', - 'defer_test.2', - 'defer_test.3', - 'defer_test.4', - 'defer_test.5', - 'defer_test.6', - 'defer_test.7', - 'defer_test.8', - 'diagnostic_output_test', - 'error_capture_test.1', - 'error_capture_test.2', - 'error_test', - 'exception_capture_test', - 'exception_test.1', - 'exception_test.2', - 'expect_test.1', - 'expect_test.2', - 'expect_test.3', - 'expect_test.4', - 'expect_test.5', + '_hpp_throw_test', + '_hpp_try_test', + 'capture_exception_async_test', + 'capture_exception_state_test', + 'capture_exception_unload_test', + 'capture_result_async_test', + 'capture_result_state_test', + 'capture_result_unload_test', + 'defer_basic_test', + 'defer_nested_error_exception_test', + 'defer_nested_error_result_test', + 'defer_nested_new_error_exception_test', + 'defer_nested_new_error_result_test', + 'defer_nested_success_exception_test', + 'defer_nested_success_result_test', + 'diagnostic_info_test', 'function_traits_test', + 'handle_all_test', + 'handle_some_basic_test', + 'handle_some_test', 'is_error_type_test', - 'multiple_errors_test', 'optional_test', - 'preload_test.1', - 'preload_test.2', - 'preload_test.3', - 'preload_test.4', - 'preload_test.5', - 'preload_test.6', - 'preload_test.7', + 'preload_basic_test', + 'preload_nested_error_exception_test', + 'preload_nested_error_result_test', + 'preload_nested_new_error_exception_test', + 'preload_nested_new_error_result_test', + 'preload_nested_success_exception_test', + 'preload_nested_success_result_test', 'print_test', - 'result_capture_test', - 'result_test.1', - 'result_test.2', - 'result_test.3', - 'result_test.4', - 'result_test.5', - 'result_test.6', - 'result_void_capture_test' + 'result_bad_result_test', + 'result_state_test', + 'static_store_deduction_test', + 'static_store_test', + 'try_exception_test', + 'try_test' ] foreach t : tests test(t, executable(t, 'test/'+t+'.cpp', dependencies: [leaf,thread_dep] ) ) @@ -83,10 +79,10 @@ endforeach examples = [ 'capture_result', 'capture_eh', + 'exception_to_result', 'print_file_result', 'print_file_eh', - 'print_half', - 'return_exception' + 'print_half' ] foreach e : examples executable(e, 'example/'+e+'.cpp', dependencies: [leaf,thread_dep] ) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e1296dc..86df7df 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -8,70 +8,68 @@ import testing ; project : requirements - gcc:"-std=c++11" + gcc:"-std=c++11 -Wno-delete-non-virtual-dtor -Wno-parentheses" gcc:"-pthread" - darwin:"-std=c++11 -Wno-unused-variable -Wno-non-virtual-dtor -Wno-dangling-else" + clang:"-Wno-dangling-else" + darwin:"-std=c++11 -Wno-unused-variable -Wno-delete-non-virtual-dtor -Wno-non-virtual-dtor -Wno-dangling-else" msvc:"-wd 4267 -wd 4996 -wd 4244" ../../.. ; -run _hpp_all_test.cpp ; -run _hpp_common_test.cpp ; -run _hpp_error_capture_test.cpp ; -run _hpp_error_test.cpp ; -run _hpp_exception_capture_test.cpp ; -run _hpp_exception_test.cpp ; -run _hpp_expect_test.cpp ; -run _hpp_preload_test.cpp ; -run _hpp_result_test.cpp ; -run basic_test.cpp ; -run defer_test.1.cpp ; -run defer_test.2.cpp ; -run defer_test.3.cpp ; -run defer_test.4.cpp ; -run defer_test.5.cpp ; -run defer_test.6.cpp ; -run defer_test.7.cpp ; -run diagnostic_output_test.cpp ; -run error_capture_test.1.cpp ; -run error_capture_test.2.cpp ; -run error_test.cpp ; -run exception_capture_test.cpp : : : multi ; -run exception_test.2.cpp ; -run exception_test.1.cpp ; -run expect_test.1.cpp ; -run expect_test.2.cpp ; -run expect_test.3.cpp ; -run expect_test.4.cpp ; -run expect_test.5.cpp ; -run multiple_errors_test.cpp ; +compile _hpp_all_test.cpp ; +compile _hpp_capture_exception_test.cpp ; +compile _hpp_capture_result_test.cpp ; +compile _hpp_common_test.cpp ; +compile _hpp_error_test.cpp ; +compile _hpp_exception_to_result_test.cpp ; +compile _hpp_handle_test.cpp ; +compile _hpp_preload_test.cpp ; +compile _hpp_result_test.cpp ; +compile _hpp_throw_test.cpp ; +compile _hpp_try_test.cpp ; + +compile-fail is_error_type_fail_test.cpp ; + +run capture_exception_async_test.cpp ; +run capture_exception_state_test.cpp ; +run capture_exception_unload_test.cpp ; +run capture_result_async_test.cpp ; +run capture_result_state_test.cpp ; +run capture_result_unload_test.cpp ; +run defer_basic_test.cpp ; +run defer_nested_error_exception_test.cpp ; +run defer_nested_error_result_test.cpp ; +run defer_nested_new_error_exception_test.cpp ; +run defer_nested_new_error_result_test.cpp ; +run defer_nested_success_exception_test.cpp ; +run defer_nested_success_result_test.cpp ; +run diagnostic_info_test.cpp ; run function_traits_test.cpp ; +run handle_all_test.cpp ; +run handle_some_basic_test.cpp ; +run handle_some_test.cpp ; run is_error_type_test.cpp ; run optional_test.cpp ; -run preload_test.1.cpp ; -run preload_test.2.cpp ; -run preload_test.3.cpp ; -run preload_test.4.cpp ; -run preload_test.5.cpp ; -run preload_test.6.cpp ; +run preload_basic_test.cpp ; +run preload_nested_error_exception_test.cpp ; +run preload_nested_error_result_test.cpp ; +run preload_nested_new_error_exception_test.cpp ; +run preload_nested_new_error_result_test.cpp ; +run preload_nested_success_exception_test.cpp ; +run preload_nested_success_result_test.cpp ; run print_test.cpp ; -run result_capture_test.cpp : : : multi ; -run result_test.1.cpp ; -run result_test.2.cpp ; -run result_test.3.cpp ; -run result_test.4.cpp ; -run result_test.5.cpp ; -run result_test.6.cpp ; -run result_void_capture_test.cpp : : : multi ; -compile-fail expect_fail_test.1.cpp ; -compile-fail expect_fail_test.2.cpp ; -compile-fail error_fail_test.cpp ; +run result_bad_result_test.cpp ; +run result_state_test.cpp ; +run static_store_deduction_test.cpp ; +run static_store_test.cpp ; +run try_exception_test.cpp ; +run try_test.cpp ; -exe print_file_result : ../example/print_file_result.cpp ; -exe print_file_eh : ../example/print_file_eh.cpp ; -#exe lua_callback_result : ../example/lua_callback_result.cpp ; -#exe lua_callback_eh : ../example/lua_callback_eh.cpp ; -exe capture_result : ../example/capture_result.cpp : multi ; exe capture_eh : ../example/capture_eh.cpp : multi ; +exe capture_result : ../example/capture_result.cpp : multi ; +exe exception_to_result : ../example/exception_to_result.cpp ; +#exe lua_callback_result : ../example/exception_to_result.cpp ; +#exe lua_callback_eh : ../example/lua_callback_eh.cpp ; +exe print_file_eh : ../example/print_file_eh.cpp ; +exe print_file_result : ../example/print_file_result.cpp ; exe print_half : ../example/print_half.cpp ; -exe return_exception : ../example/return_exception.cpp ; diff --git a/test/_hpp_all_test.cpp b/test/_hpp_all_test.cpp index 341dfa8..aa4c265 100644 --- a/test/_hpp_all_test.cpp +++ b/test/_hpp_all_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 diff --git a/test/_hpp_capture_exception_test.cpp b/test/_hpp_capture_exception_test.cpp new file mode 100644 index 0000000..bd0e4f7 --- /dev/null +++ b/test/_hpp_capture_exception_test.cpp @@ -0,0 +1,9 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +int main() { return 0; } diff --git a/test/_hpp_capture_result_test.cpp b/test/_hpp_capture_result_test.cpp new file mode 100644 index 0000000..f660b58 --- /dev/null +++ b/test/_hpp_capture_result_test.cpp @@ -0,0 +1,9 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +int main() { return 0; } diff --git a/test/_hpp_common_test.cpp b/test/_hpp_common_test.cpp index 62811a1..23dc3aa 100644 --- a/test/_hpp_common_test.cpp +++ b/test/_hpp_common_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 diff --git a/test/_hpp_error_capture_test.cpp b/test/_hpp_error_capture_test.cpp deleted file mode 100644 index cef5ac8..0000000 --- a/test/_hpp_error_capture_test.cpp +++ /dev/null @@ -1,9 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -int main() { return 0; } diff --git a/test/_hpp_error_test.cpp b/test/_hpp_error_test.cpp index ec3c95c..19a79fa 100644 --- a/test/_hpp_error_test.cpp +++ b/test/_hpp_error_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 diff --git a/test/_hpp_exception_capture_test.cpp b/test/_hpp_exception_capture_test.cpp deleted file mode 100644 index ca42968..0000000 --- a/test/_hpp_exception_capture_test.cpp +++ /dev/null @@ -1,9 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -int main() { return 0; } diff --git a/test/_hpp_exception_test.cpp b/test/_hpp_exception_test.cpp deleted file mode 100644 index 10569d6..0000000 --- a/test/_hpp_exception_test.cpp +++ /dev/null @@ -1,9 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -int main() { return 0; } diff --git a/test/_hpp_exception_to_result_test.cpp b/test/_hpp_exception_to_result_test.cpp new file mode 100644 index 0000000..0f8ba33 --- /dev/null +++ b/test/_hpp_exception_to_result_test.cpp @@ -0,0 +1,9 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +int main() { return 0; } diff --git a/test/_hpp_expect_test.cpp b/test/_hpp_expect_test.cpp deleted file mode 100644 index 230473a..0000000 --- a/test/_hpp_expect_test.cpp +++ /dev/null @@ -1,9 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -int main() { return 0; } diff --git a/test/_hpp_handle_test.cpp b/test/_hpp_handle_test.cpp new file mode 100644 index 0000000..a704338 --- /dev/null +++ b/test/_hpp_handle_test.cpp @@ -0,0 +1,9 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +int main() { return 0; } diff --git a/test/_hpp_preload_test.cpp b/test/_hpp_preload_test.cpp index a865538..005d35f 100644 --- a/test/_hpp_preload_test.cpp +++ b/test/_hpp_preload_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 diff --git a/test/_hpp_result_test.cpp b/test/_hpp_result_test.cpp index 85cfc16..88362f8 100644 --- a/test/_hpp_result_test.cpp +++ b/test/_hpp_result_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 diff --git a/test/_hpp_throw_test.cpp b/test/_hpp_throw_test.cpp new file mode 100644 index 0000000..f92173a --- /dev/null +++ b/test/_hpp_throw_test.cpp @@ -0,0 +1,9 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +int main() { return 0; } diff --git a/test/_hpp_try_test.cpp b/test/_hpp_try_test.cpp new file mode 100644 index 0000000..772af23 --- /dev/null +++ b/test/_hpp_try_test.cpp @@ -0,0 +1,9 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +int main() { return 0; } diff --git a/test/basic_test.cpp b/test/basic_test.cpp deleted file mode 100644 index 0d5dfca..0000000 --- a/test/basic_test.cpp +++ /dev/null @@ -1,134 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -template -struct info -{ - int value; -}; - -leaf::error f1() -{ - return leaf::error( info<1>{1} ); -} - -leaf::error f2() -{ - return f1().propagate( info<2>{2} ); -} - -leaf::error f3() -{ - return f2().propagate( info<3>{3} ); -} - -leaf::error f4() -{ - return f3().propagate(); -} - -int main() -{ - leaf::expect,info<2>,info<4>> exp0; - leaf::error e0 = f4(); - { - info<1> const * p = leaf::peek>(exp0,e0); - BOOST_TEST(p && p->value==1); - } - - { - info<2> const * p = leaf::peek>(exp0,e0); - BOOST_TEST(p && p->value==2); - } - - BOOST_TEST(!leaf::peek>(exp0,e0)); - leaf::expect,info<2>,info<4>> exp; - leaf::error e1 = f4(); - - { - info<1> const * p = leaf::peek>(exp0,e0); - BOOST_TEST(p && p->value==1); - } - - { - info<2> const * p = leaf::peek>(exp0,e0); - BOOST_TEST(p && p->value==2); - } - - BOOST_TEST(!leaf::peek>(exp0,e0)); - BOOST_TEST(!leaf::peek>(exp,e0)); - BOOST_TEST(!leaf::peek>(exp,e0)); - BOOST_TEST(!leaf::peek>(exp,e0)); - - { - info<1> const * p = leaf::peek>(exp,e1); - BOOST_TEST(p && p->value==1); - } - - { - info<2> const * p = leaf::peek>(exp,e1); - BOOST_TEST(p && p->value==2); - } - - BOOST_TEST(!leaf::peek>(exp,e1)); - BOOST_TEST( !handle_error( exp, e1, [ ](info<1>,info<2>,info<4>)->void { } ) ); - leaf::error e2 = f4(); - - { - info<1> const * p = leaf::peek>(exp,e2); - BOOST_TEST(p && p->value==1); - } - - { - info<2> const * p = leaf::peek>(exp,e2); - BOOST_TEST(p && p->value==2); - } - - BOOST_TEST(!leaf::peek>(exp,e2)); - - { - int c1=0, c2=0, c3=0; - bool handled = handle_error( exp, e2, - [&c1]( info<1>, info<2>, info<4> ) - { - ++c1; - }, - [&c2]( info<1>, info<2>, info<4> ) - { - ++c2; - }, - [&c3]( info<2> const & i2, info<1> const & i1 ) - { - BOOST_TEST(i1.value==1); - BOOST_TEST(i2.value==2); - ++c3; - } ); - BOOST_TEST(handled); - BOOST_TEST(c1==0); - BOOST_TEST(c2==0); - BOOST_TEST(c3==1); - } - - { - int c=0; - bool handled = handle_error( exp0, e0, - [&c]( info<2> const & i2, info<1> const & i1 ) - { - BOOST_TEST(i1.value==1); - BOOST_TEST(i2.value==2); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - - return boost::report_errors(); -} diff --git a/test/capture_exception_async_test.cpp b/test/capture_exception_async_test.cpp new file mode 100644 index 0000000..c92e285 --- /dev/null +++ b/test/capture_exception_async_test.cpp @@ -0,0 +1,85 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" +#include +#include +#include +#include + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +struct fut_info +{ + int a; + int b; + int result; + std::future fut; +}; + +template +std::vector launch_tasks( int task_count, F f ) +{ + assert(task_count>0); + std::vector fut; + std::generate_n( std::inserter(fut,fut.end()), task_count, + [=] + { + int const a = rand(); + int const b = rand(); + int const res = (rand()%10) - 5; + return fut_info { a, b, res, std::async( std::launch::async, + [=] + { + return f(a,b,res); + } ) }; + } ); + return fut; +} + +int main() +{ + std::vector fut = launch_tasks, info<2>>( 42, + leaf::capture_exception,info<2>,info<3>>( + [ ]( int a, int b, int res ) + { + if( res>=0 ) + return res; + else + throw leaf::exception( std::exception(), info<1>{a}, info<2>{b}, info<3>{} ); + } ) ); + + for( auto & f : fut ) + { + f.fut.wait(); + int r = leaf::try_( + [&] + { + return f.fut.get(); + }, + [&]( info<1> const & x1, info<2> const & x2 ) + { + BOOST_TEST(x1.value==f.a); + BOOST_TEST(x2.value==f.b); + return -1; + }, + [ ] + { + return -2; + } ); + if( f.result>=0 ) + BOOST_TEST(r==f.result); + else + BOOST_TEST(r==-1); + } + + return boost::report_errors(); +} diff --git a/test/capture_exception_state_test.cpp b/test/capture_exception_state_test.cpp new file mode 100644 index 0000000..1fd31b9 --- /dev/null +++ b/test/capture_exception_state_test.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +int count = 0; + +template +struct info +{ + info() noexcept + { + ++count; + } + + info( info const & ) noexcept + { + ++count; + } + + ~info() noexcept + { + --count; + } +}; + +namespace boost { namespace leaf { + template struct is_error_type>: public std::true_type { }; +} } + +int main() +{ + auto f = leaf::capture_exception, info<2>, info<3>>( + [ ] + { + throw leaf::exception( std::exception(), info<1>{}, info<3>{} ); + } ); + leaf::try_( + [&f] + { + BOOST_TEST(count==0); + try { f(); } + catch(...) { BOOST_TEST(count==2); throw; } + + }, + [ ] + { + } ); + BOOST_TEST(count==0); + return boost::report_errors(); +} diff --git a/test/capture_exception_unload_test.cpp b/test/capture_exception_unload_test.cpp new file mode 100644 index 0000000..18df648 --- /dev/null +++ b/test/capture_exception_unload_test.cpp @@ -0,0 +1,171 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +template +void test( F f_ ) +{ + auto f = leaf::capture_exception, info<2>, info<3>>( [=] { return f_(); } ); + + { + int c=0; + leaf::try_( + [&f] + { + return f(); + }, + [&c]( info<1> const & x ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(c==0); + c = 1; + }, + [&c] + { + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==1); + } + + { + int c=0; + leaf::try_( + [&f] + { + return f(); + }, + [&c]( info<2> const & x ) + { + BOOST_TEST(x.value==2); + BOOST_TEST(c==0); + c = 1; + }, + [&c] + { + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==2); + } + + { + int r = leaf::try_( + [&f] + { + (void) f(); return 0; + }, + [ ]( info<1> const & x ) + { + BOOST_TEST(x.value==1); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + } + + { + int r = leaf::try_( + [&f] + { + (void) f(); return 0; + }, + [ ]( info<2> const & x ) + { + BOOST_TEST(x.value==2); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==2); + } + + { + bool r = leaf::try_( + [&f] + { + (void) f(); return true; + }, + [ ]( info<1> const & x, info<2> const & ) + { + return true; + }, + [ ]( info<1> const & x, info<3> const & y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==3); + return false; + }, + [ ]( info<1> const & x ) + { + return true; + }, + [ ] + { + return true; + } ); + BOOST_TEST(!r); + } + + { + bool r = leaf::try_( + [&f] + { + (void) f(); return false; + }, + [ ]( info<1> const & x, info<2> const & ) + { + return false; + }, + [ ]( info<1> const & x, info<3> const & y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==3); + return true; + }, + [ ]( info<1> const & x ) + { + return false; + }, + [ ] + { + return false; + } ); + BOOST_TEST(r); + } +} + +int main() +{ + test( + [ ] + { + throw leaf::exception( std::exception(), info<1>{1}, info<3>{3} ); // Derives from leaf::error_id + } ); + + test( + [ ] + { + auto propagate = leaf::preload( info<1>{1}, info<3>{3} ); + throw std::exception(); // Does not derive from leaf::error_id + } ); + + return boost::report_errors(); +} diff --git a/test/capture_result_async_test.cpp b/test/capture_result_async_test.cpp new file mode 100644 index 0000000..85fe2d5 --- /dev/null +++ b/test/capture_result_async_test.cpp @@ -0,0 +1,85 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include "boost/core/lightweight_test.hpp" +#include +#include +#include +#include + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +struct fut_info +{ + int a; + int b; + int result; + std::future> fut; +}; + +template +std::vector launch_tasks( int task_count, F f ) +{ + assert(task_count>0); + std::vector fut; + std::generate_n( std::inserter(fut,fut.end()), task_count, + [=] + { + int const a = rand(); + int const b = rand(); + int const res = (rand()%10) - 5; + return fut_info { a, b, res, std::async( std::launch::async, + [=] + { + return f(a,b,res); + } ) }; + } ); + return fut; +} + +int main() +{ + std::vector fut = launch_tasks, info<2>>( 42, + leaf::capture_result,info<2>,info<3>>( + [ ]( int a, int b, int res ) -> leaf::result + { + if( res>=0 ) + return res; + else + return leaf::new_error( info<1>{a}, info<2>{b}, info<3>{} ); + } ) ); + + for( auto & f : fut ) + { + f.fut.wait(); + int r = leaf::handle_all( + [&] + { + return f.fut.get(); + }, + [&]( info<1> const & x1, info<2> const & x2 ) + { + BOOST_TEST(x1.value==f.a); + BOOST_TEST(x2.value==f.b); + return -1; + }, + [ ] + { + return -2; + } ); + if( f.result>=0 ) + BOOST_TEST(r==f.result); + else + BOOST_TEST(r==-1); + } + + return boost::report_errors(); +} diff --git a/test/capture_result_state_test.cpp b/test/capture_result_state_test.cpp new file mode 100644 index 0000000..c01277e --- /dev/null +++ b/test/capture_result_state_test.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +int count = 0; + +template +struct info +{ + info() noexcept + { + ++count; + } + + info( info const & ) noexcept + { + ++count; + } + + ~info() noexcept + { + --count; + } +}; + +namespace boost { namespace leaf { + template struct is_error_type>: public std::true_type { }; +} } + +int main() +{ + auto f = leaf::capture_result, info<2>, info<3>>( + [ ]() -> leaf::result + { + return leaf::new_error( info<1>{}, info<3>{} ); + } ); + leaf::handle_all( + [&f] + { + BOOST_TEST(count==0); + auto r = f(); + BOOST_TEST(count==2); + return r; + }, + [ ] + { + } ); + BOOST_TEST(count==0); + return boost::report_errors(); +} diff --git a/test/capture_result_unload_test.cpp b/test/capture_result_unload_test.cpp new file mode 100644 index 0000000..41922e8 --- /dev/null +++ b/test/capture_result_unload_test.cpp @@ -0,0 +1,163 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "boost/core/lightweight_test.hpp" + + + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +template +void test( F f_ ) +{ + auto f = leaf::capture_result, info<2>, info<3>>( [=] { return f_(); } ); + + { + int c=0; + leaf::handle_all( + [&f] + { + return f(); + }, + [&c]( info<1> const & x ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(c==0); + c = 1; + }, + [&c] + { + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==1); + } + + { + int c=0; + leaf::handle_all( + [&f] + { + return f(); + }, + [&c]( info<2> const & x ) + { + BOOST_TEST(x.value==2); + BOOST_TEST(c==0); + c = 1; + }, + [&c] + { + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==2); + } + + { + int r = leaf::handle_all( + [&f]() -> leaf::result + { + return f().error(); + }, + [ ]( info<1> const & x ) + { + BOOST_TEST(x.value==1); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + } + + { + int r = leaf::handle_all( + [&f]() -> leaf::result + { + return f().error(); + }, + [ ]( info<2> const & x ) + { + BOOST_TEST(x.value==2); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==2); + } + + { + bool r = leaf::handle_all( + [&f]() -> leaf::result + { + return f().error(); + }, + [ ]( info<1> const & x, info<2> const & ) + { + return true; + }, + [ ]( info<1> const & x, info<3> const & y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==3); + return false; + }, + [ ]( info<1> const & x ) + { + return true; + }, + [ ] + { + return true; + } ); + BOOST_TEST(!r); + } + + { + bool r = leaf::handle_all( + [&f]() -> leaf::result + { + return f().error(); + }, + [ ]( info<1> const & x, info<2> const & ) + { + return false; + }, + [ ]( info<1> const & x, info<3> const & y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==3); + return true; + }, + [ ]( info<1> const & x ) + { + return false; + }, + [ ] + { + return false; + } ); + BOOST_TEST(r); + } +} + +int main() +{ + test( + [ ]() -> leaf::result + { + return leaf::new_error( info<1>{1}, info<3>{3} ); + } ); + return boost::report_errors(); +} diff --git a/test/defer_basic_test.cpp b/test/defer_basic_test.cpp new file mode 100644 index 0000000..4d1e839 --- /dev/null +++ b/test/defer_basic_test.cpp @@ -0,0 +1,56 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +int global; + +int get_global() noexcept +{ + return global; +} + +struct info +{ + int value; +}; + +leaf::error_id g() +{ + global = 0; + auto propagate = leaf::defer( [ ] { return info{get_global()}; } ); + global = 42; + return leaf::new_error(); +} + +leaf::error_id f() +{ + return g(); +} + +int main() +{ + int r = leaf::handle_all( + [ ]() -> leaf::result + { + return f(); + }, + [ ]( info const & i42 ) + { + BOOST_TEST(i42.value==42); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + return boost::report_errors(); +} diff --git a/test/defer_nested_error_exception_test.cpp b/test/defer_nested_error_exception_test.cpp new file mode 100644 index 0000000..8ba07ac --- /dev/null +++ b/test/defer_nested_error_exception_test.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +void f0() +{ + auto propagate = leaf::defer( [ ] { return info<0>{0}; } ); + throw leaf::exception(std::exception(), info<2>{2} ); +} + +void f1() +{ + auto propagate = leaf::defer( [ ] { return info<0>{-1}; }, [ ] { return info<1>{1}; }, [ ] { return info<2>{-1}; } ); + f0(); +} + +void f2() +{ + try + { + f1(); + } + catch( leaf::error_id id ) + { + id.propagate( info<3>{3} ); + throw; + } +} + +int main() +{ + int r = leaf::try_( + [ ] + { + f2(); + return 0; + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST(i0.value==0); + BOOST_TEST(i1.value==1); + BOOST_TEST(i2.value==2); + BOOST_TEST(i3.value==3); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + + return boost::report_errors(); +} diff --git a/test/defer_nested_error_result_test.cpp b/test/defer_nested_error_result_test.cpp new file mode 100644 index 0000000..7589d01 --- /dev/null +++ b/test/defer_nested_error_result_test.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +leaf::error_id f0() +{ + auto propagate = leaf::defer( [ ] { return info<0>{0}; } ); + return leaf::new_error( info<2>{2} ); +} + +leaf::error_id f1() +{ + auto propagate = leaf::defer( [ ] { return info<0>{-1}; }, [ ] { return info<1>{1}; }, [ ] { return info<2>{-1}; } ); + return f0(); +} + +leaf::error_id f2() +{ + return f1().propagate( info<3>{3} ); +} + +int main() +{ + int r = leaf::handle_all( + [ ]() -> leaf::result + { + return f2(); + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST(i0.value==0); + BOOST_TEST(i1.value==1); + BOOST_TEST(i2.value==2); + BOOST_TEST(i3.value==3); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + + return boost::report_errors(); +} diff --git a/test/defer_nested_new_error_exception_test.cpp b/test/defer_nested_new_error_exception_test.cpp new file mode 100644 index 0000000..2a3d118 --- /dev/null +++ b/test/defer_nested_new_error_exception_test.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +void f0() +{ + auto propagate = leaf::defer( [ ] { return info<0>{-1}; } ); + throw leaf::exception( std::exception(), info<1>{-1} ); +} + +void f1() +{ + auto propagate = leaf::defer( [ ] { return info<0>{0}; }, [ ] { return info<1>{1}; }, [ ] { return info<2>{2}; } ); + try { f0(); } catch(...) { } + throw leaf::exception(std::exception()); +} + +leaf::error_id f2() +{ + try + { + f1(); + BOOST_TEST(false); + } + catch( leaf::error_id id ) + { + id.propagate( info<3>{3} ); + throw; + } + catch(...) + { + BOOST_TEST(false); + } + return leaf::new_error(); +} + +int main() +{ + int r = leaf::try_( + [ ] + { + f2(); + return 0; + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST(i0.value==0); + BOOST_TEST(i1.value==1); + BOOST_TEST(i2.value==2); + BOOST_TEST(i3.value==3); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + + return boost::report_errors(); +} diff --git a/test/defer_nested_new_error_result_test.cpp b/test/defer_nested_new_error_result_test.cpp new file mode 100644 index 0000000..6465426 --- /dev/null +++ b/test/defer_nested_new_error_result_test.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +leaf::error_id f0() +{ + auto propagate = leaf::defer( [ ] { return info<0>{-1}; } ); + return leaf::new_error( info<1>{-1} ); +} + +leaf::error_id f1() +{ + auto propagate = leaf::defer( [ ] { return info<0>{0}; }, [ ] { return info<1>{1}; }, [ ] { return info<2>{2}; } ); + (void) f0(); + return leaf::new_error(); +} + +leaf::error_id f2() +{ + return f1().propagate( info<3>{3} ); +} + +int main() +{ + int r = leaf::handle_all( + [ ]() -> leaf::result + { + return f2(); + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST(i0.value==0); + BOOST_TEST(i1.value==1); + BOOST_TEST(i2.value==2); + BOOST_TEST(i3.value==3); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + + return boost::report_errors(); +} diff --git a/test/defer_nested_success_exception_test.cpp b/test/defer_nested_success_exception_test.cpp new file mode 100644 index 0000000..db36b87 --- /dev/null +++ b/test/defer_nested_success_exception_test.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +void g1() +{ + auto propagate = leaf::defer( [ ] { return info{1}; } ); +} + +void g2() +{ + throw std::exception(); +} + +void f() +{ + auto propagate = leaf::defer( [ ] { return info{2}; } ); + g1(); + g2(); +} + +int main() +{ + int r = leaf::try_( + [ ] + { + f(); + return 0; + }, + [ ]( info x ) + { + BOOST_TEST(x.value==2); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + + return boost::report_errors(); +} diff --git a/test/defer_nested_success_result_test.cpp b/test/defer_nested_success_result_test.cpp new file mode 100644 index 0000000..ed35d04 --- /dev/null +++ b/test/defer_nested_success_result_test.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +leaf::result g1() +{ + auto propagate = leaf::defer( [ ] { return info{1}; } ); + return { }; +} + +leaf::result g2() +{ + return leaf::new_error(); +} + +leaf::result f() +{ + auto propagate = leaf::defer( [ ] { return info{2}; } ); + LEAF_CHECK(g1()); + return g2(); +} + +int main() +{ + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_CHECK(f()); + return 1; + }, + [ ]( info x ) + { + BOOST_TEST(x.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + + return boost::report_errors(); +} diff --git a/test/defer_test.1.cpp b/test/defer_test.1.cpp deleted file mode 100644 index af57613..0000000 --- a/test/defer_test.1.cpp +++ /dev/null @@ -1,51 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -int global; - -int get_global() noexcept -{ - return global; -} - -struct info -{ - int value; -}; - -leaf::error g() -{ - global = 0; - auto propagate = leaf::defer( [ ] { return info{get_global()}; } ); - global = 42; - return leaf::error(); -} - -leaf::error f() -{ - return g(); -} - -int main() -{ - leaf::expect exp; - int c=0; - bool handled = handle_error( exp, f(), - [&c]( info const & i42 ) - { - BOOST_TEST(i42.value==42); - ++c; - } ); - BOOST_TEST(handled); - return boost::report_errors(); - BOOST_TEST(c==1); -} diff --git a/test/defer_test.2.cpp b/test/defer_test.2.cpp deleted file mode 100644 index deac3a2..0000000 --- a/test/defer_test.2.cpp +++ /dev/null @@ -1,53 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -template -struct info -{ - int value; -}; - -leaf::error f0() -{ - auto propagate = leaf::defer( [ ] { return info<0>{0}; } ); - return leaf::error( info<2>{2} ); -} - -leaf::error f1() -{ - auto propagate = leaf::defer( [ ] { return info<0>{-1}; }, [ ] { return info<1>{1}; }, [ ] { return info<2>{-1}; } ); - return f0(); -} - -leaf::error f2() -{ - return f1().propagate( info<4>{4} ); -} - -int main() -{ - leaf::expect,info<1>,info<2>,info<3>,info<4>> exp; - leaf::error e = f2(); - BOOST_TEST(!leaf::peek>(exp,e)); - int c=0; - bool handled = handle_error( exp, e, - [&c]( info<1> const & i1, info<2> const & i2, info<4> const & i4 ) - { - BOOST_TEST(i1.value==1); - BOOST_TEST(i2.value==2); - BOOST_TEST(i4.value==4); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - return boost::report_errors(); -} diff --git a/test/defer_test.3.cpp b/test/defer_test.3.cpp deleted file mode 100644 index d61bece..0000000 --- a/test/defer_test.3.cpp +++ /dev/null @@ -1,47 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info { int value; }; - -leaf::result g1() -{ - auto propagate = leaf::defer( [ ] { return info{1}; } ); - return { }; -} - -leaf::result g2() -{ - return leaf::error(); -} - -leaf::result f() -{ - auto propagate = leaf::defer( [ ] { return info{2}; } ); - LEAF_CHECK(g1()); - return g2(); -} - -int main() -{ - leaf::expect exp; - leaf::result r = f(); - int c=0; - BOOST_TEST( handle_error( exp, r, - [&c]( info const & x ) - { - BOOST_TEST(x.value==2); - ++c; - } ) ); - BOOST_TEST(c==1); - return boost::report_errors(); -} diff --git a/test/defer_test.4.cpp b/test/defer_test.4.cpp deleted file mode 100644 index 6296563..0000000 --- a/test/defer_test.4.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct my_error: std::exception { }; - -struct info { int value; }; - -void g1() -{ - auto propagate = leaf::defer( [ ] { return info{1}; } ); -} - -void g2() -{ - throw my_error(); -} - -void f() -{ - auto propagate = leaf::defer( [ ] { return info{2}; } ); - g1(); - g2(); -} - -int main() -{ - leaf::expect exp; - try - { - f(); - } - catch( my_error & e ) - { - int c=0; - handle_exception( exp, e, - [&c]( info const & x ) - { - BOOST_TEST(x.value==2); - ++c; - } ); - BOOST_TEST(c==1); - } - return boost::report_errors(); -} diff --git a/test/defer_test.5.cpp b/test/defer_test.5.cpp deleted file mode 100644 index 6ccd6b2..0000000 --- a/test/defer_test.5.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -template -struct info -{ - int value; -}; - -leaf::error f0() -{ - auto propagate = leaf::defer( [ ] { return info<0>{-1}; } ); - return leaf::error( info<1>{-1} ); -} - -leaf::error f1() -{ - auto propagate = leaf::defer( [ ] { return info<0>{0}; }, [ ] { return info<1>{1}; }, [ ] { return info<2>{2}; } ); - (void) f0(); - return leaf::error(); -} - -leaf::error f2() -{ - return f1().propagate( info<3>{3} ); -} - -int main() -{ - leaf::expect,info<1>,info<2>,info<3>> exp; - leaf::error e = f2(); - int c=0; - bool handled = handle_error( exp, e, - [&c]( info<0> const & i0, info<1> const & i1, info<2> const & i2, info<3> const & i3 ) - { - BOOST_TEST(i0.value==0); - BOOST_TEST(i1.value==1); - BOOST_TEST(i2.value==2); - BOOST_TEST(i3.value==3); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - return boost::report_errors(); -} diff --git a/test/defer_test.6.cpp b/test/defer_test.6.cpp deleted file mode 100644 index 943324a..0000000 --- a/test/defer_test.6.cpp +++ /dev/null @@ -1,44 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info -{ - int value; -}; - -leaf::error f0() -{ - return leaf::error(); -} - -leaf::error f1() -{ - auto propagate = leaf::defer( [ ] { return info{0}; } ); - (void) f0(); - return leaf::error(); -} - -int main() -{ - leaf::expect exp; - leaf::error e = f1(); - int c=0; - bool handled = handle_error( exp, e, - [&c]( info const & i0 ) - { - BOOST_TEST(i0.value==0); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - return boost::report_errors(); -} diff --git a/test/defer_test.7.cpp b/test/defer_test.7.cpp deleted file mode 100644 index 0093710..0000000 --- a/test/defer_test.7.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct my_error: std::exception { }; - -struct info { int value; }; - -void g1() -{ - auto propagate = leaf::defer( [ ] { return info{1}; } ); -} - -void g2() -{ - throw leaf::exception(my_error()); -} - -void f() -{ - auto propagate = leaf::defer( [ ] { return info{2}; } ); - g1(); - g2(); -} - -int main() -{ - leaf::expect exp; - try - { - f(); - } - catch( my_error & e ) - { - int c=0; - handle_exception( exp, e, - [&c]( info const & x ) - { - BOOST_TEST(x.value==2); - ++c; - } ); - BOOST_TEST(c==1); - } - return boost::report_errors(); -} diff --git a/test/defer_test.8.cpp b/test/defer_test.8.cpp deleted file mode 100644 index a7e1fa5..0000000 --- a/test/defer_test.8.cpp +++ /dev/null @@ -1,45 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info -{ - int value; -}; - -leaf::error f0() -{ - return leaf::error(); -} - -leaf::error f1() -{ - auto propagate = leaf::defer( [ ] { return info{42}; } ); - return f0(); -} - -int main() -{ - leaf::expect exp; - leaf::error e = f1(); - int c=0; - bool handled = handle_error( exp, e, - [&c]( leaf::e_unexpected const & unx, leaf::e_unexpected_diagnostic_output const & unxdo ) - { - BOOST_TEST(unx.count==1); - BOOST_TEST(unx.first_type==&leaf::type); - BOOST_TEST(unxdo.value.find(": 42")!=std::string::npos); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - return boost::report_errors(); -} diff --git a/test/diagnostic_info_test.cpp b/test/diagnostic_info_test.cpp new file mode 100644 index 0000000..d9aeb57 --- /dev/null +++ b/test/diagnostic_info_test.cpp @@ -0,0 +1,186 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" +#include + +namespace leaf = boost::leaf; + +template +struct unexpected_test +{ + int value; +}; + +struct my_error: + virtual std::exception +{ + char const * what() const noexcept + { + return "my_error"; + } +}; + +struct printable_payload +{ + friend std::ostream & operator<<( std::ostream & os, printable_payload const & x ) + { + return os << "printed printable_payload"; + } +}; + +struct non_printable_payload +{ +}; + +struct printable_info_printable_payload +{ + printable_payload value; + + friend std::ostream & operator<<( std::ostream & os, printable_info_printable_payload const & x ) + { + return os << "*** printable_info_printable_payload " << x.value << " ***"; + } +}; + +struct printable_info_non_printable_payload +{ + non_printable_payload value; + + friend std::ostream & operator<<( std::ostream & os, printable_info_non_printable_payload const & x ) + { + return os << "*** printable_info_non_printable_payload ***"; + } +}; + +struct non_printable_info_printable_payload +{ + printable_payload value; +}; + +struct non_printable_info_non_printable_payload +{ + non_printable_payload value; +}; + +int main() +{ + leaf::try_( + [ ] + { + LEAF_THROW( my_error(), + printable_info_printable_payload(), + printable_info_non_printable_payload(), + non_printable_info_printable_payload(), + non_printable_info_non_printable_payload(), + unexpected_test<1>{1}, + unexpected_test<2>{2}, + leaf::e_errno{ENOENT} ); + }, + [ ]( + leaf::e_source_location, + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload, + leaf::e_errno, + leaf::error_info const & unmatched ) + { + std::ostringstream st; + st << unmatched; + std::string s = st.str(); + BOOST_TEST(s.find("std::exception::what(): my_error")!=s.npos); + BOOST_TEST(s.find(": N/A")!=s.npos); + BOOST_TEST(s.find(": printed printable_payload")!=s.npos); + BOOST_TEST(s.find("*** printable_info_non_printable_payload ***")!=s.npos); + BOOST_TEST(s.find("*** printable_info_printable_payload printed printable_payload ***")!=s.npos); + BOOST_TEST(s.find(") in function")!=s.npos); + BOOST_TEST(s.find("unexpected")==s.npos); + std::cout << s; + } ); + + std::cout << std::endl; + + leaf::try_( + [ ] + { + LEAF_THROW( my_error(), + printable_info_printable_payload(), + printable_info_non_printable_payload(), + non_printable_info_printable_payload(), + non_printable_info_non_printable_payload(), + unexpected_test<1>{1}, + unexpected_test<2>{2}, + leaf::e_errno{ENOENT} ); + }, + [ ]( + leaf::e_source_location, + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload, + leaf::e_errno, + leaf::diagnostic_info const & unmatched ) + { + std::ostringstream st; + st << unmatched; + std::string s = st.str(); + BOOST_TEST(s.find("std::exception::what(): my_error")!=s.npos); + BOOST_TEST(s.find(": N/A")!=s.npos); + BOOST_TEST(s.find(": printed printable_payload")!=s.npos); + BOOST_TEST(s.find("*** printable_info_non_printable_payload ***")!=s.npos); + BOOST_TEST(s.find("*** printable_info_printable_payload printed printable_payload ***")!=s.npos); + BOOST_TEST(s.find(") in function")!=s.npos); + BOOST_TEST(s.find("Detected 2 attempts")!=s.npos); + BOOST_TEST(s.find("unexpected_test<1>")!=s.npos); + BOOST_TEST(s.find("unexpected_test<2>")==s.npos); + std::cout << s; + } ); + + std::cout << std::endl; + + leaf::try_( + [ ] + { + LEAF_THROW( my_error(), + printable_info_printable_payload(), + printable_info_non_printable_payload(), + non_printable_info_printable_payload(), + non_printable_info_non_printable_payload(), + unexpected_test<1>{1}, + unexpected_test<2>{2}, + leaf::e_errno{ENOENT} ); + }, + [ ]( + leaf::e_source_location, + printable_info_printable_payload, + printable_info_non_printable_payload, + non_printable_info_printable_payload, + non_printable_info_non_printable_payload, + leaf::e_errno, + leaf::verbose_diagnostic_info const & di ) + { + std::ostringstream st; + st << di; + std::string s = st.str(); + BOOST_TEST(s.find("std::exception::what(): my_error")!=s.npos); + BOOST_TEST(s.find(": N/A")!=s.npos); + BOOST_TEST(s.find(": printed printable_payload")!=s.npos); + BOOST_TEST(s.find("*** printable_info_non_printable_payload ***")!=s.npos); + BOOST_TEST(s.find("*** printable_info_printable_payload printed printable_payload ***")!=s.npos); + BOOST_TEST(s.find(") in function")!=s.npos); + BOOST_TEST(s.find("unexpected_test<1>")!=s.npos); + BOOST_TEST(s.find("unexpected_test<2>")!=s.npos); + std::cout << s; + } ); + + std::cout << std::endl; + + return boost::report_errors(); +} diff --git a/test/diagnostic_output_test.cpp b/test/diagnostic_output_test.cpp deleted file mode 100644 index 866b347..0000000 --- a/test/diagnostic_output_test.cpp +++ /dev/null @@ -1,118 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" -#include - -namespace leaf = boost::leaf; - -template -struct unexpected_test -{ - int value; -}; - -struct my_error: - virtual std::exception -{ - char const * what() const noexcept - { - return "my_error"; - } -}; - -struct printable_payload -{ - friend std::ostream & operator<<( std::ostream & os, printable_payload const & x ) - { - return os << "printed printable_payload"; - } -}; - -struct non_printable_payload -{ -}; - -struct printable_info_printable_payload -{ - printable_payload value; - - friend std::ostream & operator<<( std::ostream & os, printable_info_printable_payload const & x ) - { - return os << "*** printable_info_printable_payload " << x.value << " ***"; - } -}; - -struct printable_info_non_printable_payload -{ - non_printable_payload value; - - friend std::ostream & operator<<( std::ostream & os, printable_info_non_printable_payload const & x ) - { - return os << "*** printable_info_non_printable_payload ***"; - } -}; - -struct non_printable_info_printable_payload -{ - printable_payload value; -}; - -struct non_printable_info_non_printable_payload -{ - non_printable_payload value; -}; - -int main() -{ - BOOST_TEST(leaf::leaf_detail::tl_unexpected_enabled_counter()==0); - { - leaf::expect - < - leaf::e_source_location, - printable_info_printable_payload, - printable_info_non_printable_payload, - non_printable_info_printable_payload, - non_printable_info_non_printable_payload, - leaf::e_errno, - leaf::e_unexpected_diagnostic_output, - leaf::e_unexpected - > exp; - BOOST_TEST(leaf::leaf_detail::tl_unexpected_enabled_counter()==2); - try - { - LEAF_THROW( my_error(), - printable_info_printable_payload(), - printable_info_non_printable_payload(), - non_printable_info_printable_payload(), - non_printable_info_non_printable_payload(), - unexpected_test<1>{1}, - unexpected_test<2>{2}, - leaf::e_errno{ENOENT} ); - } - catch( my_error & e ) - { - std::ostringstream st; - diagnostic_output_current_exception(st,exp); - std::string s = st.str(); - BOOST_TEST(s.find("std::exception::what(): my_error")!=s.npos); - BOOST_TEST(s.find(": N/A")!=s.npos); - BOOST_TEST(s.find(": printed printable_payload")!=s.npos); - BOOST_TEST(s.find("*** printable_info_non_printable_payload ***")!=s.npos); - BOOST_TEST(s.find("*** printable_info_printable_payload printed printable_payload ***")!=s.npos); - BOOST_TEST(s.find(") in function")!=s.npos); - BOOST_TEST(s.find("Detected 2 attempts to communicate unexpected error objects, the first one of type ")!=s.npos); - BOOST_TEST(s.find("unexpected_test")!=s.npos); - std::cout << s; - handle_exception( exp, e, [ ]{ } ); - } - } - BOOST_TEST(leaf::leaf_detail::tl_unexpected_enabled_counter()==0); - return boost::report_errors(); -} diff --git a/test/error_capture_test.1.cpp b/test/error_capture_test.1.cpp deleted file mode 100644 index b3b561b..0000000 --- a/test/error_capture_test.1.cpp +++ /dev/null @@ -1,183 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -template struct info { int value; }; - -leaf::error f() -{ - return leaf::error(info<1>{1},info<3>{3}); -} - -leaf::error_capture make_capture() -{ - leaf::expect,info<2>,info<3>> exp; - return capture(exp,f()); -} - -int main() -{ - leaf::error_capture ec = make_capture(); - - { - int c=0; - bool r = handle_error( ec, - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - c = 1; - } ); - BOOST_TEST(r); - BOOST_TEST(c==1); - } - - { - int c=0; - bool r = handle_error( ec, - [&c]( info<2> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==2); - c = 1; - } ); - BOOST_TEST(!r); - BOOST_TEST(c==0); - } - - { - int c=0; - int r = handle_error( ec, - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - return c = 1; - } ); - BOOST_TEST(r==1); - } - - { - int c=0; - int r = handle_error( ec, - [&c]( info<2> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==2); - return c = 1; - } ); - BOOST_TEST(r==-1); - } - - { - int c=0; - bool r = handle_error( ec, - [&c]( info<1> const & x, info<2> const & ) - { - BOOST_TEST(c==0); - c = 1; - }, - [&c]( info<1> const & x, info<3> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(y.value==3); - c = 2; - return false; - }, - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - c = 3; - } ); - BOOST_TEST(!r); - BOOST_TEST(c==2); - } - - { - int c=0; - bool r = handle_error( ec, - [&c]( info<1> const & x, info<2> const & ) - { - BOOST_TEST(c==0); - c = 1; - return false; - }, - [&c]( info<1> const & x, info<3> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(y.value==3); - c = 2; - }, - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - c = 3; - return false; - } ); - BOOST_TEST(r); - BOOST_TEST(c==2); - } - - { - leaf::expect,info<2>,info<3>> exp; - leaf::error e1( info<1>{-1}, info<2>{-2}, info<3>{-3} ); - leaf::error e2 = ec.unload(); - { - int c = 0; - bool r = handle_error( exp, e1, - [&c]( info<1>, info<2>, info<3> ) - { - BOOST_TEST(c==0); - c = 1; - }, - [&c]( info<1>, info<3> ) - { - BOOST_TEST(c==0); - c = 2; - }, - [&c]( info<2> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(y.value==-2); - c = 3; - } ); - BOOST_TEST(r); - BOOST_TEST(c==3); - } - { - int c = 0; - bool r = handle_error( exp, e2, - [&c]( info<1>, info<2>, info<3> ) - { - BOOST_TEST(c==0); - c = 1; - }, - [&c]( info<2> const & y ) - { - BOOST_TEST(c==0); - c = 2; - }, - [&c]( info<1> const & x, info<3> const & z ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(z.value==3); - c = 3; - } ); - BOOST_TEST(r); - BOOST_TEST(c==3); - } - } - - return boost::report_errors(); -} diff --git a/test/error_capture_test.2.cpp b/test/error_capture_test.2.cpp deleted file mode 100644 index 697b18d..0000000 --- a/test/error_capture_test.2.cpp +++ /dev/null @@ -1,76 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -int count = 0; - -template -struct info -{ - info() noexcept - { - ++count; - } - - info( info const & ) noexcept - { - ++count; - } - - ~info() noexcept - { - --count; - } -}; - -namespace boost { namespace leaf { - template struct is_error_type>: public std::true_type { }; -} } - -leaf::error f() -{ - return leaf::error(info<1>{},info<3>{}); -} - -leaf::error_capture make_capture() -{ - leaf::expect,info<2>,info<3>> exp; - return capture(exp,f()); -} - -int main() -{ - { - leaf::error_capture ec1 = make_capture(); - BOOST_TEST( handle_error( ec1, [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST(count==2); - leaf::error_capture ec2(ec1); - BOOST_TEST(count==2); - BOOST_TEST( handle_error( ec1, [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( handle_error( ec2, [ ]( info<1>, info<3> ) { } ) ); - leaf::error_capture ec3(std::move(ec2)); - BOOST_TEST(count==2); - BOOST_TEST( handle_error( ec1, [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( handle_error( ec3, [ ]( info<1>, info<3> ) { } ) ); - leaf::error_capture ec4; ec4 = ec3; - BOOST_TEST(count==2); - BOOST_TEST( handle_error( ec1, [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( handle_error( ec3, [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( handle_error( ec4, [ ]( info<1>, info<3> ) { } ) ); - leaf::error_capture ec5; ec5 = std::move(ec4); - BOOST_TEST(count==2); - BOOST_TEST( handle_error( ec1, [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( handle_error( ec3, [ ]( info<1>, info<3> ) { } ) ); - BOOST_TEST( handle_error( ec5, [ ]( info<1>, info<3> ) { } ) ); - } - BOOST_TEST(count==0); - return boost::report_errors(); -} diff --git a/test/error_fail_test.cpp b/test/error_fail_test.cpp deleted file mode 100644 index 813730d..0000000 --- a/test/error_fail_test.cpp +++ /dev/null @@ -1,17 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include - -namespace leaf = boost::leaf; - -struct no_member_value { }; - -leaf::error f() -{ - //Note: the line below should trigger a compile error (via static_assert). - return leaf::error( no_member_value{ } ); -} diff --git a/test/error_test.cpp b/test/error_test.cpp deleted file mode 100644 index c5b9dcf..0000000 --- a/test/error_test.cpp +++ /dev/null @@ -1,91 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include "boost/core/lightweight_test.hpp" -#include - -namespace leaf = boost::leaf; - -template -struct unexp -{ - int value; -}; - -template -struct info -{ - int value; -}; - -leaf::error f1() -{ - return LEAF_ERROR( info<1>{1}, unexp<1>{1}, unexp<2>{2} ); -} - -leaf::error f2() -{ - leaf::expect> exp; - return f1().propagate( info<2>{2} ); -} - -leaf::error f3() -{ - leaf::expect,info<3>,unexp<1>> exp; - leaf::error e = f2().propagate( info<4>{4} ); - BOOST_TEST(leaf::peek>(exp,e)->value==1); - return e; -} - -leaf::error f4() -{ - leaf::expect,info<2>,info<3>,info<4>> exp; - { - leaf::error e = f3(); - bool handled = handle_error( exp, e, - [ ]( info<1>, info<2>, info<3>, info<4> ){ }, - [ ]( info<1>, info<2>, info<4> ) { } ); - BOOST_TEST(handled); - } - leaf::error e = f3(); - int c1=0, c2=0; - bool handled = handle_error( exp, e, - [&c1]( info<1>,info<2>,info<3>,info<4> ) - { - ++c1; - }, - [&c2]( leaf::e_source_location const & loc, leaf::e_unexpected const & unx, info<1> const & i1, info<2> const & i2, info<4> const & i4 ) - { - BOOST_TEST(loc.line==27); - BOOST_TEST(strcmp(loc.file,__FILE__)==0); - BOOST_TEST(strstr(loc.function,"f1")!=0); - BOOST_TEST(unx.count==2); - BOOST_TEST(unx.first_type==&leaf::type>); - BOOST_TEST(i1.value==1); - BOOST_TEST(i2.value==2); - BOOST_TEST(i4.value==4); - ++c2; - } ); - BOOST_TEST(handled); - BOOST_TEST(c1==0); - BOOST_TEST(c2==1); - return leaf::error(); -} - -int main() -{ - leaf::expect,info<3>,info<4>> exp; - leaf::error e=f4(); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()==0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - return boost::report_errors(); -} diff --git a/test/exception_capture_test.cpp b/test/exception_capture_test.cpp deleted file mode 100644 index 53c1ab2..0000000 --- a/test/exception_capture_test.cpp +++ /dev/null @@ -1,100 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" -#include -#include -#include -#include - -namespace leaf = boost::leaf; - -struct my_error: std::exception { }; - -template struct info { int value; }; - -struct fut_info -{ - int a; - int b; - int result; - std::future fut; -}; - -template -std::vector launch_tasks( int task_count, F && f ) -{ - assert(task_count>0); - std::vector fut; - std::generate_n( std::inserter(fut,fut.end()), task_count, [&f] - { - int const a = rand(); - int const b = rand(); - int const res = (rand()%10) - 5; - return fut_info { a, b, res, std::async( std::launch::async, [f,a,b,res] - { - auto wrapper = leaf::capture_exception,info<2>,info<3>>(std::move(f)); - return wrapper( a, b, res ); - } ) }; - } ); - return fut; -} - -template -void test( int task_count, F && f ) noexcept -{ - std::vector fut = launch_tasks( task_count, std::forward(f) ); - for( auto & f : fut ) - { - using namespace leaf::leaf_detail; - f.fut.wait(); - leaf::expect,info<2>,info<4>> exp; - try - { - int r = leaf::get(f.fut); - BOOST_TEST(r>=0); - BOOST_TEST(r==f.result); - } - catch( my_error const & e ) - { - int c=0; - handle_exception( exp, e, [&f,&c]( info<1> const & x1, info<2> const & x2 ) - { - BOOST_TEST(x1.value==f.a); - BOOST_TEST(x2.value==f.b); - ++c; - } ); - BOOST_TEST(c==1); - } - } -} - -int main() -{ - test( 42, [ ]( int a, int b, int res ) - { - if( res>=0 ) - return res; - else - throw leaf::exception( my_error(), info<1>{a}, info<2>{b}, info<3>{} ); - } ); - - test( 42, [ ]( int a, int b, int res ) - { - if( res>=0 ) - return res; - else - { - auto propagate = leaf::preload( info<1>{a}, info<2>{b}, info<3>{} ); - throw my_error(); - } - } ); - - return boost::report_errors(); -} diff --git a/test/exception_test.1.cpp b/test/exception_test.1.cpp deleted file mode 100644 index 8938544..0000000 --- a/test/exception_test.1.cpp +++ /dev/null @@ -1,122 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" -#include - -namespace leaf = boost::leaf; - -struct my_error: std::exception { }; - -std::function thrower; - -template -struct info -{ - int value; -}; - -void f1() -{ - thrower(); -} - -void f2() -{ - leaf::expect> exp; - try - { - f1(); - BOOST_TEST(false); - } - catch( std::exception const & ex ) - { - leaf::get_error(ex).propagate( info<2>{2} ); - throw; - } -} - -void f3() -{ - leaf::expect,info<3>> exp; - auto propagate = leaf::preload( info<4>{4} ); - f2(); -} - -void f4() -{ - leaf::expect,info<2>,info<3>,info<4>> exp; - try - { - f3(); - BOOST_TEST(false); - } - catch( my_error const & e ) - { - int c1=0, c2=0; - handle_exception( exp, e, - [&c1]( info<1>, info<2>, info<3>, info<4> ) - { - ++c1; - }, - [&c2]( info<1> const & i1, info<2> const & i2, info<4> const & i4 ) - { - BOOST_TEST(i1.value==1); - BOOST_TEST(i2.value==2); - BOOST_TEST(i4.value==4); - ++c2; - } - ); - BOOST_TEST(c1==0); - BOOST_TEST(c2==1); - } - throw my_error(); -} - -void test( std::function const & f ) -{ - thrower = f; - leaf::expect,info<3>,info<4>> exp; - try - { - f4(); - BOOST_TEST(false); - } - catch( my_error const & e ) - { - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()==0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - } -} - -int main() -{ - test( [ ] - { - LEAF_THROW( my_error(), info<1>{1} ); - } ); - - test( [ ] - { - throw LEAF_EXCEPTION( my_error{}, info<1>{1} ); - } ); - - test( [ ] - { - auto propagate = leaf::preload(info<1>{1}); - throw my_error(); - } ); - - return boost::report_errors(); -} diff --git a/test/exception_test.2.cpp b/test/exception_test.2.cpp deleted file mode 100644 index 7db1403..0000000 --- a/test/exception_test.2.cpp +++ /dev/null @@ -1,122 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" -#include - -namespace leaf = boost::leaf; - -struct my_error: std::exception { }; - -std::function thrower; - -template -struct info -{ - int value; -}; - -void f1() -{ - thrower(); -} - -void f2() -{ - leaf::expect> exp; - try - { - f1(); - BOOST_TEST(false); - } - catch( std::exception const & ex ) - { - leaf::get_error(ex).propagate( info<2>{2} ); - throw; - } -} - -void f3() -{ - leaf::expect,info<3>> exp; - auto propagate = leaf::defer( [ ] { return info<4>{4}; } ); - f2(); -} - -void f4() -{ - leaf::expect,info<2>,info<3>,info<4>> exp; - try - { - f3(); - BOOST_TEST(false); - } - catch( my_error const & e ) - { - int c1=0, c2=0; - handle_exception( exp, e, - [&c1]( info<1>, info<2>, info<3>, info<4> ) - { - ++c1; - }, - [&c2]( info<1> const & i1, info<2> const & i2, info<4> const & i4 ) - { - BOOST_TEST(i1.value==1); - BOOST_TEST(i2.value==2); - BOOST_TEST(i4.value==4); - ++c2; - } - ); - BOOST_TEST(c1==0); - BOOST_TEST(c2==1); - } - throw my_error(); -} - -void test( std::function const & f ) -{ - thrower = f; - leaf::expect,info<3>,info<4>> exp; - try - { - f4(); - BOOST_TEST(false); - } - catch( my_error const & e ) - { - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()==0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - } -} - -int main() -{ - test( [ ] - { - throw leaf::exception( my_error(), info<1>{1} ); - } ); - - test( [ ] - { - LEAF_THROW( my_error(), info<1>{1} ); - } ); - - test( [ ] - { - auto propagate = leaf::defer( [ ] { return info<1>{1}; } ); - throw my_error(); - } ); - - return boost::report_errors(); -} diff --git a/test/expect_fail_test.1.cpp b/test/expect_fail_test.1.cpp deleted file mode 100644 index 9c8a913..0000000 --- a/test/expect_fail_test.1.cpp +++ /dev/null @@ -1,33 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include - -namespace leaf = boost::leaf; - -template struct info { int value; }; - -leaf::error f() -{ - return leaf::error(info<1>{1},info<3>{3}); -} - -int main() -{ - leaf::expect,info<2>,info<3>> exp; - - (void) handle_error( exp, f(), - [ ]( info<1>, info<2> ) - { - return 42; - }, - [ ]( info<1>, info<3> ) - { - return 42.0f; - } ); - - return 0; -} diff --git a/test/expect_fail_test.2.cpp b/test/expect_fail_test.2.cpp deleted file mode 100644 index 40b42cb..0000000 --- a/test/expect_fail_test.2.cpp +++ /dev/null @@ -1,35 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include - -namespace leaf = boost::leaf; - -template struct info { int value; }; - -struct test { }; - -leaf::error f() -{ - return leaf::error(info<1>{1},info<3>{3}); -} - -int main() -{ - leaf::expect,info<2>,info<3>> exp; - - (void) handle_error( exp, f(), - [ ]( info<1>, info<2> ) - { - return test { }; - }, - [ ]( info<1>, info<3> ) - { - return 42; - } ); - - return 0; -} diff --git a/test/expect_test.1.cpp b/test/expect_test.1.cpp deleted file mode 100644 index 2905a68..0000000 --- a/test/expect_test.1.cpp +++ /dev/null @@ -1,36 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info { int value; }; - -leaf::error g() -{ - leaf::expect exp; - return leaf::error(); -} - -leaf::error f() -{ - return g(); -} - -int main() -{ - leaf::expect exp; - int c=0; - BOOST_TEST( !handle_error( exp, f(), - [&c]( info ) - { - ++c; - } ) ); - BOOST_TEST(c==0); - return boost::report_errors(); -} diff --git a/test/expect_test.2.cpp b/test/expect_test.2.cpp deleted file mode 100644 index 61f8515..0000000 --- a/test/expect_test.2.cpp +++ /dev/null @@ -1,55 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info { int value; }; - -struct my_error: std::exception { }; - -void g() -{ - leaf::expect exp; - throw my_error(); -} - -void f() -{ - return g(); -} - -int main() -{ - leaf::expect exp; - try - { - f(); - BOOST_TEST(false); - } - catch( my_error & e ) - { - int c=0; - try - { - handle_exception( exp, e, - [&c]( info ) - { - ++c; - } ); - BOOST_TEST(false); - } - catch( my_error & ) - { - } - BOOST_TEST(c==0); - } - - return boost::report_errors(); -} diff --git a/test/expect_test.3.cpp b/test/expect_test.3.cpp deleted file mode 100644 index 92d1374..0000000 --- a/test/expect_test.3.cpp +++ /dev/null @@ -1,55 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info { int value; }; - -struct my_error: std::exception { }; - -void g() -{ - leaf::expect exp; - throw leaf::exception(my_error()); -} - -void f() -{ - return g(); -} - -int main() -{ - leaf::expect exp; - try - { - f(); - BOOST_TEST(false); - } - catch( my_error & e ) - { - int c=0; - try - { - handle_exception( exp, e, - [&c]( info ) - { - ++c; - } ); - BOOST_TEST(false); - } - catch( my_error & ) - { - } - BOOST_TEST(c==0); - } - - return boost::report_errors(); -} diff --git a/test/expect_test.4.cpp b/test/expect_test.4.cpp deleted file mode 100644 index 3184979..0000000 --- a/test/expect_test.4.cpp +++ /dev/null @@ -1,124 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -class MStatus { }; - -namespace boost { namespace leaf { - template<> struct is_error_type: std::true_type { }; -} } - -int main() -{ - leaf::expect exp; - MStatus ms; - { - MStatus copy = ms; - { - int c=0; - bool handled = leaf::handle_error( exp, leaf::error(copy), - [&c]( MStatus ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - { - int c=0; - bool handled = leaf::handle_error( exp, leaf::error(copy), - [&c]( MStatus const ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - { - int c=0; - bool handled = leaf::handle_error( exp, leaf::error(copy), - [&c]( MStatus const & ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - } - { - MStatus & ref = ms; - { - int c=0; - bool handled = leaf::handle_error( exp, leaf::error(ref), - [&c]( MStatus ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - { - int c=0; - bool handled = leaf::handle_error( exp, leaf::error(ref), - [&c]( MStatus const ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - { - int c=0; - bool handled = leaf::handle_error( exp, leaf::error(ref), - [&c]( MStatus const & ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - } - { - { - MStatus const & cref = ms; - int c=0; - bool handled = leaf::handle_error( exp, leaf::error(cref), - [&c]( MStatus ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - { - MStatus const & cref = ms; - int c=0; - bool handled = leaf::handle_error( exp, leaf::error(cref), - [&c]( MStatus const ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - { - MStatus const & cref = ms; - int c=0; - bool handled = leaf::handle_error( exp, leaf::error(cref), - [&c]( MStatus const & ms ) - { - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - } - return boost::report_errors(); -} diff --git a/test/expect_test.5.cpp b/test/expect_test.5.cpp deleted file mode 100644 index 47681a1..0000000 --- a/test/expect_test.5.cpp +++ /dev/null @@ -1,125 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -template struct info { int value; }; - -leaf::error f() -{ - return leaf::error(info<1>{1},info<3>{3}); -} - -int main() -{ - leaf::expect,info<2>,info<3>> exp; - - { - int c=0; - bool r = handle_error( exp, f(), - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - c = 1; - } ); - BOOST_TEST(r); - BOOST_TEST(c==1); - } - - { - int c=0; - bool r = handle_error( exp, f(), - [&c]( info<2> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==2); - c = 1; - } ); - BOOST_TEST(!r); - BOOST_TEST(c==0); - } - - { - int c=0; - int r = handle_error( exp, f(), - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - return c = 1; - } ); - BOOST_TEST(r==1); - } - - { - int c=0; - int r = handle_error( exp, f(), - [&c]( info<2> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==2); - return c = 1; - } ); - BOOST_TEST(r==-1); - } - - { - int c=0; - bool r = handle_error( exp, f(), - [&c]( info<1> const & x, info<2> const & ) - { - BOOST_TEST(c==0); - c = 1; - }, - [&c]( info<1> const & x, info<3> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(y.value==3); - c = 2; - return false; - }, - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - c = 3; - } ); - BOOST_TEST(!r); - BOOST_TEST(c==2); - } - - { - int c=0; - bool r = handle_error( exp, f(), - [&c]( info<1> const & x, info<2> const & ) - { - BOOST_TEST(c==0); - c = 1; - return false; - }, - [&c]( info<1> const & x, info<3> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(y.value==3); - c = 2; - }, - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - c = 3; - return false; - } ); - BOOST_TEST(r); - BOOST_TEST(c==2); - } - - return boost::report_errors(); -} diff --git a/test/function_traits_test.cpp b/test/function_traits_test.cpp index 83c9e70..07f1991 100644 --- a/test/function_traits_test.cpp +++ b/test/function_traits_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 @@ -11,16 +11,13 @@ template void check_traits( F ) { using namespace boost::leaf::leaf_detail; + using boost::leaf::leaf_detail_mp11::mp_list; static_assert(function_traits::arity==4,"arity"); static_assert(std::is_same::return_type,double>::value,"return_type"); static_assert(std::is_same::template arg<0>::type,int>::value,"arg<0>"); - static_assert(std::is_same::template arg<0>::type_,int>::value,"arg<0>"); static_assert(std::is_same::template arg<1>::type,float>::value,"arg<1>"); - static_assert(std::is_same::template arg<1>::type_,float>::value,"arg<1>"); static_assert(std::is_same::template arg<2>::type,int const &>::value,"arg<2>"); - static_assert(std::is_same::template arg<2>::type_,int>::value,"arg<2>"); static_assert(std::is_same::template arg<3>::type,float &&>::value,"arg<3>"); - static_assert(std::is_same::template arg<3>::type_,float>::value,"arg<3>"); static_assert(std::is_same::mp_args,mp_list>::value,"mp_args"); } diff --git a/test/handle_all_test.cpp b/test/handle_all_test.cpp new file mode 100644 index 0000000..aa0d3a0 --- /dev/null +++ b/test/handle_all_test.cpp @@ -0,0 +1,350 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +enum class error_code +{ + ok, + error1, + error2, + error3 +}; +namespace boost { namespace leaf { + template <> struct is_error_type: std::true_type { }; +} } + +struct error_codes_ { error_code value; }; + +template +leaf::result f( error_code ec ) +{ + if( ec==error_code::ok ) + return R(42); + else + return leaf::new_error(ec,error_codes_{ec},info<1>{1},info<2>{2},info<3>{3}); +} + +int main() +{ + // void, handle_all (success) + { + int c=0; + leaf::handle_all( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::ok)); + c = answer; + return { }; + }, + [&c] + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(c==42); + } + + // void, handle_all (failure) + { + int c=0; + leaf::handle_all( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==1); + } + + // void, handle_all (failure), match enum (single enum value) + { + int c=0; + leaf::handle_all( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + // void, handle_all (failure), match enum (multiple enum values) + { + int c=0; + leaf::handle_all( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + // void, handle_all (failure), match value (single value) + { + int c=0; + leaf::handle_all( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + // void, handle_all (failure), match value (multiple values) + { + int c=0; + leaf::handle_all( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + ////////////////////////////////////// + + // int, handle_all (success) + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::ok)); + return answer; + }, + [ ] + { + return 1; + } ); + BOOST_TEST(r==42); + } + + // int, handle_all (failure) + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + } + + // int, handle_all (failure), match enum (single enum value) + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + // int, handle_all (failure), match enum (multiple enum values) + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + // int, handle_all (failure), match value (single value) + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + // int, handle_all (failure), match value (multiple values) + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + return boost::report_errors(); +} diff --git a/test/handle_some_basic_test.cpp b/test/handle_some_basic_test.cpp new file mode 100644 index 0000000..f3a3439 --- /dev/null +++ b/test/handle_some_basic_test.cpp @@ -0,0 +1,148 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +enum class error_code +{ + error1=1, + error2, + error3 +}; + +namespace boost { namespace leaf { + template <> struct is_error_type: public std::true_type { }; +} } + +leaf::result compute_answer( int what_to_do ) noexcept +{ + switch( what_to_do ) + { + case 0: + return 42; + case 1: + return leaf::new_error(error_code::error1); + case 2: + return leaf::new_error(error_code::error2); + default: + assert(what_to_do==3); + return leaf::new_error(error_code::error3); + } +} + +leaf::result handle_some_errors( int what_to_do ) +{ + return leaf::handle_some( + [=] + { + return compute_answer(what_to_do); + }, + [ ]( leaf::match ) + { + return -42; + } ); +} + +leaf::result handle_some_errors_float( int what_to_do ) +{ + return leaf::handle_some( + [=]() -> leaf::result + { + return compute_answer(what_to_do); + }, + [ ]( leaf::match ) + { + return -42.0f; + } ); +} + +leaf::result handle_some_errors_void( int what_to_do ) +{ + return leaf::handle_some( + [=]() -> leaf::result + { + LEAF_AUTO(answer, compute_answer(what_to_do)); + (void) answer; + return { }; + }, + [ ]( leaf::match ) + { + } ); +} + +int main() +{ + BOOST_TEST(handle_some_errors(0).value()==42); + BOOST_TEST(handle_some_errors(1).value()==-42); + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer,handle_some_errors(3)); + (void) answer; + return 0; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + } + + /////////////////////////// + + BOOST_TEST(handle_some_errors_float(0).value()==42.0f); + BOOST_TEST(handle_some_errors_float(2).value()==-42.0f); + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_AUTO(answer,handle_some_errors_float(1)); + (void) answer; + return 0; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + } + + /////////////////////////// + + BOOST_TEST(handle_some_errors_void(0)); + BOOST_TEST(handle_some_errors_void(3)); + { + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_CHECK(handle_some_errors_void(2)); + return 0; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + } + + return boost::report_errors(); +} diff --git a/test/handle_some_test.cpp b/test/handle_some_test.cpp new file mode 100644 index 0000000..3ccea70 --- /dev/null +++ b/test/handle_some_test.cpp @@ -0,0 +1,1013 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +enum class error_code +{ + ok, + error1, + error2, + error3 +}; +namespace boost { namespace leaf { + template <> struct is_error_type: std::true_type { }; +} } + +struct error_codes_ { error_code value; }; + +template +leaf::result f( error_code ec ) +{ + if( ec==error_code::ok ) + return R(42); + else + return leaf::new_error(ec,error_codes_{ec},info<1>{1},info<2>{2},info<3>{3}); +} + +int main() +{ + + // void, handle_some (success) + { + int c=0; + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::ok)); + c = answer; + return { }; + }, + [&c]( leaf::error_info const & unmatched ) + { + BOOST_TEST(c==0); + c = 1; + return unmatched.error(); + } ); + BOOST_TEST(r); + BOOST_TEST(c==42); + } + + // void, handle_some (failure, matched) + { + int c=0; + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(c==1); + BOOST_TEST(r); + } + + // void, handle_some (failure, matched), match enum (single enum value) + { + int c=0; + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==2); + BOOST_TEST(r); + } + + // void, handle_some (failure, matched), match enum (multiple enum values) + { + int c=0; + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==2); + BOOST_TEST(r); + } + + // void, handle_some (failure, matched), match value (single value) + { + int c=0; + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==2); + BOOST_TEST(r); + } + + // void, handle_some (failure, matched), match value (multiple values) + { + int c=0; + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + } ); + BOOST_TEST(c==2); + BOOST_TEST(r); + } + + // void, handle_some (failure, initially not matched) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( info<4> ) + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST(c==0); + return r; + }, + [&c]( error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + // void, handle_some (failure, initially not matched), match enum (single enum value) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST(c==0); + return r; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + // void, handle_some (failure, initially not matched), match enum (multiple enum values) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST(c==0); + return r; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + // void, handle_some (failure, initially not matched), match value (single value) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST(c==0); + return r; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + // void, handle_some (failure, initially not matched), match value (multiple values) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(!r); + BOOST_TEST(c==0); + return r; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + // void, handle_some (failure, initially matched) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST(c==1); + return r; + }, + [&c]( info<4> ) + { + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==1); + } + + // void, handle_some (failure, initially matched), match enum (single enum value) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST(c==1); + return r; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==1); + } + + // void, handle_some (failure, initially matched), match enum (multiple enum values) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST(c==1); + return r; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==1); + } + + // void, handle_some (failure, initially matched), match value (single value) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST(c==1); + return r; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==1); + } + + // void, handle_some (failure, initially matched), match value (multiple values) + { + int c=0; + leaf::handle_all( + [&c] + { + leaf::result r = leaf::handle_some( + [&c]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + c = answer; + return { }; + }, + [&c]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(r); + BOOST_TEST(c==1); + return r; + }, + [&c]( leaf::match ) + { + BOOST_TEST(c==0); + c = 2; + }, + [&c]() + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==1); + } + + ////////////////////////////////////// + + // int, handle_some (success) + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::ok)); + return answer; + }, + [ ]( leaf::error_info const & unmatched ) + { + return unmatched.error(); + } ); + BOOST_TEST(r && *r==42); + } + + // int, handle_some (failure, matched) + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r && *r==1); + } + + // int, handle_some (failure, matched), match enum (single enum value) + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + } ); + BOOST_TEST(r && *r==2); + } + + // int, handle_some (failure, matched), match enum (multiple enum values) + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + } ); + BOOST_TEST(r && *r==2); + } + + // int, handle_some (failure, matched), match value (single value) + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + } ); + BOOST_TEST(r && *r==2); + } + + // int, handle_some (failure, matched), match value (multiple values) + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + } ); + BOOST_TEST(r && *r==2); + } + + // int, handle_some (failure, initially not matched) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( info<4> ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + [ ]( error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + // int, handle_some (failure, initially not matched), match enum (single enum value) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + // int, handle_some (failure, initially not matched), match enum (multiple enum values) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + // int, handle_some (failure, initially not matched), match value (single value) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + // int, handle_some (failure, initially not matched), match value (multiple values) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ) + { + return 1; + } ); + BOOST_TEST(!r); + return r; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + // int, handle_some (failure, initially matched) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( error_code ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + [ ]( info<4> ) + { + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==1); + } + + // int, handle_some (failure, initially matched), match enum (single enum value) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + [ ]( leaf::match ) + { + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==1); + } + + // int, handle_some (failure, initially matched), match enum (multiple enum values) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + [ ]( leaf::match ) + { + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==1); + } + + // int, handle_some (failure, initially matched), match value (single value) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + [ ]( leaf::match ) + { + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==1); + } + + // int, handle_some (failure, initially matched), match value (multiple values) + { + int r = leaf::handle_all( + [ ] + { + leaf::result r = leaf::handle_some( + [ ]() -> leaf::result + { + LEAF_AUTO(answer, f(error_code::error1)); + return answer; + }, + [ ]( leaf::match ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(ec.value==error_code::error1); + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r); + return r; + }, + [ ]( leaf::match ) + { + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==1); + } + + return boost::report_errors(); +} diff --git a/test/is_error_type_fail_test.cpp b/test/is_error_type_fail_test.cpp new file mode 100644 index 0000000..2c2c0e6 --- /dev/null +++ b/test/is_error_type_fail_test.cpp @@ -0,0 +1,17 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +namespace leaf = boost::leaf; + +struct no_member_value { }; + +leaf::error_id f() +{ + // Note: the line below should trigger a compile error (via static_assert). + return leaf::new_error( no_member_value{ } ); +} diff --git a/test/is_error_type_test.cpp b/test/is_error_type_test.cpp index b44ab1b..424f458 100644 --- a/test/is_error_type_test.cpp +++ b/test/is_error_type_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 diff --git a/test/multiple_errors_test.cpp b/test/multiple_errors_test.cpp index 7e057b6..4ab2f50 100644 --- a/test/multiple_errors_test.cpp +++ b/test/multiple_errors_test.cpp @@ -1,10 +1,10 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 "boost/core/lightweight_test.hpp" namespace leaf = boost::leaf; @@ -15,24 +15,24 @@ struct info int value; }; -leaf::error f12() +leaf::error_id f12() { - return leaf::error( info<1>{1}, info<2>{2} ); + return leaf::new_error( info<1>{1}, info<2>{2} ); } -leaf::error f34() +leaf::error_id f34() { - return leaf::error( info<3>{3}, info<4>{4} ); + return leaf::new_error( info<3>{3}, info<4>{4} ); } int main() { - leaf::expect,info<2>,info<3>,info<4>> exp; - leaf::error e1=f12(); - leaf::error e2=f34(); + leaf::static_store,info<2>,info<3>,info<4>> exp; + leaf::error_id e1=f12(); + leaf::error_id e2=f34(); { int e1c1=0, e1c2=0; - bool handled = handle_error( exp, e1, + bool handled = exp.handle_error( e1, [&e1c1]( info<3>, info<4> ) { ++e1c1; @@ -49,7 +49,7 @@ int main() } { int e2c1=0, e2c2=0; - bool handled = handle_error( exp, e2, + bool handled = exp.handle_error( e2, [&e2c1]( info<1>, info<2> ) { ++e2c1; diff --git a/test/optional_test.cpp b/test/optional_test.cpp index 7cd5a6b..fda9c9b 100644 --- a/test/optional_test.cpp +++ b/test/optional_test.cpp @@ -1,15 +1,14 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 "boost/core/lightweight_test.hpp" namespace leaf = boost::leaf; -struct error { }; int object_count=0; int value_count=0; @@ -64,7 +63,7 @@ public: throws_on_copy( throws_on_copy const & ) { - throw error(); + throw std::exception(); } throws_on_copy( throws_on_copy && ) @@ -107,7 +106,7 @@ void run_tests() optional x(a); BOOST_TEST(false); } - catch( error & ) + catch( std::exception & ) { } } @@ -201,7 +200,7 @@ void run_tests() { (void) (y=x); } - catch( error & ) + catch( std::exception & ) { } BOOST_TEST(object_count==1); diff --git a/test/preload_basic_test.cpp b/test/preload_basic_test.cpp new file mode 100644 index 0000000..f31e2c3 --- /dev/null +++ b/test/preload_basic_test.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info +{ + int value; +}; + +leaf::error_id g() +{ + auto propagate = leaf::preload( info{42} ); + return leaf::new_error(); +} + +leaf::error_id f() +{ + return g(); +} + +int main() +{ + int r = leaf::handle_all( + [ ]() -> leaf::result + { + return f(); + }, + [ ]( info const & i42 ) + { + BOOST_TEST(i42.value==42); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + return boost::report_errors(); +} diff --git a/test/preload_nested_error_exception_test.cpp b/test/preload_nested_error_exception_test.cpp new file mode 100644 index 0000000..4ed2f24 --- /dev/null +++ b/test/preload_nested_error_exception_test.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +void f0() +{ + auto propagate = leaf::preload( info<0>{0} ); + throw leaf::exception(std::exception(), info<2>{2} ); +} + +void f1() +{ + auto propagate = leaf::preload( info<0>{-1}, info<1>{1}, info<2>{-1} ); + f0(); +} + +void f2() +{ + try + { + f1(); + } + catch( leaf::error_id id ) + { + id.propagate( info<3>{3} ); + throw; + } +} + +int main() +{ + int r = leaf::try_( + [ ] + { + f2(); + return 0; + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST(i0.value==0); + BOOST_TEST(i1.value==1); + BOOST_TEST(i2.value==2); + BOOST_TEST(i3.value==3); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + + return boost::report_errors(); +} diff --git a/test/preload_nested_error_result_test.cpp b/test/preload_nested_error_result_test.cpp new file mode 100644 index 0000000..3f229c3 --- /dev/null +++ b/test/preload_nested_error_result_test.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +leaf::error_id f0() +{ + auto propagate = leaf::preload( info<0>{0} ); + return leaf::new_error( info<2>{2} ); +} + +leaf::error_id f1() +{ + auto propagate = leaf::preload( info<0>{-1}, info<1>{1}, info<2>{-1} ); + return f0(); +} + +leaf::error_id f2() +{ + return f1().propagate( info<3>{3} ); +} + +int main() +{ + int r = leaf::handle_all( + [ ]() -> leaf::result + { + return f2(); + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST(i0.value==0); + BOOST_TEST(i1.value==1); + BOOST_TEST(i2.value==2); + BOOST_TEST(i3.value==3); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + + return boost::report_errors(); +} diff --git a/test/preload_nested_new_error_exception_test.cpp b/test/preload_nested_new_error_exception_test.cpp new file mode 100644 index 0000000..ce733b1 --- /dev/null +++ b/test/preload_nested_new_error_exception_test.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +void f0() +{ + auto propagate = leaf::preload( info<0>{-1} ); + throw leaf::exception( std::exception(), info<1>{-1} ); +} + +void f1() +{ + auto propagate = leaf::preload( info<0>{0}, info<1>{1}, info<2>{2} ); + try { f0(); } catch(...) { } + throw leaf::exception(std::exception()); +} + +leaf::error_id f2() +{ + try + { + f1(); + BOOST_TEST(false); + } + catch( leaf::error_id id ) + { + id.propagate( info<3>{3} ); + throw; + } + catch(...) + { + BOOST_TEST(false); + } + return leaf::new_error(); +} + +int main() +{ + int r = leaf::try_( + [ ] + { + f2(); + return 0; + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST(i0.value==0); + BOOST_TEST(i1.value==1); + BOOST_TEST(i2.value==2); + BOOST_TEST(i3.value==3); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + + return boost::report_errors(); +} diff --git a/test/preload_nested_new_error_result_test.cpp b/test/preload_nested_new_error_result_test.cpp new file mode 100644 index 0000000..97e8235 --- /dev/null +++ b/test/preload_nested_new_error_result_test.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +leaf::error_id f0() +{ + auto propagate = leaf::preload( info<0>{-1} ); + return leaf::new_error( info<1>{-1} ); +} + +leaf::error_id f1() +{ + auto propagate = leaf::preload( info<0>{0}, info<1>{1}, info<2>{2} ); + (void) f0(); + return leaf::new_error(); +} + +leaf::error_id f2() +{ + return f1().propagate( info<3>{3} ); +} + +int main() +{ + int r = leaf::handle_all( + [ ]() -> leaf::result + { + return f2(); + }, + [ ]( info<0> i0, info<1> i1, info<2> i2, info<3> i3 ) + { + BOOST_TEST(i0.value==0); + BOOST_TEST(i1.value==1); + BOOST_TEST(i2.value==2); + BOOST_TEST(i3.value==3); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + + return boost::report_errors(); +} diff --git a/test/preload_nested_success_exception_test.cpp b/test/preload_nested_success_exception_test.cpp new file mode 100644 index 0000000..4ff1f19 --- /dev/null +++ b/test/preload_nested_success_exception_test.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +void g1() +{ + auto propagate = leaf::preload( info{1} ); +} + +void g2() +{ + throw std::exception(); +} + +void f() +{ + auto propagate = leaf::preload( info{2} ); + g1(); + g2(); +} + +int main() +{ + int r = leaf::try_( + [ ] + { + f(); + return 0; + }, + [ ]( info x ) + { + BOOST_TEST(x.value==2); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + + return boost::report_errors(); +} diff --git a/test/preload_nested_success_result_test.cpp b/test/preload_nested_success_result_test.cpp new file mode 100644 index 0000000..5a64739 --- /dev/null +++ b/test/preload_nested_success_result_test.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +leaf::result g1() +{ + auto propagate = leaf::preload( info{1} ); + return { }; +} + +leaf::result g2() +{ + return leaf::new_error(); +} + +leaf::result f() +{ + auto propagate = leaf::preload( info{2} ); + LEAF_CHECK(g1()); + return g2(); +} + +int main() +{ + int r = leaf::handle_all( + [ ]() -> leaf::result + { + LEAF_CHECK(f()); + return 1; + }, + [ ]( info x ) + { + BOOST_TEST(x.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + + return boost::report_errors(); +} diff --git a/test/preload_test.1.cpp b/test/preload_test.1.cpp deleted file mode 100644 index 4c90b00..0000000 --- a/test/preload_test.1.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -template -struct info -{ - int value; -}; - -leaf::error f0() -{ - auto propagate = leaf::preload( info<0>{0} ); - return leaf::error( info<2>{2} ); -} - -leaf::error f1() -{ - auto propagate = leaf::preload( info<0>{-1}, info<1>{1}, info<2>{-1} ); - return f0(); -} - -leaf::error f2() -{ - return f1().propagate( info<4>{4} ); -} - -int main() -{ - leaf::expect,info<1>,info<2>,info<3>,info<4>> exp; - leaf::error e = f2(); - BOOST_TEST(!leaf::peek>(exp,e)); - int c=0; - bool handled = handle_error( exp, e, - [&c]( info<0> const & i0, info<1> const & i1, info<2> const & i2, info<4> const & i4 ) - { - BOOST_TEST(i0.value==0); - BOOST_TEST(i1.value==1); - BOOST_TEST(i2.value==2); - BOOST_TEST(i4.value==4); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - return boost::report_errors(); -} diff --git a/test/preload_test.2.cpp b/test/preload_test.2.cpp deleted file mode 100644 index 9e43ea7..0000000 --- a/test/preload_test.2.cpp +++ /dev/null @@ -1,49 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info { int value; }; - -leaf::result g1() -{ - auto propagate = leaf::preload( info{1} ); - return { }; -} - -leaf::result g2() -{ - return leaf::error(); -} - -leaf::result f() -{ - auto propagate = leaf::preload( info{2} ); - LEAF_CHECK(g1()); - return g2(); -} - -int -main() -{ - leaf::expect exp; - leaf::result r = f(); - int c=0; - bool handled = handle_error( exp, r, - [&c]( info const & x ) - { - BOOST_TEST(x.value==2); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - return boost::report_errors(); -} diff --git a/test/preload_test.3.cpp b/test/preload_test.3.cpp deleted file mode 100644 index 04a9c16..0000000 --- a/test/preload_test.3.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct my_error: std::exception { }; - -struct info { int value; }; - -void g1() -{ - auto propagate = leaf::preload( info{1} ); -} - -void g2() -{ - throw my_error(); -} - -void f() -{ - auto propagate = leaf::preload( info{2} ); - g1(); - g2(); -} - -int main() -{ - leaf::expect exp; - try - { - f(); - } - catch( my_error & e ) - { - int c=0; - handle_exception( exp, e, - [&c]( info const & x ) - { - BOOST_TEST(x.value==2); - ++c; - } ); - BOOST_TEST(c==1); - } - return boost::report_errors(); -} diff --git a/test/preload_test.4.cpp b/test/preload_test.4.cpp deleted file mode 100644 index d2939f3..0000000 --- a/test/preload_test.4.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct my_error: std::exception { }; - -struct info { int value; }; - -void g1() -{ - auto propagate = leaf::preload( info{1} ); -} - -void g2() -{ - throw leaf::exception(my_error()); -} - -void f() -{ - auto propagate = leaf::preload( info{2} ); - g1(); - g2(); -} - -int main() -{ - leaf::expect exp; - try - { - f(); - } - catch( my_error & e ) - { - int c=0; - handle_exception( exp, e, - [&c]( info const & x ) - { - BOOST_TEST(x.value==2); - ++c; - } ); - BOOST_TEST(c==1); - } - return boost::report_errors(); -} diff --git a/test/preload_test.5.cpp b/test/preload_test.5.cpp deleted file mode 100644 index a9fa9d1..0000000 --- a/test/preload_test.5.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -template -struct info -{ - int value; -}; - -leaf::error f0() -{ - auto propagate = leaf::preload( info<0>{-1} ); - return leaf::error( info<1>{-1} ); -} - -leaf::error f1() -{ - auto propagate = leaf::preload( info<0>{0}, info<1>{1}, info<2>{2} ); - (void) f0(); - return leaf::error(); -} - -leaf::error f2() -{ - return f1().propagate( info<3>{3} ); -} - -int main() -{ - leaf::expect,info<1>,info<2>,info<3>> exp; - leaf::error e = f2(); - int c=0; - bool handled = handle_error( exp, e, - [&c]( info<0> const & i0, info<1> const & i1, info<2> const & i2, info<3> const & i3 ) - { - BOOST_TEST(i0.value==0); - BOOST_TEST(i1.value==1); - BOOST_TEST(i2.value==2); - BOOST_TEST(i3.value==3); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - return boost::report_errors(); -} diff --git a/test/preload_test.6.cpp b/test/preload_test.6.cpp deleted file mode 100644 index e935b60..0000000 --- a/test/preload_test.6.cpp +++ /dev/null @@ -1,44 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info -{ - int value; -}; - -leaf::error f0() -{ - return leaf::error(); -} - -leaf::error f1() -{ - auto propagate = leaf::preload( info{0} ); - (void) f0(); - return leaf::error(); -} - -int main() -{ - leaf::expect exp; - leaf::error e = f1(); - int c=0; - bool handled = handle_error( exp, e, - [&c]( info const & i0 ) - { - BOOST_TEST(i0.value==0); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - return boost::report_errors(); -} diff --git a/test/preload_test.7.cpp b/test/preload_test.7.cpp deleted file mode 100644 index 13cc6f5..0000000 --- a/test/preload_test.7.cpp +++ /dev/null @@ -1,45 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct info -{ - int value; -}; - -leaf::error f0() -{ - return leaf::error(); -} - -leaf::error f1() -{ - auto propagate = leaf::preload( info{42} ); - return f0(); -} - -int main() -{ - leaf::expect exp; - leaf::error e = f1(); - int c=0; - bool handled = handle_error( exp, e, - [&c]( leaf::e_unexpected const & unx, leaf::e_unexpected_diagnostic_output const & unxdo ) - { - BOOST_TEST(unx.count==1); - BOOST_TEST(unx.first_type==&leaf::type); - BOOST_TEST(unxdo.value.find(": 42")!=std::string::npos); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - return boost::report_errors(); -} diff --git a/test/print_test.cpp b/test/print_test.cpp index 3bd2f9b..867b8d0 100644 --- a/test/print_test.cpp +++ b/test/print_test.cpp @@ -1,8 +1,8 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// 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 "boost/core/lightweight_test.hpp" diff --git a/test/result_bad_result_test.cpp b/test/result_bad_result_test.cpp new file mode 100644 index 0000000..2b63db8 --- /dev/null +++ b/test/result_bad_result_test.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +int check( leaf::catch_, leaf::e_source_location const & x ) +{ + BOOST_TEST(strstr(x.file,"result.hpp")!=0); + BOOST_TEST(x.line>0); + BOOST_TEST(strstr(x.function,"value")!=0); + return 1; +} + +int main() +{ + { + int r = leaf::try_( + [ ] + { + leaf::result r = leaf::new_error(); + (void) r.value(); + return 0; + }, + check ); + BOOST_TEST(r==1); + } + { + int r = leaf::try_( + [ ] + { + leaf::result const r = leaf::new_error(); + (void) r.value(); + return 0; + }, + check ); + BOOST_TEST(r==1); + } + { + int r = leaf::try_( + [ ] + { + leaf::result r = leaf::new_error(); + (void) *r; + return 0; + }, + check ); + BOOST_TEST(r==1); + } + { + int r = leaf::try_( + [ ] + { + leaf::result const r = leaf::new_error(); + (void) *r; + return 0; + }, + check ); + BOOST_TEST(r==1); + } + return boost::report_errors(); +} diff --git a/test/result_capture_test.cpp b/test/result_capture_test.cpp deleted file mode 100644 index 6803361..0000000 --- a/test/result_capture_test.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" -#include -#include -#include -#include - -namespace leaf = boost::leaf; - -template struct info { int value; }; - -struct fut_info -{ - int a; - int b; - int result; - std::future> fut; -}; - -template -std::vector launch_tasks( int task_count, F f ) -{ - assert(task_count>0); - std::vector fut; - std::generate_n( std::inserter(fut,fut.end()), task_count, [f] - { - int const a = rand(); - int const b = rand(); - int const res = (rand()%10) - 5; - return fut_info { a, b, res, std::async( std::launch::async, [f,a,b,res] - { - leaf::expect,info<2>,info<3>> exp; - return capture(exp,f(a,b,res)); - } ) }; - } ); - return fut; -} - -int main() -{ - std::vector fut = launch_tasks( 42, [ ]( int a, int b, int res ) noexcept -> leaf::result - { - if( res>=0 ) - return res; - else - return leaf::error(info<1>{a},info<2>{b},info<3>{}); - } ); - for( auto & f : fut ) - { - using namespace leaf::leaf_detail; - f.fut.wait(); - leaf::expect,info<2>,info<4>> exp; - if( leaf::result r = f.fut.get() ) - { - BOOST_TEST(*r>=0); - BOOST_TEST(*r==f.result); - } - else - { - int c=0; - bool handled = handle_error( exp, r, [&f,&c]( info<1> const & x1, info<2> const & x2 ) - { - BOOST_TEST(x1.value==f.a); - BOOST_TEST(x2.value==f.b); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - } - return boost::report_errors(); -} diff --git a/test/result_state_test.cpp b/test/result_state_test.cpp new file mode 100644 index 0000000..488b849 --- /dev/null +++ b/test/result_state_test.cpp @@ -0,0 +1,846 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct val +{ + static int id_count; + static int count; + int id; + + val(): + id(++id_count) + { + ++count; + } + + val( val const & x ): + id(x.id) + { + ++count; + } + + val( val && x ): + id(x.id) + { + ++count; + } + + ~val() + { + --count; + } + + friend bool operator==( val const & a, val const & b ) + { + return a.id==b.id; + } +}; +int val::count = 0; +int val::id_count = 0; + +struct err +{ + static int count; + + err() + { + ++count; + } + + err( err const & ) + { + ++count; + } + + err( err && ) + { + ++count; + } + + ~err() + { + --count; + } +}; +int err::count = 0; +struct e_err { err value; }; + +bool eq_value( leaf::result & r1, leaf::result & r2 ) +{ + leaf::result const & cr1 = r1; + leaf::result const & cr2 = r2; + return + r1.value()==r2.value() && + cr1.value()==cr2.value() && + *r1==*r2 && + *cr1==*cr2; +} + +int main() +{ + using leaf::leaf_detail::static_store; + using leaf::capture_result; + + { // value default -> copy + leaf::result r1; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value move -> copy + leaf::result r1 = val(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value copy -> copy + val v; + leaf::result r1 = v; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + leaf::result r2 = r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==3); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { // value default -> move + leaf::result r1; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value move -> move + leaf::result r1 = val(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value copy -> move + val v; + leaf::result r1 = v; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==3); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { // value default -> assign-copy + leaf::result r1; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value move -> assign-copy + leaf::result r1 = val(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value copy -> assign-copy + val v; + leaf::result r1 = v; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==3); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { // value default -> assign-move + leaf::result r1; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value move -> assign-move + leaf::result r1 = val(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value copy -> assign-move + val v; + leaf::result r1 = v; + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==3); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { // value default -> capture -> copy + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value move -> capture -> copy + leaf::result r1 = capture_result( [ ]{ return leaf::result(val()); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value copy -> capture -> copy + leaf::result r1 = capture_result( [ ]{ val v; return leaf::result(v); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { // value default -> capture -> move + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value move -> capture -> move + leaf::result r1 = capture_result( [ ]{ return leaf::result(val()); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value copy -> capture -> move + leaf::result r1 = capture_result( [ ]{ val v; return leaf::result(v); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { // value default -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value move -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ return leaf::result(val()); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value copy -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ val v; return leaf::result(v); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + BOOST_TEST(eq_value(r1,r2)); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { // value default -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value move -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ return leaf::result(val()); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // value copy -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ val v; return leaf::result(v); } )(); + BOOST_TEST(r1); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==2); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + // ^^ value ^^ + // vv error vv + + { // error move -> copy + static_store exp; + exp.set_reset(true); + leaf::result r1 = leaf::new_error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // error copy -> copy + static_store exp; + exp.set_reset(true); + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { // error move -> move + static_store exp; + exp.set_reset(true); + leaf::result r1 = leaf::new_error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // error copy -> move + static_store exp; + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { // error move -> assign copy + static_store exp; + leaf::result r1 = leaf::new_error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // error copy -> assign copy + static_store exp; + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { // error move -> assign move + static_store exp; + leaf::result r1 = leaf::new_error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + leaf::error_id const id = r2.error(); + exp.handle_error(leaf::error_info(id),[ ]{ }); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // error copy -> assign move + static_store exp; + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { // error move -> capture -> copy + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // error copy -> capture -> copy + leaf::result r1 = capture_result( [ ] { leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { // error move -> capture -> move + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // error copy -> capture -> move + leaf::result r1 = capture_result( [ ]{ leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { // error move -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // error copy -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + { // error move -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + { // error copy -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + BOOST_TEST(val::count==0); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + BOOST_TEST(val::count==0); + + // ^^ result ^^ + + ///////////////////////////////////////////////////////////// + + // vv result vv + + { // void default -> copy + leaf::result r1; + BOOST_TEST(r1); + leaf::result r2 = r1; + BOOST_TEST(r2); + } + + { // void default -> move + leaf::result r1; + BOOST_TEST(r1); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + } + + { // void default -> assign-copy + leaf::result r1; + BOOST_TEST(r1); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + } + + { // void default -> assign-move + leaf::result r1; + BOOST_TEST(r1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + } + + { // void default -> capture -> copy + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + leaf::result r2 = r1; + BOOST_TEST(r2); + } + + { // void default -> capture -> move + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + leaf::result r2 = std::move(r1); + BOOST_TEST(r2); + } + + { // void default -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + leaf::result r2; r2=r1; + BOOST_TEST(r2); + } + + { // void default -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ return leaf::result(); } )(); + BOOST_TEST(r1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(r2); + } + + // ^^ void default ^^ + // vv void error vv + + { // void error move -> copy + static_store exp; + exp.set_reset(true); + leaf::result r1 = leaf::new_error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + { // void error copy -> copy + static_store exp; + exp.set_reset(true); + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + { // void error move -> move + static_store exp; + exp.set_reset(true); + leaf::result r1 = leaf::new_error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + { // void error copy -> move + static_store exp; + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + { // void error move -> assign copy + static_store exp; + leaf::result r1 = leaf::new_error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + { // void error copy -> assign copy + static_store exp; + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + { // void error move -> assign move + static_store exp; + leaf::result r1 = leaf::new_error( e_err { } ); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + leaf::error_id const id = r2.error(); + exp.handle_error(leaf::error_info(id),[ ]{ }); + BOOST_TEST(err::count==1); + } + BOOST_TEST(err::count==0); + { // void error copy -> assign move + static_store exp; + leaf::error_id id = leaf::new_error( e_err{ } ); + leaf::result r1 = id; + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + { // void error move -> capture -> copy + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + { // void error copy -> capture -> copy + leaf::result r1 = capture_result( [ ] { leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + { // void error move -> capture -> move + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + { // void error copy -> capture -> move + leaf::result r1 = capture_result( [ ]{ leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2 = std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + { // void error move -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + { // void error copy -> capture -> assign-copy + leaf::result r1 = capture_result( [ ]{ leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=r1; + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + { // void error move -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ return leaf::result( leaf::new_error( e_err { } ) ); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + { // void error copy -> capture -> assign-move + leaf::result r1 = capture_result( [ ]{ leaf::error_id id = leaf::new_error( e_err{ } ); return leaf::result(id); } )(); + BOOST_TEST(!r1); + BOOST_TEST(err::count==1); + leaf::result r2; r2=std::move(r1); + BOOST_TEST(!r2); + BOOST_TEST(r1.error()==r2.error()); + } + BOOST_TEST(err::count==0); + + return boost::report_errors(); +} diff --git a/test/result_test.1.cpp b/test/result_test.1.cpp deleted file mode 100644 index 0273b7d..0000000 --- a/test/result_test.1.cpp +++ /dev/null @@ -1,90 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct my_value -{ -}; - -template -struct info -{ - int value; -}; - -leaf::result f1( bool success ) -{ - if( success ) - return { }; - else - return leaf::error( info<1>{1} ); -} - -leaf::result f2( bool success ) -{ - leaf::expect> exp; - if( leaf::result r=f1(success) ) - return r; - else - return r.error( info<2>{2} ); -} - -leaf::result f3( bool success ) -{ - leaf::expect,info<3>> exp; - auto propagate = leaf::preload( info<4>{4} ); - return f2(success); -} - -leaf::result f4( bool success ) -{ - leaf::expect,info<2>,info<3>,info<4>> exp; - if( leaf::result r = f3( success ) ) - return r; - else - { - int c1=0, c2=0; - bool handled = handle_error( exp, r, - [&c1]( info<1>, info<2>, info<3>, info<4> ) - { - ++c1; - }, - [&c2]( info<1> const & i1, info<2> const & i2, info<4> const & i4 ) - { - BOOST_TEST(i1.value==1); - BOOST_TEST(i2.value==2); - BOOST_TEST(i4.value==4); - ++c2; - } ); - BOOST_TEST(handled); - BOOST_TEST(c1==0); - BOOST_TEST(c2==1); - return leaf::error(); - } -} - -int main() -{ - leaf::expect,info<3>,info<4>> exp; - BOOST_TEST(f4(true)); - leaf::result r=f4(false); - BOOST_TEST(!r); - leaf::error e = r.error(); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(!leaf::peek>(exp,e)); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()==0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - BOOST_TEST(leaf::leaf_detail::tl_slot_ptr>()!=0); - return boost::report_errors(); -} diff --git a/test/result_test.2.cpp b/test/result_test.2.cpp deleted file mode 100644 index cb5e64f..0000000 --- a/test/result_test.2.cpp +++ /dev/null @@ -1,638 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct val -{ - static int count; - - val() - { - ++count; - } - - val( val const & ) - { - ++count; - } - - val( val && ) - { - ++count; - } - - ~val() - { - --count; - } -}; -int val::count = 0; - -struct err -{ - static int count; - - err() - { - ++count; - } - - err( err const & ) - { - ++count; - } - - err( err && ) - { - ++count; - } - - ~err() - { - --count; - } -}; -int err::count = 0; -struct e_err { err value; }; - -int main() -{ - { //value default-copy - leaf::expect exp; - leaf::result r1; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move-copy - leaf::expect exp; - leaf::result r1 = val(); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value copy-copy - leaf::expect exp; - val v; - leaf::result r1 = v; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2 = r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //value default-move - leaf::expect exp; - leaf::result r1; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move-move - leaf::expect exp; - leaf::result r1 = val(); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move-move - leaf::expect exp; - val v; - leaf::result r1 = v; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2 = std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //value default-assign copy - leaf::expect exp; - leaf::result r1; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move-assign copy - leaf::expect exp; - leaf::result r1 = val(); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move-assign copy - leaf::expect exp; - val v; - leaf::result r1 = v; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2; r2=r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //value default-assign move - leaf::expect exp; - leaf::result r1; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move-assign move - leaf::expect exp; - leaf::result r1 = val(); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move-assign move - leaf::expect exp; - val v; - leaf::result r1 = v; - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //value default - capture - copy - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result()); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move - capture - copy - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result(val())); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value copy - capture - copy - leaf::expect exp; - val v; - leaf::result r1 = capture(exp,leaf::result(v)); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2 = r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //value default - capture - move - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result()); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move - capture - move - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result(val())); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2 = std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move - capture - move - leaf::expect exp; - val v; - leaf::result r1 = capture(exp,leaf::result(v)); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2 = std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //value default - capture - assign copy - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result()); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move - capture - assign copy - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result(val())); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move - capture - assign copy - leaf::expect exp; - val v; - leaf::result r1 = capture(exp,leaf::result(v)); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2; r2=r1; - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //value default - capture - assign move - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result()); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move - capture - assign move - leaf::expect exp; - leaf::result r1 = capture(exp,leaf::result(val())); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==1); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //value move - capture - assign move - leaf::expect exp; - val v; - leaf::result r1 = capture(exp,leaf::result(v)); - BOOST_TEST(r1); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==2); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(r2); - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==3); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - copy - leaf::expect exp; - leaf::result r1 = leaf::error( e_err { } ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = r1; - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - copy - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = e; - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = r1; - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - move - leaf::expect exp; - leaf::result r1 = leaf::error( e_err { } ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - move - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = e; - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - assign copy - leaf::expect exp; - leaf::result r1 = leaf::error( e_err { } ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=r1; - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - assign copy - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = e; - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=r1; - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - assign move - leaf::expect exp; - leaf::result r1 = leaf::error( e_err { } ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - assign move - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = e; - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - capture - copy - leaf::expect exp; - leaf::result r1 = capture( exp, leaf::result( leaf::error( e_err { } ) ) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = r1; - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - capture - copy - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = capture( exp, leaf::result(e) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = r1; - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - capture - move - leaf::expect exp; - leaf::result r1 = capture( exp, leaf::result( leaf::error( e_err { } ) ) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - capture - move - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = capture( exp, leaf::result(e) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2 = std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - capture - assign copy - leaf::expect exp; - leaf::result r1 = capture( exp, leaf::result( leaf::error( e_err { } ) ) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=r1; - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - capture - assign copy - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = capture( exp, leaf::result(e) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=r1; - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - { //error move - capture - assign move - leaf::expect exp; - leaf::result r1 = capture( exp, leaf::result( leaf::error( e_err { } ) ) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - { //error copy - capture - assign move - leaf::expect exp; - leaf::error e( e_err{ } ); - leaf::result r1 = capture( exp, leaf::result(e) ); - BOOST_TEST(!r1); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - leaf::result r2; r2=std::move(r1); - BOOST_TEST(!r2); - BOOST_TEST(handle_error(exp,r2,[ ]{ })); - BOOST_TEST(err::count==1); - BOOST_TEST(val::count==0); - } - BOOST_TEST(err::count==0); - BOOST_TEST(val::count==0); - - return boost::report_errors(); -} diff --git a/test/result_test.3.cpp b/test/result_test.3.cpp deleted file mode 100644 index 718f44a..0000000 --- a/test/result_test.3.cpp +++ /dev/null @@ -1,59 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -struct err -{ - static int count; - - err() - { - ++count; - } - - err( err const & ) - { - ++count; - } - - err( err && ) - { - ++count; - } - - ~err() - { - --count; - } -}; -int err::count = 0; -struct e_err1 { err value; }; -struct e_err2 { err value; }; - -int main() -{ - leaf::result r0; - { - leaf::expect exp; - leaf::result r1 = leaf::error( e_err1{ } ); - BOOST_TEST(!r1); - r0 = capture(exp,r1); - BOOST_TEST(err::count==1); - } - BOOST_TEST(err::count==1); - BOOST_TEST(!r0); - leaf::expect exp; - leaf::result r2 = r0.error( e_err2{ } ); - BOOST_TEST(err::count==2); - BOOST_TEST(handle_error(exp,r2,[ ](e_err1,e_err2){ })); - return boost::report_errors(); -} diff --git a/test/result_test.4.cpp b/test/result_test.4.cpp deleted file mode 100644 index 051e200..0000000 --- a/test/result_test.4.cpp +++ /dev/null @@ -1,81 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -template -void check( Expect & exp, leaf::bad_result const & e ) -{ - handle_exception( exp, e, - [ ]( leaf::e_source_location const & x ) - { - BOOST_TEST(strstr(x.file,"result.hpp")!=0); - BOOST_TEST(x.line>0); - BOOST_TEST(strstr(x.function,"value")!=0); - } ); -} - -int main() -{ - { - leaf::expect exp; - try - { - leaf::result r((leaf::error())); - (void) r.value(); - BOOST_TEST(false); - } - catch( leaf::bad_result const & e ) - { - check(exp,e); - } - } - { - leaf::expect exp; - try - { - leaf::result const r((leaf::error())); - (void) r.value(); - BOOST_TEST(false); - } - catch( leaf::bad_result const & e ) - { - check(exp,e); - } - } - { - leaf::expect exp; - try - { - leaf::result r((leaf::error())); - (void) *r; - BOOST_TEST(false); - } - catch( leaf::bad_result const & e ) - { - check(exp,e); - } - } - { - leaf::expect exp; - try - { - leaf::result const r((leaf::error())); - (void) *r; - BOOST_TEST(false); - } - catch( leaf::bad_result const & e ) - { - check(exp,e); - } - } - return boost::report_errors(); -} diff --git a/test/result_test.5.cpp b/test/result_test.5.cpp deleted file mode 100644 index 577d539..0000000 --- a/test/result_test.5.cpp +++ /dev/null @@ -1,186 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -template struct info { int value; }; - -leaf::result f() -{ - return leaf::error(info<1>{1},info<3>{3}); -} - -int main() -{ - leaf::expect,info<2>,info<3>> exp; - - { - int c=0; - bool r = handle_error( exp, f(), - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - c = 1; - } ); - BOOST_TEST(r); - BOOST_TEST(c==1); - } - - { - int c=0; - bool r = handle_error( exp, f(), - [&c]( info<2> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==2); - c = 1; - } ); - BOOST_TEST(!r); - BOOST_TEST(c==0); - } - - { - int c=0; - int r = handle_error( exp, f(), - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - return c = 1; - } ); - BOOST_TEST(r==1); - } - - { - int c=0; - int r = handle_error( exp, f(), - [&c]( info<2> const & x ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==2); - return c = 1; - } ); - BOOST_TEST(r==-1); - } - - { - int c=0; - bool r = handle_error( exp, f(), - [&c]( info<1> const & x, info<2> const & ) - { - BOOST_TEST(c==0); - c = 1; - }, - [&c]( info<1> const & x, info<3> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(y.value==3); - c = 2; - return false; - }, - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - c = 3; - } ); - BOOST_TEST(!r); - BOOST_TEST(c==2); - } - - { - int c=0; - bool r = handle_error( exp, f(), - [&c]( info<1> const & x, info<2> const & ) - { - BOOST_TEST(c==0); - c = 1; - return false; - }, - [&c]( info<1> const & x, info<3> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(y.value==3); - c = 2; - }, - [&c]( info<1> const & x ) - { - BOOST_TEST(c==0); - c = 3; - return false; - } ); - BOOST_TEST(r); - BOOST_TEST(c==2); - } - - { - int c=0; - leaf::result r = handle_error( exp, f(), - [&c]( info<1> const & x, info<3> const & ) -> leaf::result - { - BOOST_TEST(c==0); - c = 1; - return 42; - } ); - BOOST_TEST(*r==42); - BOOST_TEST(c==1); - } - - { - int c=0; - leaf::result r1 = f(); - leaf::expect> exp1; - leaf::result r2 = handle_error( exp, r1, - [&c]( info<1> const & x, info<3> const & ) -> leaf::result - { - BOOST_TEST(c==0); - c = 1; - return leaf::error(info<42>{42}); - } ); - BOOST_TEST(c==1); - c = 0; - bool b = handle_error( exp1, r2, - [&c]( info<42> const & x ) - { - BOOST_TEST(c==0); - c = 1; - BOOST_TEST(x.value==42); - } ); - BOOST_TEST(b); - BOOST_TEST(c==1); - } - - { - int c=0; - leaf::result r1 = f(); - leaf::result r2 = handle_error( exp, r1, - [&c]( info<2> const & ) -> leaf::result - { - BOOST_TEST(c==0); - c = 1; - return 42; - } ); - BOOST_TEST(c==0); - bool b = handle_error( exp, r2, - [&c]( info<1> const & x, info<3> const & y ) - { - BOOST_TEST(c==0); - BOOST_TEST(x.value==1); - BOOST_TEST(y.value==3); - c = 1; - } ); - BOOST_TEST(b); - BOOST_TEST(c==1); - } - - return boost::report_errors(); -} diff --git a/test/result_test.6.cpp b/test/result_test.6.cpp deleted file mode 100644 index 22bcd40..0000000 --- a/test/result_test.6.cpp +++ /dev/null @@ -1,182 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" - -namespace leaf = boost::leaf; - -enum class error_code -{ - error1=1, - error2, - error3 -}; - -namespace boost { namespace leaf { - template <> struct is_error_type: public std::true_type { }; -} } - -leaf::result compute_answer( int what_to_do ) noexcept -{ - switch( what_to_do ) - { - case 0: - return 42; - case 1: - return leaf::error(error_code::error1); - case 2: - return leaf::error(error_code::error2); - default: - assert(what_to_do==3); - return leaf::error(error_code::error3); - } -} - -leaf::result handle_some_errors( int what_to_do ) -{ - leaf::expect exp; - if( leaf::result r = compute_answer(what_to_do) ) - return r; - else - return handle_error( exp, r, - [&r]( error_code ec ) -> leaf::result - { - if( ec==error_code::error1 ) - return 42; - else - return r; - } ); -} - -leaf::result handle_some_errors_float( int what_to_do ) -{ - leaf::expect exp; - if( leaf::result r = compute_answer(what_to_do) ) - return r; - else - return handle_error( exp, r, - [&r]( error_code ec ) -> leaf::result - { - if( ec==error_code::error2 ) - return 42.0f; - else - return r; - } ); -} - -leaf::result handle_some_errors_void( int what_to_do ) -{ - leaf::expect exp; - if( leaf::result r = compute_answer(what_to_do) ) - return { }; - else - return handle_error( exp, r, - [&r]( error_code ec ) -> leaf::result - { - if( ec==error_code::error3 ) - return { }; - else - return r.error(); - } ); -} - -int main() -{ - leaf::expect exp; - - /////////////////////////// - - BOOST_TEST(handle_some_errors(0).value()==42); - BOOST_TEST(handle_some_errors(1).value()==42); - { - leaf::result r = handle_some_errors(2); - BOOST_TEST(!r); - int c=0; - BOOST_TEST( handle_error( exp, r, - [&c]( error_code ec ) - { - BOOST_TEST(ec==error_code::error2); - ++c; - } ) ); - BOOST_TEST(c==1); - } - { - leaf::result r = handle_some_errors(3); - BOOST_TEST(!r); - int c=0; - BOOST_TEST( handle_error( exp, r, - [&c]( error_code ec ) - { - BOOST_TEST(ec==error_code::error3); - ++c; - } ) ); - BOOST_TEST(c==1); - } - - /////////////////////////// - - BOOST_TEST(handle_some_errors_float(0).value()==42); - { - leaf::result r = handle_some_errors_float(1); - BOOST_TEST(!r); - int c=0; - BOOST_TEST( handle_error( exp, r, - [&c]( error_code ec ) - { - BOOST_TEST(ec==error_code::error1); - ++c; - } ) ); - BOOST_TEST(c==1); - } - BOOST_TEST(handle_some_errors_float(2).value()==42.0f); - { - leaf::result r = handle_some_errors_float(3); - BOOST_TEST(!r); - int c=0; - BOOST_TEST( handle_error( exp, r, - [&c]( error_code ec ) - { - BOOST_TEST(ec==error_code::error3); - ++c; - } ) ); - BOOST_TEST(c==1); - } - - /////////////////////////// - - BOOST_TEST(handle_some_errors_void(0)); - { - leaf::result r = handle_some_errors_void(1); - BOOST_TEST(!r); - int c=0; - BOOST_TEST( handle_error( exp, r, - [&c]( error_code ec ) - { - BOOST_TEST(ec==error_code::error1); - ++c; - } ) ); - BOOST_TEST(c==1); - } - { - leaf::result r = handle_some_errors_void(2); - BOOST_TEST(!r); - int c=0; - BOOST_TEST( handle_error( exp, r, - [&c]( error_code ec ) - { - BOOST_TEST(ec==error_code::error2); - ++c; - } ) ); - BOOST_TEST(c==1); - } - BOOST_TEST(handle_some_errors_void(3)); - - /////////////////////////// - - return boost::report_errors(); -} diff --git a/test/result_void_capture_test.cpp b/test/result_void_capture_test.cpp deleted file mode 100644 index 9aa6bcd..0000000 --- a/test/result_void_capture_test.cpp +++ /dev/null @@ -1,78 +0,0 @@ -//Copyright (c) 2018 Emil Dotchevski -//Copyright (c) 2018 Second Spectrum, Inc. - -//Distributed under the Boost Software License, Version 1.0. (See accompanying -//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include "boost/core/lightweight_test.hpp" -#include -#include -#include -#include - -namespace leaf = boost::leaf; - -template struct info { int value; }; - -struct fut_info -{ - int a; - int b; - int result; - std::future> fut; -}; - -template -std::vector launch_tasks( int task_count, F f ) -{ - assert(task_count>0); - std::vector fut; - std::generate_n( std::inserter(fut,fut.end()), task_count, [f] - { - int const a = rand(); - int const b = rand(); - int const res = (rand()%10) - 5; - return fut_info { a, b, res, std::async( std::launch::async, [f,a,b,res] - { - leaf::expect,info<2>,info<3>> exp; - return capture(exp,f(a,b,res)); - } ) }; - } ); - return fut; -} - -int main() -{ - std::vector fut = launch_tasks( 42, [ ]( int a, int b, int res ) noexcept -> leaf::result - { - if( res>=0 ) - return { }; - else - return leaf::error(info<1>{a},info<2>{b},info<3>{}); - } ); - - for( auto & f : fut ) - { - using namespace leaf::leaf_detail; - f.fut.wait(); - leaf::expect,info<2>,info<4>> exp; - if( leaf::result r = f.fut.get() ) - { - } - else - { - int c=0; - bool handled = handle_error( exp, r, [&f,&c]( info<1> const & x1, info<2> const & x2 ) - { - BOOST_TEST(x1.value==f.a); - BOOST_TEST(x2.value==f.b); - ++c; - } ); - BOOST_TEST(handled); - BOOST_TEST(c==1); - } - } - return boost::report_errors(); -} diff --git a/test/static_store_deduction_test.cpp b/test/static_store_deduction_test.cpp new file mode 100644 index 0000000..3fcfb67 --- /dev/null +++ b/test/static_store_deduction_test.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +namespace leaf = boost::leaf; + +template +typename leaf::leaf_detail::deduce_static_store::type>::type * expd( Handler && ... ) +{ + return 0; +} + +template +void test( U * ) +{ + static_assert(std::is_same::value,"static_store_deduction"); +} + +template struct info { int value; }; + +enum class my_error_code +{ + ok, + error1, + error2, + error3 +}; +namespace boost { namespace leaf { + template <> struct is_error_type: std::true_type { }; +} } + +void not_called_on_purpose() +{ + using leaf::leaf_detail::static_store; + + test< static_store> >( expd([ ]( info<1> ){ }) ); + test< static_store> >( expd([ ]( info<1> const ){ }) ); + test< static_store> >( expd([ ]( info<1> const & ){ }) ); + test< static_store> >( expd([ ]( info<1>, leaf::error_info const & ){ }) ); + + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( info<1>, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<1>, info<2> ){ }) ); + + test< static_store,info<2>> >( expd([ ]( leaf::error_info const &, info<1> ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( leaf::error_info const &, info<1> ){ }, [ ]( info<1>, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( leaf::error_info const &, info<1>, info<2> ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( leaf::error_info const &, info<1>, info<2> ){ }, [ ]( info<1>, info<2> ){ }) ); + + test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error_info const & ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error_info const & ){ }, [ ]( info<1>, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error_info const &, info<2> ){ }, [ ]( info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, leaf::error_info const &, info<2> ){ }, [ ]( info<1>, info<2> ){ }) ); + + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( leaf::error_info const &, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( leaf::error_info const &, info<1>, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( leaf::error_info const &, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( leaf::error_info const &, info<1>, info<2> ){ }) ); + + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( info<2>, leaf::error_info const & ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1> ){ }, [ ]( info<1>, leaf::error_info const &, info<2> ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<2>, leaf::error_info const & ){ }) ); + test< static_store,info<2>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<1>, leaf::error_info const &, info<2> ){ }) ); + + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<2> ){ }, [ ]( info<3> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<1>, info<2> ){ }, [ ]( info<1>, info<3> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<1>, info<2> ){ }, [ ]( info<1>, info<3> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<2> ){ }, [ ]( info<3> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1>, info<2> ){ }, [ ]( info<2> ){ }, [ ]( info<3> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<2> ){ }, [ ]( info<3>, info<2> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<2> ){ }, [ ]( info<3>, info<2> ){ }) ); + test< static_store,info<3>,info<2>> >( expd([ ]( info<1>, info<3> ){ }, [ ]( info<2> ){ }, [ ]( info<3> ){ }) ); + test< static_store,info<3>,info<2>> >( expd([ ]( info<1>, info<3> ){ }, [ ]( info<2> ){ }, [ ]( info<3> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<2>, info<3> ){ }, [ ]( info<3> ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> ){ }, [ ]( info<2>, info<3> ){ }, [ ]( info<3> ){ }) ); + + test< static_store >( expd([ ]( leaf::match ){ }) ); + test< static_store> >( expd([ ]( leaf::match,42> ){ }) ); + test< static_store> >( expd([ ]( leaf::catch_, info<1> ){ }) ); + + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> const *, info<2> ){ }, [ ]( info<1>, info<3> const * ){ }) ); + test< static_store,info<2>,info<3>> >( expd([ ]( info<1> const, info<2> ){ }, [ ]( info<1> const *, info<3> ){ }) ); +} + +int main() +{ + return 0; +} diff --git a/test/static_store_test.cpp b/test/static_store_test.cpp new file mode 100644 index 0000000..1195ac2 --- /dev/null +++ b/test/static_store_test.cpp @@ -0,0 +1,149 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template +struct info +{ + int value; +}; + +leaf::error_id f1() +{ + return leaf::new_error( info<1>{1} ); +} + +leaf::error_id f2() +{ + return f1().propagate( info<2>{2} ); +} + +leaf::error_id f3() +{ + return f2().propagate( info<3>{3} ); +} + +leaf::error_id f4() +{ + return f3().propagate(); +} + +int main() +{ + using leaf::leaf_detail::static_store; + + static_store,info<2>,info<4>> exp0; + leaf::error_id e0 = f4(); + { + info<1> const * p = exp0.peek>(e0); + BOOST_TEST(p && p->value==1); + } + + { + info<2> const * p = exp0.peek>(e0); + BOOST_TEST(p && p->value==2); + } + + BOOST_TEST(!exp0.peek>(e0)); + static_store,info<2>,info<4>> exp; + leaf::error_id e1 = f4(); + + { + info<1> const * p = exp0.peek>(e0); + BOOST_TEST(p && p->value==1); + } + + { + info<2> const * p = exp0.peek>(e0); + BOOST_TEST(p && p->value==2); + } + + BOOST_TEST(!exp0.peek>(e0)); + BOOST_TEST(!exp.peek>(e0)); + BOOST_TEST(!exp.peek>(e0)); + BOOST_TEST(!exp.peek>(e0)); + + { + info<1> const * p = exp.peek>(e1); + BOOST_TEST(p && p->value==1); + } + + { + info<2> const * p = exp.peek>(e1); + BOOST_TEST(p && p->value==2); + } + + BOOST_TEST(!exp.peek>(e1)); + { + int r = exp.handle_error( leaf::error_info(e1), + [ ](info<1>,info<2>,info<4>) + { + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==2); + } + leaf::error_id e2 = f4(); + + { + info<1> const * p = exp.peek>(e2); + BOOST_TEST(p && p->value==1); + } + + { + info<2> const * p = exp.peek>(e2); + BOOST_TEST(p && p->value==2); + } + + BOOST_TEST(!exp.peek>(e2)); + + { + int r = exp.handle_error( leaf::error_info(e2), + [ ]( info<1>, info<2>, info<4> ) + { + return 1; + }, + [ ]( info<1>, info<2>, info<4> ) + { + return 2; + }, + [ ]( info<2> const & i2, info<1> const & i1 ) + { + BOOST_TEST(i1.value==1); + BOOST_TEST(i2.value==2); + return 3; + }, + [ ] + { + return 4; + } ); + BOOST_TEST(r==3); + } + + { + int r = exp0.handle_error( leaf::error_info(e0), + [ ]( info<2> const & i2, info<1> const & i1 ) + { + BOOST_TEST(i1.value==1); + BOOST_TEST(i2.value==2); + return 1; + }, + [ ] + { + return 2; + } ); + BOOST_TEST(r==1); + } + + return boost::report_errors(); +} diff --git a/test/try_exception_test.cpp b/test/try_exception_test.cpp new file mode 100644 index 0000000..c80635c --- /dev/null +++ b/test/try_exception_test.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +struct info { int value; }; + +struct my_error: std::exception { }; + +template +int test( F && f ) +{ + return leaf::try_( + [&]() -> int + { + f(); + return 1; + }, + [ ]( leaf::catch_, info, leaf::e_source_location ) + { + return 2; + }, + [ ]( leaf::catch_, info x ) + { + return 3; + }, + [ ]( leaf::catch_, leaf::e_source_location ) + { + return 4; + }, + [ ]( leaf::catch_ ) + { + return 5; + }, + [ ] + { + return 6; + } ); +} + +int main() +{ + BOOST_TEST( 5==test( [ ] { throw my_error(); } ) ); + + BOOST_TEST( 5==test( [ ] { throw leaf::exception(my_error()); } ) ); + BOOST_TEST( 3==test( [ ] { throw leaf::exception(my_error(),info{42}); } ) ); + + BOOST_TEST( 4==test( [ ] { throw LEAF_EXCEPTION(my_error()); } ) ); + BOOST_TEST( 2==test( [ ] { throw LEAF_EXCEPTION(my_error(),info{42}); } ) ); + + BOOST_TEST( 4==test( [ ] { LEAF_THROW(my_error()); } ) ); + BOOST_TEST( 2==test( [ ] { LEAF_THROW(my_error(),info{42}); } ) ); + + return boost::report_errors(); +} diff --git a/test/try_test.cpp b/test/try_test.cpp new file mode 100644 index 0000000..0e861ed --- /dev/null +++ b/test/try_test.cpp @@ -0,0 +1,522 @@ +// Copyright (c) 2018 Emil Dotchevski +// Copyright (c) 2018 Second Spectrum, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "boost/core/lightweight_test.hpp" + +namespace leaf = boost::leaf; + +template struct info { int value; }; + +struct error1: std::exception { }; +struct error2: std::exception { }; +struct error3: std::exception { }; + +template +R f( Ex && ex ) +{ + throw leaf::exception(std::move(ex),info<1>{1},info<2>{2},info<3>{3}); +} + +template +R f() +{ + return R(42); +} + +int main() +{ + // void, handle_all (success) + { + int c=0; + leaf::try_( + [&c] + { + c = f(); + }, + [&c] + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(c==42); + } + + // void, handle_all (failure), match_enum (single enum value) + { + int c=0; + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + // void, handle_all (failure), match_enum (multiple enum values) + { + int c=0; + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + // void, handle_all (failure), match_value (single value) + { + int c=0; + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + // void, handle_all (failure), match_value (multiple values) + { + int c=0; + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 1; + }, + [&c]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + ////////////////////////////////////// + + // void, handle_some (failure, initially not matched), match_enum (single enum value) + { + int c=0; + leaf::try_( + [&c] + { + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(false); + }, + [&c]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + // void, handle_some (failure, initially not matched), match_enum (multiple enum values) + { + int c=0; + leaf::try_( + [&c] + { + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 1; + } ); + BOOST_TEST(false); + }, + [&c]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==2); + } + + // void, handle_some (failure, initially matched), match_enum (single enum value) + { + int c=0; + leaf::try_( + [&c] + { + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_ ec, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==1); + } + + // void, handle_some (failure, initially matched), match_enum (multiple enum values) + { + int c=0; + leaf::try_( + [&c] + { + leaf::try_( + [&c] + { + c = f(error1()); + }, + [&c]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + BOOST_TEST(c==0); + c = 1; + } ); + }, + [&c]( leaf::catch_ ) + { + BOOST_TEST(c==0); + c = 2; + }, + [&c] + { + BOOST_TEST(c==0); + c = 3; + } ); + BOOST_TEST(c==1); + } + + ////////////////////////////////////// + + // int, handle_all (success) + { + int r = leaf::try_( + [ ] + { + return f(); + }, + [ ] + { + return 1; + } ); + BOOST_TEST(r==42); + } + + // int, handle_all (failure), match_enum (single enum value) + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_ ) + { + return 1; + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + // int, handle_all (failure), match_enum (multiple enum values) + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_ ) + { + return 1; + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + ////////////////////////////////////// + + // int, handle_some (failure, matched), match_enum (single enum value) + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_ ) + { + return 1; + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + } ); + BOOST_TEST(r==2); + } + + // int, handle_some (failure, matched), match_enum (multiple enum values) + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_ ) + { + return 1; + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + } ); + BOOST_TEST(r==2); + } + + // int, handle_some (failure, initially not matched), match_enum (single enum value) + { + int r = leaf::try_( + [ ] + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_ ) + { + return 1; + } ); + BOOST_TEST(false); + return r; + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + // int, handle_some (failure, initially not matched), match_enum (multiple enum values) + { + int r = leaf::try_( + [ ] + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_ ) + { + return 1; + } ); + BOOST_TEST(false); + return r; + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==2); + } + + // int, handle_some (failure, initially matched), match_enum (single enum value) + { + int r = leaf::try_( + [ ] + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r==1); + return r; + }, + [ ]( leaf::catch_ ) + { + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==1); + } + + // int, handle_some (failure, initially matched), match_enum (multiple enum values) + { + int r = leaf::try_( + [ ] + { + int r = leaf::try_( + [ ] + { + return f(error1()); + }, + [ ]( leaf::catch_, info<1> const & x, info<2> y ) + { + BOOST_TEST(x.value==1); + BOOST_TEST(y.value==2); + return 1; + } ); + BOOST_TEST(r==1); + return r; + }, + [ ]( leaf::catch_ ) + { + return 2; + }, + [ ] + { + return 3; + } ); + BOOST_TEST(r==1); + } + + ////////////////////////////////////// + + return boost::report_errors(); +}