├── .gitignore ├── .vscode ├── c_cpp_properties.json ├── launch.json └── tasks-chooser.json ├── README.adoc ├── adoc └── synopses │ ├── noexcept.hpp.adoc │ ├── throw.hpp.adoc │ └── try.hpp.adoc ├── benchmark ├── deep_stack.cpp └── deep_stack_outcome.cpp ├── examples ├── FILE_ptr_example.cpp ├── FILE_ptr_throw_example.cpp ├── c_api_example.cpp ├── ifstream_example.cpp └── lua_example.cpp ├── include └── boost │ ├── noexcept.hpp │ └── noexcept │ ├── noexcept_config │ ├── assert.hpp │ ├── inline.hpp │ ├── rtti.hpp │ ├── thread_local.hpp │ └── throw_exception.hpp │ ├── noexcept_detail │ ├── ceh.hpp │ ├── eh.hpp │ └── error.hpp │ ├── throw.hpp │ └── try.hpp ├── meson.build ├── meson_options.txt ├── subprojects ├── boost.wrap └── lua.wrap └── test ├── Jamfile.v2 ├── _rethrow_test.cpp ├── _unhandled_error_test1.cpp ├── _unhandled_error_test2.cpp ├── _unhandled_error_test3.cpp ├── _unhandled_error_test4.cpp ├── _unhandled_error_test5.cpp ├── check_test.cpp ├── diagnostic_information_test.cpp ├── error_info_test.cpp ├── error_test.cpp ├── has_current_error_test.cpp ├── header-test.cpp ├── multiple_active_errors_test.cpp ├── result_mt_test.cpp ├── result_test.cpp ├── throw_exception_test.cpp ├── throw_return_test.cpp └── throw_test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .Trashes 3 | *.suo 4 | *.sdf 5 | *.opensdf 6 | *.vcxproj.user 7 | *.xcuserstate 8 | bld/* 9 | subprojects/* 10 | .vscode/tasks.json 11 | !*.wrap 12 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1", 7 | "/usr/local/include", 8 | "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.0.0/include", 9 | "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include", 10 | "/usr/include", 11 | "${workspaceRoot}", 12 | "${workspaceRoot}/include", 13 | "${workspaceRoot}/../.." 14 | ], 15 | "defines": [], 16 | "intelliSenseMode": "clang-x64", 17 | "browse": { 18 | "path": [ 19 | "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1", 20 | "/usr/local/include", 21 | "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.0.0/include", 22 | "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include", 23 | "/usr/include", 24 | "${workspaceRoot}" 25 | ], 26 | "limitSymbolsToIncludedHeaders": true, 27 | "databaseFilename": "" 28 | }, 29 | "macFrameworkPath": [ 30 | "/System/Library/Frameworks", 31 | "/Library/Frameworks" 32 | ] 33 | }, 34 | { 35 | "name": "Linux", 36 | "includePath": [ 37 | "/usr/include", 38 | "/usr/local/include", 39 | "${workspaceRoot}", 40 | "${workspaceRoot}/include", 41 | "${workspaceRoot}/../..", 42 | "/usr/include/c++/7", 43 | "/usr/include/x86_64-linux-gnu/c++/7" 44 | ], 45 | "defines": [], 46 | "intelliSenseMode": "clang-x64", 47 | "browse": { 48 | "path": [ 49 | "/usr/include", 50 | "/usr/local/include", 51 | "${workspaceRoot}", 52 | "${workspaceRoot}/include", 53 | "${workspaceRoot}/../.." 54 | ], 55 | "limitSymbolsToIncludedHeaders": true, 56 | "databaseFilename": "" 57 | } 58 | }, 59 | { 60 | "name": "Win32", 61 | "includePath": [ 62 | "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include", 63 | "${workspaceRoot}" 64 | ], 65 | "defines": [ 66 | "_DEBUG", 67 | "UNICODE" 68 | ], 69 | "intelliSenseMode": "msvc-x64", 70 | "browse": { 71 | "path": [ 72 | "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include/*", 73 | "${workspaceRoot}" 74 | ], 75 | "limitSymbolsToIncludedHeaders": true, 76 | "databaseFilename": "" 77 | } 78 | } 79 | ], 80 | "version": 3 81 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "C++ Launch", 6 | "type": "cppdbg", 7 | "request": "launch", 8 | "program": "${workspaceRoot}/bld/minimal/throw_test", 9 | "cwd": "${workspaceRoot}/test", 10 | "args": [], 11 | "stopAtEntry": false, 12 | "environment": [], 13 | "externalConsole": false, 14 | "linux": { 15 | "MIMode": "gdb", 16 | "setupCommands": [ 17 | { 18 | "description": "Enable pretty-printing for gdb", 19 | "text": "-enable-pretty-printing", 20 | "ignoreFailures": true 21 | } 22 | ] 23 | }, 24 | "osx": { 25 | "MIMode": "lldb" 26 | }, 27 | "windows": { 28 | "MIMode": "gdb", 29 | "miDebuggerPath": "C:\\Program Files (x86)\\mingw-w64\\i686-7.1.0-posix-dwarf-rt_v5-rev0\\mingw32\\bin\\gdb.exe", 30 | "setupCommands": [ 31 | { 32 | "description": "Enable pretty-printing for gdb", 33 | "text": "-enable-pretty-printing", 34 | "ignoreFailures": true 35 | } 36 | ] 37 | } 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /.vscode/tasks-chooser.json: -------------------------------------------------------------------------------- 1 | { 2 | "selectDescription": "Select build setting", 3 | "items": [ 4 | { 5 | "displayName": "check_test", 6 | "description": "Unit test", 7 | "isBuildCommand": "true", 8 | "command": "sh", 9 | "args": [ "-c", "cd ${workspaceRoot}/bld/debug && mesontest check_test" ] 10 | }, 11 | { 12 | "displayName": "error_info_test", 13 | "description": "Unit test", 14 | "isBuildCommand": "true", 15 | "command": "sh", 16 | "args": [ "-c", "cd ${workspaceRoot}/bld/debug && mesontest error_info_test" ] 17 | }, 18 | { 19 | "displayName": "diagnostic_information_test", 20 | "description": "Unit test", 21 | "isBuildCommand": "true", 22 | "command": "sh", 23 | "args": [ "-c", "cd ${workspaceRoot}/bld/debug && mesontest diagnostic_information_test" ] 24 | }, 25 | { 26 | "displayName": "error_test", 27 | "description": "Unit test", 28 | "isBuildCommand": "true", 29 | "command": "sh", 30 | "args": [ "-c", "cd ${workspaceRoot}/bld/debug && mesontest error_test" ] 31 | }, 32 | { 33 | "displayName": "error_test_no_exception_info", 34 | "description": "Unit test", 35 | "isBuildCommand": "true", 36 | "command": "sh", 37 | "args": [ "-c", "cd ${workspaceRoot}/bld/debug && mesontest error_test_no_exception_info" ] 38 | }, 39 | { 40 | "displayName": "handler_test", 41 | "description": "Unit test", 42 | "isBuildCommand": "true", 43 | "command": "sh", 44 | "args": [ "-c", "cd ${workspaceRoot}/bld/debug && mesontest handler_test" ] 45 | }, 46 | { 47 | "displayName": "has_current_error_test", 48 | "description": "Unit test", 49 | "isBuildCommand": "true", 50 | "command": "sh", 51 | "args": [ "-c", "cd ${workspaceRoot}/bld/debug && mesontest has_current_error_test" ] 52 | }, 53 | { 54 | "displayName": "multiple_active_errors_test", 55 | "description": "Unit test", 56 | "isBuildCommand": "true", 57 | "command": "sh", 58 | "args": [ "-c", "cd ${workspaceRoot}/bld/debug && mesontest multiple_active_errors_test" ] 59 | }, 60 | { 61 | "displayName": "result_test", 62 | "description": "Unit test", 63 | "isBuildCommand": "true", 64 | "command": "sh", 65 | "args": [ "-c", "cd ${workspaceRoot}/bld/debug && mesontest result_test" ] 66 | }, 67 | { 68 | "displayName": "result_mt_test", 69 | "description": "Unit test", 70 | "isBuildCommand": "true", 71 | "command": "sh", 72 | "args": [ "-c", "cd ${workspaceRoot}/bld/debug && mesontest result_mt_test" ] 73 | }, 74 | { 75 | "displayName": "throw_exception_test", 76 | "description": "Unit test", 77 | "isBuildCommand": "true", 78 | "command": "sh", 79 | "args": [ "-c", "cd ${workspaceRoot}/bld/debug && mesontest throw_exception_test" ] 80 | }, 81 | { 82 | "displayName": "throw_return_test", 83 | "description": "Unit test", 84 | "isBuildCommand": "true", 85 | "command": "sh", 86 | "args": [ "-c", "cd ${workspaceRoot}/bld/debug && mesontest throw_return_test" ] 87 | }, 88 | { 89 | "displayName": "throw_test", 90 | "description": "Unit test", 91 | "isBuildCommand": "true", 92 | "command": "sh", 93 | "args": [ "-c", "cd ${workspaceRoot}/bld/debug && mesontest throw_test" ] 94 | }, 95 | { 96 | "displayName": "All unit tests", 97 | "description": "Unit test", 98 | "isBuildCommand": "true", 99 | "command": "sh", 100 | "args": [ "-c", "cd ${workspaceRoot}/bld/debug && mesontest && cd ${workspaceRoot}/bld/boost_tss && mesontest && cd ${workspaceRoot}/bld/minimal && mesontest" ] 101 | } 102 | ], 103 | "baseItem": { 104 | "version": "0.1.0", 105 | "problemMatcher": { 106 | "fileLocation": ["relative", "${workspaceRoot}/bld/debug"], 107 | "owner": "cpp", 108 | "pattern": { 109 | "regexp": "^(.*):(\\d+):(\\d+):\\s+.*(warning|error|note):\\s+(.*)$", 110 | "file": 1, 111 | "line": 2, 112 | "column": 3, 113 | "severity": 4, 114 | "message": 5 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | :sourcedir: . 2 | :last-update-label!: 3 | :source-highlighter: coderay 4 | :icons: font 5 | = Noexcept 6 | Lightweight error handling without exceptions for C++11 7 | :toclevels: 3 8 | :toc: left 9 | :toc-title: 10 | 11 | [abstract] 12 | == Abstract 13 | 14 | Noexcept is small and efficient {CPP}11 error-handling library which does not use exception handling and does not require exception-safe environment. Some of its features are: 15 | 16 | * Function return values are not burdened with transporting error objects in case of a failure. 17 | * Errors can be identified by error code _values_ (including `std::error_code`), or by static _types_ if type safety is desired. 18 | * Error-neutral contexts can forward to the caller any error condition detected in lower level functions, without touching the error object. 19 | * Error-handling contexts may selectively handle some errors but remain neutral to others. 20 | 21 | Because Noexcept is not intrusive to function return types, it is able to propagate failures even across third-party code which may not be able to use specific sophisticated error-reporting types. This makes Noexcept deployable even in tricky use cases where a more intrusive error handling mechanism could not be used: 22 | 23 | * When errors are reported from a C-style callback, or 24 | * from a {CPP} virtual function override when deriving from a third-party base type. 25 | * When reported errors need to cross multiple API levels, each with their own error reporting mechanism. 26 | * When reported errors need to cross programming language boundaries. 27 | 28 | == Distribution 29 | 30 | Noexcept is distributed under the http://www.boost.org/LICENSE_1_0.txt[Boost Software License, Version 1.0]. 31 | 32 | The source code is available in https://github.com/zajo/boost-noexcept[this GitHub repository]. 33 | 34 | NOTE: Noexcept is not part of Boost. 35 | 36 | == Feedback / support 37 | 38 | Please use the link:https://lists.boost.org/mailman/listinfo.cgi/boost[Boost Developers mailing list]. 39 | 40 | [[design_rationale]] 41 | == Design rationale 42 | 43 | `noexcept` functions which could potentially fail must communicate the failure directly to the caller -- but they also need to be able to return a value in case of success. 44 | 45 | This naturally leads designers of error-handling libraries to creating a special return type capable of transporting any value or any error, with two design goals: 46 | 47 | . Zero abstraction penalty, at the very least in the case the function succeeds. 48 | . Flexibility to transport a wide range of error objects and data pertaining to error conditions. 49 | 50 | The problem is that these are competing goals -- which would force us into a compromise, the victim _probably_ being the flexibility of the error-transporting facilities. This is not ideal for an error handling library. 51 | 52 | In addition, such design would lead to problems specific to C and {CPP}: 53 | 54 | * It is intrusive, so it couldn't be used in many practical cases, for example when reporting errors from a C callback. 55 | * {CPP} programmers do not even agree on what string type they should use, so interoperability between the different parts of a large program is going to be a problem. 56 | 57 | For these reasons, Noexcept does not define a special return type. Programmers may use any type whatsoever, the only requirement is that it provides a special state -- a value that can be returned in case of a failure. Besides facilitating interoperability, this virtually eliminates the chance of Noexcept impacting the performance of passing values up the call chain. 58 | 59 | When a failure is detected, the error object is created in thread-local storage rather than passed up one level at a time. It is destroyed when control reaches an error-handling function that recognizes it and recovers from the failure. 60 | 61 | Further rationale for using TLS for transporting error objects is that errors are semantically very different from successful results. In a typical program, there may be very many results of different types produced, transformed, processed, used in different computations and algorithms, passed up and down the call chain. In contrast, the path taken by an error object is much simpler -- and it is (mostly, sometimes even exclusively) of interest to only two contexts: the function that detects and reports the error, and the function which recognizes it and is able to handle it. For that reason, it makes sense to push errors to the side, so they are less intrusive yet still available for access if needed. 62 | 63 | NOTE: For a comparison between various error-reporting libraries and proposals, see <>. There is also a <>. 64 | 65 | [[guarantee]] 66 | == No error gets ignored guarantee 67 | 68 | Noexcept will never ignore an error once it is reported. The handling of any given error may be postponed (e.g. using `< >>`), but eventually the program is required to recognize each and every error object and flag it as handled. Situations which would lead to a pending error being ignored are detected and result in a call to `std::terminate`. 69 | 70 | == Crash course 71 | 72 | [[example_FILE]] 73 | === Handling `FILE` errors 74 | 75 | [source,c++] 76 | ---- 77 | include::{sourcedir}/examples/FILE_ptr_example.cpp[tabsize=1] 78 | ---- 79 | <1> If an error is detected, `<>` associates the passed `std::error_code` object with the calling thread and converts to a `0` `FILE *` for the return value (as specified by the `<>` template). 80 | <2> `<>` checks for success and moves the return value of `open_file` (or the error it passed to `throw_`) into `r`. 81 | <3> `<>` returns a pointer to the error object (or `0` if it is not a `std::error_code`) and flags it as handled. 82 | <4> When `r` is destroyed, the error object is destroyed if handled, otherwise left intact for another `try_`/`catch_` up the call chain to handle. 83 | 84 | IMPORTANT: If unhandled errors remain at the time the current thread terminates, Noexcept calls `std::terminate()`. Use the default for the `catch_<>` function template to handle any error regardless of its type. 85 | 86 | *** 87 | 88 | [[example_ifstream]] 89 | === Handling `std::ifstream` errors 90 | 91 | This program shows how to use Noexcept with `std::ifstream`, and demonstrates the use of `BOOST_NOEXCEPT_CHECK` in error-neutral functions (see <> for details). 92 | 93 | [source,c++] 94 | ---- 95 | include::{sourcedir}/examples/ifstream_example.cpp[] 96 | ---- 97 | <1> `open_stream` returns a "good" `ifstream` or `std::error_code` (the `throw_` converts to an empty `ifstream`, as specified by the `<>` template). 98 | <2> The `BOOST_NOEXCEPT_CHECK` (on the previous line) would immediately return any pending errors to the caller using `return throw_()`, so here we can `assert(f.good())`. 99 | <3> The `read_line` function returns a `string` or another `std::error_code`. 100 | <4> Thanks to the `BOOST_NOEXCEPT_CHECK` in `read_line`, here we don't need to check for errors: any success or failure reported by `read_line` or `open_stream` will be propagated automatically. 101 | <5> Dispatch on known errors and print the appropriate message. 102 | <6> The commented-out section shows how `link:http://www.boost.org/doc/libs/release/libs/exception/doc/diagnostic_information.html[boost::diagnostic_information]()` could be used by a more complex program to report that `main()` could not recognize an error object that reached it. 103 | 104 | *** 105 | 106 | [[example_lua]] 107 | === Propagating errors across Lua functions 108 | 109 | This program demonstrates that errors reported by Noexcept survive intact across API and even programming language boundaries. From {CPP}, it calls a link:http://www.lua.org/[Lua] function, which calls back a {CPP} function. If the callback {CPP} function fails, the error is propagated all the way to `main()` where it is handled. 110 | 111 | [source,c++] 112 | ---- 113 | include::{sourcedir}/examples/lua_example.cpp[] 114 | ---- 115 | <1> Initialize the Lua interpreter and define a simple Lua function (provided as a C string literal) called `call_do_work`, which simply returns the value returned by the Lua-global `do_work` function, which is a {CPP} function. 116 | <2> In the {CPP} `do_work` function, in case of success we return `42` back to Lua. 117 | <3> In case of a failure, we pass `do_work_error` to `throw_` as usual, and call `lua_error`. 118 | <4> The `call_lua` function instructs the Lua interpreter to execute the `call_do_work` Lua function (which calls `do_work`). If `do_work` was successful, `call_lua` returns the answer; otherwise it forwards failures by `return throw_()`, as usual. 119 | <5> In case `do_work` fails, the reported error object (which at this point has been propagated from {CPP}, through Lua and back into {CPP}) is handled here. 120 | 121 | *** 122 | 123 | [[example_optional]] 124 | === Using `optional<>` as a return type 125 | 126 | This program demonstrates that Noexcept is compatible with `optional<>`, which is handy when we need a return value with an explicit empty state. 127 | 128 | [source,c++] 129 | ---- 130 | include::{sourcedir}/examples/c_api_example.cpp[] 131 | ---- 132 | <1> A C function which uses `int` status codes to indicate success or failure, and a `float` pointer to store the result. 133 | <2> A simple error type to report failures from the C API function. 134 | <3> The cumbersome `float` pointer interface of the `C` function is converted to `boost::optional`. The `<>` returns an empty `optional` in case of failure (as specified by the `<>` template). 135 | <4> `<>` both checks for success and moves the return value of `erratic_caller` into `r`. 136 | <5> `<>` returns a pointer to the error object (or `0` if it can not be converted to `erratic_error`) and flags it as handled. 137 | 138 | == Synopsis 139 | 140 | === boost/noexcept.hpp 141 | 142 | include::{sourcedir}/adoc/synopses/noexcept.hpp.adoc[] 143 | 144 | === boost/noexcept/throw.hpp 145 | 146 | include::{sourcedir}/adoc/synopses/throw.hpp.adoc[] 147 | 148 | === boost/noexcept/try.hpp 149 | 150 | include::{sourcedir}/adoc/synopses/try.hpp.adoc[] 151 | 152 | == Reference 153 | 154 | [[throw_]] 155 | === Report: `throw_` 156 | [[throw_return]] 157 | 158 | include::{sourcedir}/adoc/synopses/throw.hpp.adoc[] 159 | 160 | `throw_` is a utility class designed to be used in `return` expressions to indicate failure: 161 | 162 | [source,c++] 163 | ---- 164 | return throw_( my_error() ); 165 | ---- 166 | or 167 | [source,c++] 168 | ---- 169 | return throw_( std::error_code { 42, my_domain } ); 170 | ---- 171 | The passed object becomes the current error for the calling thread. The error remains current until captured into a `<>` object (see `<>`). For threads that have a current error, `<>()` returns `true`. 172 | 173 | The `throw_` object itself converts to any type so it can be used in `return` expressions in any function: if the function return type is `T`, the returned value is `throw_return::value()`. 174 | 175 | The `throw_return` template may be specialized to specify the value to be returned to the caller when `throw_` is used to report a failure. The main template is defined such that: 176 | 177 | * If `T` is not `bool` and `std::is_integral::value` is `true`, then `throw_return::value()` returns `static_cast(-1)`. 178 | * Otherwise it returns `T()`. 179 | 180 | The `throw_` class has the following members: 181 | 182 | *** 183 | 184 | [[throw_ctor]] 185 | ==== throw_ constructors 186 | [source,c++] 187 | ---- 188 | template 189 | throw_( E && e ) noexcept; 190 | 191 | template 192 | throw_( E && e, char const * file, int line, char const * function ) noexcept; 193 | 194 | #define THROW_(e)\ 195 | ::boost::noexcept::throw_(e,__FILE__,__LINE__,BOOST_CURRENT_FUNCTION) 196 | ---- 197 | 198 | Preconditions: :: `!<>()` 199 | Effects: :: `e` becomes the current error for the calling thread. If `file`, `line` and `function` are specified, that information is recorded for diagnostic purposes (use with the `THROW_` macro). 200 | Postconditions: :: `has_current_error()` 201 | 202 | [source,c++] 203 | ---- 204 | throw_() noexcept; 205 | ---- 206 | Effects: :: None: the default constructor is used to continue propagating the current error. 207 | 208 | NOTE: This is different from the `<>` member function of `result`, which is used in return expressions to propagate the error stored in the `result` object, rather than the current error. 209 | 210 | *** 211 | 212 | [[throw_operator_T]] 213 | ==== throw_::operator T() 214 | [source,c++] 215 | ---- 216 | template 217 | operator T() noexcept; 218 | ---- 219 | Preconditions: :: `<>()` 220 | Returns: :: `throw_return::value()`. 221 | 222 | *** 223 | 224 | [[has_current_error]] 225 | ==== has_current_error() 226 | [source,c++] 227 | ---- 228 | bool has_current_error() noexcept; 229 | ---- 230 | Returns: :: If `<>` was used to set the current error for the calling thread, and if that error has not been captured by a `<>` object (see `<>`), `has_current_error` returns `true`. Otherwise it returns `false`. 231 | 232 | *** 233 | 234 | [[BOOST_NOEXCEPT_CHECK]] 235 | [source,c++] 236 | ---- 237 | #define BOOST_NOEXCEPT_CHECK\ 238 | { if( ::boost::noexcept_::has_current_error() )\ 239 | return ::boost::noexcept_::throw_(); } 240 | 241 | #define BOOST_NOEXCEPT_CHECK_VOID\ 242 | { if( ::boost::noexcept_::has_current_error() )\ 243 | return; } 244 | ---- 245 | These macros are designed to be used right after the opening `{` of a function to return immediately in case previous operations have resulted in an error. Please see <> for a motivating example. 246 | 247 | {blank} + 248 | 249 | [[try_]] 250 | === Handle: `try_`/`catch_` 251 | 252 | include::{sourcedir}/adoc/synopses/try.hpp.adoc[] 253 | 254 | Pass a function return value directly to `try_` (or use `current_error()` in the case of void functions) if you want to _handle_ the errors it could report: 255 | 256 | * If the function was successful (`has_current_error()` is `false`), use the `<>` member function of the returned `result` object to obtain the returned value. 257 | * Otherwise instead of storing a `T` result, the returned `result` object captures the current error, which leaves the calling thread without a current error. Use the `<>` member function template to handle specific errors (recognized by their type). 258 | 259 | The following convenient syntax is supported: 260 | 261 | [source,c++] 262 | ---- 263 | if( auto r=try_(f()) ) { 264 | //Success, use r.get() to obtain the value returned by f() 265 | } else { 266 | //Handle error, see result::catch_<> 267 | } 268 | ---- 269 | 270 | The `result` template defines the following public members: 271 | 272 | *** 273 | 274 | [[result_dtor]] 275 | ==== result destructor 276 | [source,c++] 277 | ---- 278 | ~result() noexcept; 279 | ---- 280 | * At the time a `result` object for which `<>()` is `true` is destroyed: 281 | ** If `<>()` is also `true`, `result` calls `std::terminate()`. 282 | ** Otherwise the error contained in `*this` becomes the current error for the calling thread again. + 283 | * Otherwise (`has_unhandled_error()` is `false`), if `<>()` is `true`, the captured error object is destroyed. 284 | * Otherwise the captured `T` value is destroyed. 285 | 286 | *** 287 | 288 | [[result_has_error]] 289 | ==== result::has_error() 290 | [source,c++] 291 | ---- 292 | bool has_error() const noexcept; 293 | ---- 294 | Returns: :: `true` if `*this` contains an error object, `false` if it contains a value. 295 | 296 | *** 297 | 298 | [[result_has_unhandled_error]] 299 | ==== result::has_unhandled_error() 300 | [source,c++] 301 | ---- 302 | bool has_unhandled_error() const noexcept; 303 | ---- 304 | Returns: :: `true` if `has_error()` and the error has not yet been handled, `false` otherwise. 305 | 306 | NOTE: The error contained in a `result` is marked as handled when `<>` is called, but only if it can be converted to type `E`. 307 | 308 | *** 309 | 310 | [[result_operator_bool]] 311 | ==== result::operator bool() 312 | [source,c++] 313 | ---- 314 | explicit operator bool() const noexcept; 315 | ---- 316 | Returns: :: Equivalent to `return !<>()`. 317 | 318 | *** 319 | 320 | [[catch_]] 321 | ==== result::catch_() 322 | [source,c++] 323 | ---- 324 | template 325 | E * catch_() noexcept; 326 | ---- 327 | Returns: :: If `<>()` is `true` _and_ the error object contained in `*this` is of type `E`, returns a pointer to that object; otherwise returns `0`. The returned pointer becomes invalid if the `result` object is moved or destroyed. 328 | Effects: :: The error object captured into `*this` is marked as handled but only if it is of type `E`; see `<>()`. 329 | 330 | NOTE: `catch_<>` (using the default for `E`) serves similar function to `catch(pass:[...])` when handling exceptions, however in Noexcept any error object passed to `throw_` (even objects of built-in types) internally will have a `std::exception` component, so `catch_<>` is able to return a valid `std::exception` pointer regardless of the type passed to `throw_`. 331 | 332 | *** 333 | 334 | [[result_throw_]] 335 | ==== result::throw_() 336 | [source,c++] 337 | ---- 338 | throw_() noexcept; 339 | ---- 340 | Preconditions: :: `<>`(). 341 | Effects: :: The error object stored into `*this` becomes the current error for the calling thread again. 342 | Returns: :: An object of unspecified type which converts implicitly to any type for use in `return` expressions. Example usage: 343 | 344 | [source,c++] 345 | ---- 346 | if( auto r=try_(f()) ) { 347 | //success, use r.get() 348 | } else { 349 | //possibly handle some errors, then: 350 | return r.throw_(); //propagate the error object up the call chain 351 | } 352 | ---- 353 | 354 | *** 355 | 356 | [[result_throw_exception]] 357 | ==== result::throw_exception() 358 | [source,c++] 359 | ---- 360 | void throw_exception(); 361 | ---- 362 | 363 | Preconditions: :: `has_error()`. 364 | Effects: :: Throws the error object as a {CPP} exception. 365 | 366 | *** 367 | 368 | [[result_get]] 369 | ==== result::get() 370 | [source,c++] 371 | ---- 372 | //Not available in result: 373 | T const & get() const; 374 | T & get(); 375 | ---- 376 | Effects: :: If `!<>()`, `get()` returns a reference to the object passed to `<>` and moved into `*this`; otherwise `get()` calls `<>()`. 377 | 378 | *** 379 | 380 | [[result_try_]] 381 | ==== try_() 382 | [source,c++] 383 | ---- 384 | template 385 | result try_( T && res ) noexcept; 386 | 387 | result void_try_() noexcept; 388 | ---- 389 | 390 | Effects: :: 391 | + 392 | * If `!<>()`, `res` is moved into the returned `result` object, later accessible by a call to `<>()`. 393 | * Otherwise the current error for the calling thread is moved into the returned `result` object, later accessible by a call to `<>()`. 394 | Postconditions: :: `!has_current_error()`. 395 | 396 | NOTE: `result` fully encapsulates the result (value or error) of the function call and has no association with the calling thread, so `result` objects can be temporarily stored (e.g. in some `queue< result >`) or moved to a different thread before their contents is consumed; see <> for an example. 397 | 398 | [[programming_techniques]] 399 | == Programming techniques and guidelines 400 | 401 | === Selecting optimal function return types 402 | 403 | Noexcept is able to propagate failures without imposing a specific function return type, which is important because sometimes failures need to be propagated through third-party functions whose return types are beyond our control. 404 | 405 | When it is possible to take Noexcept into account in the design of function return value semantics, it is best to pick types that have a natural state (in the respective function domain) that can be used to detect failures without resorting to `<>()`. In this case Noexcept can stay completely out of the way, except when detected errors are being handled. 406 | 407 | There are many function return types which usually fit this guideline quite naturally. Here are a few examples: 408 | 409 | * `bool` 410 | * `T *` (notably `FILE *`) 411 | * `shared_ptr` 412 | * `unique_ptr` 413 | * `optional` 414 | * `ifstream` 415 | * `HANDLE` (Windows system objects) 416 | * `int` and any integral type (-1 is invalid count, length, magnitude, size, width, height, file descriptor, etc.) 417 | 418 | TIP: `shared_ptr` deserves special mentioning here because the possibility to use custom deleters make it extremely deployable as the return value from any factory function which allocates any type of resource. 419 | 420 | Other types that have an empty state may require more careful consideration: 421 | 422 | * `string` 423 | * `vector` (and any other container type) 424 | 425 | Case in point, `string` works great with functions that return a file name or user name because these may not be empty. When an empty `string` is a valid return value, requiring the caller to use `has_current_error()` to check for error is usually the best choice, but sometimes it might make sense to use a wrapper, e.g. `optional` or `shared_ptr >`. 426 | 427 | *** 428 | 429 | === Enforcing successful control flow 430 | 431 | Consider the following declarations: 432 | 433 | [source,c++] 434 | ---- 435 | std::vector read_file( FILE * f ) noexcept; 436 | int count_words( std::vector const & lines ) noexcept; 437 | ---- 438 | The `count_words` function is designed to take as input the result returned by `read_file`, yet in a calling function we can not use function composition and simply say: 439 | 440 | [source,c++] 441 | ---- 442 | int count_words_in_file( FILE * f ) noexcept { 443 | return count_words( read_file(f) ); 444 | } 445 | ---- 446 | That's because we must check if `read_file` was successful. Specifically, we need to be able to tell the difference between `read_file` returning an empty `vector` (using `<>`) because it failed to read the file, and it returning an empty `vector` because the file was empty. 447 | 448 | In terms of Noexcept, we could use: 449 | 450 | [source,c++] 451 | ---- 452 | int count_words_in_file( FILE * f ) noexcept { 453 | if( auto r = try_( read_file(f) ) ) 454 | return count_words( r.get() ); 455 | else 456 | return r.throw_(); 457 | } 458 | ---- 459 | This is not too bad: at least errors are pushed out of the way, and we don't have to worry about how to propagate all possible errors up the call chain. Still, requiring users to check for errors every time they call `read_file` is prone to errors -- and in this case it is particularly annoying, since `count_words` and `read_file` fit so well together. 460 | 461 | Using Noexcept we can do better: we can have functions like `count_words` check for errors themselves. That's because error-neutral contexts can propagate errors up the call stack using `return <>()`: 462 | 463 | [source,c++] 464 | ---- 465 | if( has_current_error() ) 466 | return throw_(); 467 | ---- 468 | 469 | For convenience, this handy `if` statement -- surrounded by `{` `}` -- is provided as the `BOOST_NOEXCEPT_CHECK` macro, which allows us to define `count_words` as: 470 | 471 | [source,c++] 472 | ---- 473 | int count_words( std::vector const & lines ) noexcept { 474 | BOOST_NOEXCEPT_CHECK 475 | //No errors, continue with counting words. 476 | .... 477 | return n; //the number of words in lines 478 | } 479 | ---- 480 | 481 | In turn, this allows the calling `count_words_in_file` function to safely use simple function composition: 482 | 483 | [source,c++] 484 | ---- 485 | int count_words_in_file( FILE * f ) noexcept { 486 | return count_words( read_file(f) ); 487 | } 488 | ---- 489 | 490 | NOTE: The macro `BOOST_NOEXCEPT_CHECK_VOID` can be used similarly in `void` functions. 491 | 492 | *** 493 | 494 | === Automatically returning early in case of a failure in a sequence of function calls 495 | 496 | As demonstrated above, the `BOOST_NOEXCEPT_CHECK` macro can be used to automate error checking when using function composition (calling one function with the result of another, as in `f(g())`. 497 | 498 | While `void` function calls can not be composed, the `BOOST_NOEXCEPT_CHECK_VOID` macro could be used in a similar manner: 499 | 500 | [source,c++] 501 | ---- 502 | void f1() noexcept { 503 | BOOST_NOEXCEPT_CHECK_VOID 504 | .... 505 | if( !f1_succeeds ) 506 | return (void) throw_( error1() ); 507 | } 508 | 509 | void f2() noexcept { 510 | BOOST_NOEXCEPT_CHECK_VOID 511 | .... 512 | if( !f2_succeeds ) 513 | return (void) throw_( error2() ); 514 | } 515 | 516 | void f3() noexcept { 517 | BOOST_NOEXCEPT_CHECK_VOID 518 | .... 519 | if( !f3_succeeds ) 520 | return (void) throw_( error3() ); 521 | } 522 | 523 | void caller() noexcept { 524 | //No need to check for errors here, since f1, f2 and f3 each check for errors: 525 | f1(); 526 | f2(); 527 | f3(); 528 | } 529 | ---- 530 | 531 | This works, but as is the case when using function composition with non-`void` functions, all 3 functions will be called, even though they would return immediately in case the previous one failed. 532 | 533 | Can Noexcept help avoid calling `f2` or `f3` if `f1` fails? Yes it can, though we must refactor the `void` functions to return `bool` (indicating success or failure) which is a good idea anyway for any `void` function which could fail: 534 | 535 | [source,c++] 536 | ---- 537 | bool f1() noexcept { 538 | BOOST_NOEXCEPT_CHECK 539 | .... 540 | if( f1_succeeds ) 541 | return true; 542 | else 543 | return throw_( error1() ); 544 | } 545 | 546 | bool f2() noexcept { 547 | BOOST_NOEXCEPT_CHECK 548 | .... 549 | if( f2_succeeds ) 550 | return true; 551 | else 552 | return throw_( error2() ); 553 | } 554 | 555 | bool f3() noexcept { 556 | BOOST_NOEXCEPT_CHECK 557 | .... 558 | if( f3_succeeds ) 559 | return true; 560 | else 561 | return throw_( error3() ); 562 | } 563 | ---- 564 | 565 | With this change, the `caller` function can simply use `operator &&` to both propagate errors from any of `f1`, `f2` or `f3` _and_ return early if any of them fails: 566 | 567 | [source,c++] 568 | ---- 569 | bool caller() noexcept { 570 | return 571 | f1() && 572 | f2() && 573 | f3(); 574 | } 575 | ---- 576 | 577 | *** 578 | 579 | === Obtaining diagnostic information from unknown errors 580 | 581 | Noexcept is compatible with `link:http://www.boost.org/doc/libs/release/libs/exception/doc/diagnostic_information.html[boost::diagnostic_information]()` without being coupled with it. 582 | 583 | TIP: `boost::diagnostic_information` does not require exception handling to be enabled. 584 | 585 | One possible use case is to print diagnostic information about unhandled errors that reach the `main` function: 586 | 587 | [source,c++] 588 | ---- 589 | int main() { 590 | if( auto r=try_(do_work(....)) ) { 591 | return 0; 592 | } else if( auto err=r.catch_() ) { 593 | //print an error message about error1, then return to the OS 594 | return 1; 595 | } else if( auto err=r.catch_() ) { 596 | //print an error message about error2, then return to the OS 597 | return 2; 598 | } else { 599 | //Unknown error! 600 | auto err=r.catch_<>(); 601 | assert(err!=0); //catch_<> will bind any error type 602 | std::cerr << 603 | "Unhandled error reached main!\n" 604 | "Diagnostic information follows:\n" << 605 | boost::diagnostic_information(*err); 606 | return 3; 607 | } 608 | } 609 | ---- 610 | Calling `boost::diagnostic_information` will probe the passed object and extract as much useful information as possible. This includes the location the error was reported (if available: file, line number, function name), the output from `std::exception::what()`, as well as any `boost::exception`-style error info. An example output may look like this: 611 | 612 | ---- 613 | test/diagnostic_information_test.cpp(26): Throw in function int f1() 614 | Dynamic exception type: boost::noexcept_::noexcept_detail::class_dispatch::type 615 | std::exception::what: my_error 616 | [answer_*] = 42 617 | ---- 618 | 619 | *** 620 | 621 | === Augmenting error objects in error-neutral contexts 622 | 623 | Consider the following error type: 624 | 625 | [source,c++] 626 | ---- 627 | class file_read_error { 628 | std::string fn_; 629 | public: 630 | explicit file_read_error( std::string && fn ) noexcept: 631 | fn_(std::move(fn)) { } 632 | std::string const & file_name() const noexcept { return fn_; } 633 | }; 634 | ---- 635 | 636 | A call to `<>` that handles `file_read_error`: 637 | 638 | [source,c++] 639 | ---- 640 | if( auto e=r.catch_() ) { 641 | std::cerr << "Error reading \"" << e->file_name() << "\"\n"; 642 | } 643 | ---- 644 | 645 | Finally, a function that may report a `file_read_error` using Noexcept: 646 | 647 | [source,c++] 648 | ---- 649 | bool read_file(FILE * f) noexcept { 650 | .... 651 | size_t nr=fread(buf,1,count,f); 652 | if(ferror(f)) 653 | return throw_(file_read_error("???")); //File name not available here! 654 | .... 655 | } 656 | ---- 657 | 658 | The issue is that the `catch_` needs a file name, but at the point of the `throw_` a file name is not available (only a `FILE` pointer is). In general the error might be detected in a library which can not assume that a meaningful name is available for any `FILE` it reads, even if a program that uses the library could reasonably make the same assumption. 659 | 660 | Using `link:http://www.boost.org/doc/libs/release/libs/exception/doc/error_info.html[boost::error_info]` a file name may be added to any error after it has been passed to `throw_`, while anything available at the point of the `throw_` (e.g. `errno`) may be stored in the original object​: 661 | 662 | [source,c++] 663 | ---- 664 | class file_io_error: public boost::exception { }; 665 | class file_open_error: public virtual file_io_error { }; 666 | class file_read_error: public virtual file_io_error { }; 667 | typedef boost::error_info xi_file_name; 668 | typedef boost::error_info xi_errno; 669 | 670 | using namespace boost::noexcept_; 671 | 672 | bool read_file(FILE * f) noexcept { 673 | .... 674 | size_t nr=fread(buf,1,count,f); 675 | if(ferror(f)) 676 | return throw_( file_read_error() << xi_errno(errno) ); 677 | .... 678 | } 679 | 680 | bool process_file(char const * name) noexcept { 681 | if( FILE * fp=fopen(name,"rt") ) { 682 | std::shared_ptr f(fp,fclose); 683 | 684 | if( auto r=try_(read_file(fp)) ) { 685 | //success! 686 | return true; 687 | } else if( auto err=r.catch_() ) { 688 | //Augment any passing error: 689 | (*err) << xi_file_name(name); 690 | return r.throw_(); 691 | } 692 | 693 | } else { 694 | 695 | //report failure to open the file: 696 | return throw_( file_open_error() << xi_file_name(name) ); 697 | 698 | } 699 | } 700 | ---- 701 | 702 | Now the final `catch_` may look like this: 703 | 704 | [source,c++] 705 | ---- 706 | bool do_work() noexcept { 707 | return process_file("file.txt"); 708 | } 709 | 710 | int main() { 711 | if( auto r=try_(do_work()) ) { 712 | 713 | std::cout << "Success!\n"; 714 | return 0; 715 | 716 | } else if( auto err=r.catch_ { 717 | 718 | std::cerr << "I/O error!\n"; 719 | std::string const * fn=boost::get_error_info(); 720 | assert(fn!=0); //In this program all files have names. 721 | std::cerr << "File name: " << *fn << "\n"; 722 | return 1; 723 | 724 | } else { 725 | 726 | //Unknown error! 727 | auto err=r.catch_<>(); 728 | assert(err!=0); //catch_<> will bind any error type 729 | std::cerr << 730 | "Unhandled error reached main!\n" 731 | "Diagnostic information follows:\n" << 732 | boost::diagnostic_information(*err); 733 | return 2; 734 | 735 | } 736 | } 737 | ---- 738 | 739 | NOTE: `boost::error_info` does not require exception handling. Noexcept is not coupled with `boost::error_info`. 740 | 741 | *** 742 | 743 | [[example_threads]] 744 | === Passing "value-or-error" between threads using `result` 745 | 746 | The usual Noexcept error handling semantics (<>, <>, <>) are a poor match for use cases where the value (or the error) can not be consumed immediately, for example when it is produced asynchronously in a worker thread, or when values are temporarily stored into a queue for later consumption, in which case the queue must have the ability to hold multiple outstanding errors. 747 | 748 | The `result` class template is designed for use in such cases. For example, suppose we have this function which simulates a computation which may succeed or fail: 749 | 750 | [source,c++] 751 | ---- 752 | int compute() noexcept { 753 | if( rand()%2 ) 754 | return 42; 755 | else 756 | return throw_(compute_error()); 757 | } 758 | ---- 759 | 760 | As usual, it uses `<>` to report failures. Using `std::future >` to collect the results from many concurrent calls to `compute` is easy: 761 | 762 | [source,c++] 763 | ---- 764 | std::vector > > fut; 765 | 766 | //Create and launch 100 tasks: 767 | std::generate_n( std::inserter(fut,fut.end()), 100, [ ] { 768 | 769 | //Call compute() and capture the result into a result: 770 | std::packaged_task()> task( [ ] { 771 | return try_(compute()); 772 | } ); 773 | 774 | //Get the future and kick off the task 775 | auto f=task.get_future(); 776 | std::thread(std::move(task)).detach(); 777 | return f; 778 | 779 | } ); 780 | ---- 781 | 782 | We can now `wait` on each future then pass the collected `result` object to `<>` to either get the successfully computed value or the failure, now transported into the main thread: 783 | 784 | [source,c++] 785 | ---- 786 | for( auto & f:fut ) { 787 | f.wait(); 788 | if( auto r=try_(f.get()) ) 789 | std::cout << "Success! Answer=" << r.get() << '\n'; 790 | else if( auto err=r.catch_() ) 791 | std::cout << "Failure!\n"; 792 | } 793 | ---- 794 | 795 | [[alternatives]] 796 | == Alternatives to Noexcept 797 | 798 | Besides Noexcept, there are multiple other {CPP} libraries and proposals for solving the problem of error reporting without using exceptions: 799 | 800 | * link:https://github.com/viboes/std-make/blob/master/doc/proposal/expected/d0323r2.md[D0323R2] by Vicente J. Botet Escriba proposes a class template `expected`, designed to be used as a return type in functions that could fail, the general idea being that in case of success the function returns a result of type `T`, or else it returns a reason for the failure, of type `E`. 801 | 802 | * link:https://github.com/pdimov/variant2[variant2] by Peter Dimov is a never-valueless implementation of `std::variant` and an extended version of `expected`. The class `boost::variant2::expected` represents the return type of an operation that may potentially fail. It contains either the expected result of type `T`, or a reason for the failure, of one of the error types in `E...` (internally, this is stored as `variant`). 803 | 804 | * link:https://ned14.github.io/outcome/[Outcome] by Niall Douglas recently underwent a formal Boost review. Unlike `expected` and `expected`, in `outcome` the error is either of type `std::error_code` or an exception object, transported as a `std::exception_ptr`. The motivation for this more restrictive interface is to force the user to "be a good citizen" and communicate `noexcept` failures in terms of `std::error_code`, in order to facilitate interoperability between different libraries (with the added ability to capture exceptions in case some lower level library throws). 805 | 806 | * link:http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0262r1.html[P0262] by Lawrence Crowl and Chris Mysen proposes a control helper called `status_value`, which differs from all of the above libraries in that the returning function is expected to always provide a status _and_ a value. This can be useful to communicate conditions which did not lead to a failure but are still likely to be of interest to the caller. 807 | 808 | [[configuration]] 809 | == Macros and configuration 810 | 811 | ---- 812 | BOOST_NOEXCEPT_ASSERT 813 | ---- 814 | All assertions in Noexcept use this macro; if not `#defined`, Noexcept header files `#define` it as link:http://www.boost.org/doc/libs/release/libs/assert/assert.html[`BOOST_ASSERT`]. 815 | 816 | *** 817 | 818 | ---- 819 | BOOST_NOEXCEPT_NO_THREADS 820 | ---- 821 | If `#defined`, Noexcept assumes that static storage is equivalent to thread-local storage. 822 | 823 | If `BOOST_NOEXCEPT_NO_THREADS` is not explicitly `#defined`, thread-safety depends on `BOOST_NO_THREADS`. 824 | 825 | *** 826 | 827 | ---- 828 | BOOST_NOEXCEPT_THREAD_LOCAL(type,object) 829 | ---- 830 | This macro is used to define objects with static thread-local storage; if not `#defined`, Noexcept header files `#define` it as: 831 | 832 | [source,c++] 833 | ---- 834 | #define BOOST_NOEXCEPT_THREAD_LOCAL(type,object) static thread_local type object 835 | ---- 836 | 837 | or, under `BOOST_NOEXCEPT_NO_THREADS`, as: 838 | 839 | [source,c++] 840 | ---- 841 | #define BOOST_NOEXCEPT_THREAD_LOCAL(type,object) static type object 842 | ---- 843 | 844 | *** 845 | 846 | ---- 847 | BOOST_NOEXCEPT_NO_RTTI 848 | ---- 849 | If `#defined`, Noexcept will not use `dynamic_cast` (and errors bound by `< >>()` must match `T` exactly). 850 | 851 | If `BOOST_NOEXCEPT_NO_RTTI` is not explicitly `#defined`, the availability of RTTI is determined using `BOOST_NO_RTTI`. 852 | 853 | *** 854 | 855 | ---- 856 | BOOST_NOEXCEPT_THROW_EXCEPTION 857 | ---- 858 | All Noexcept functions are declared as `noexcept`, except for `<>()` which throws exceptions using this macro. If not `#defined`, Noexcept header files `#define` it as link:http://www.boost.org/doc/libs/release/libs/exception/doc/BOOST_THROW_EXCEPTION.html[`BOOST_THROW_EXCEPTION`]. 859 | 860 | *** 861 | 862 | ---- 863 | BOOST_NOEXCEPT_NO_EXCEPTION_INFO 864 | ---- 865 | 866 | While Noexcept is not coupled with `boost::error_info`, by default it is coupled with a tiny part of Boost which is needed to make `boost::error_info` work if used. Defining this macro removes that dependency. 867 | 868 | == Building and installation 869 | 870 | Noexcept is a header-only library and requires no building or installation. 871 | 872 | == Building the unit tests and the examples 873 | 874 | The unit tests and the examples can be built within the Boost framework: clone Noexcept under the `libs` subdirectory in your boost installation, then `cd` into `noexcept/test` and execute `b2` as usual. 875 | 876 | [[benchmark]] 877 | == Benchmark 878 | 879 | Error handling library benchmarks are tricky to write and with limited usefulness as design exploration tool, but since _speed!!!_ is a hot topic of discussion, Noexcept includes a benchmark program called `deep_stack`. It can propagate different types of return values and error objects across configurable depth of function calls. In addition: 880 | 881 | - If exception handling is disabled, `deep_stack` uses Noexcept to propagate errors, otherwise it uses C++ exceptions. 882 | - By default inlining is disabled, which simulates propagating errors across different compilation units. If `BOOST_NOEXCEPT_INLINE_FORCEINLINE` is `#defined`, the function calls are inlined, which simulates propagating errors within a single compilation unit. 883 | 884 | Below is the result of running `deep_stack`, compiled with `Apple LLVM version 9.0.0 (clang-900.0.39.2)`, on an old-ish Macbook Pro, using a variety of compiler command lines (assuming the current working directory is `${BOOST_ROOT}/libs/noexcept/benchmark`). 885 | 886 | The source code of `deep_stack` is available https://github.com/zajo/boost-noexcept/blob/master/benchmark/deep_stack.cpp[here]. 887 | 888 | ==== 889 | .clang++ -O3 -I../include -I../../.. ./deep_stack.cpp -fno-exceptions 890 | ---- 891 | Using Noexcept <1> 892 | 893 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = int], count=420000, success %=10: 14ms 894 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = int], count=420000, success %=90: 9ms 895 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = int], count=420000, success %=10: 26ms 896 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = int], count=420000, success %=90: 22ms 897 | 898 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=10: 20ms 899 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=90: 16ms 900 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=10: 83ms 901 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=90: 76ms 902 | ---- 903 | <1> Exception handling *disabled*, inlining *disabled*, using Noexcept to propagate errors across 10 or 30 stack frames, with 10% or 90% chance of the operation being successful (vs. failing), returning `int` or `string` in case of success. 904 | ==== 905 | 906 | ==== 907 | .clang++ -O3 -I../include -I ../../.. deep_stack.cpp 908 | ---- 909 | Using C++ exception handling <1> 910 | 911 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = int], count=420000, success %=10: 371ms 912 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = int], count=420000, success %=90: 45ms 913 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = int], count=420000, success %=10: 397ms 914 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = int], count=420000, success %=90: 57ms 915 | 916 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=10: 792ms 917 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=90: 95ms 918 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=10: 1708ms 919 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=90: 239ms 920 | ---- 921 | <1> Exception handling *enabled*, inlining *disabled*, using `throw` to propagate errors across 10 or 30 stack frames, with 10% or 90% chance of the operation being successful (vs. failing), returning `int` or `string` in case of success. 922 | ==== 923 | 924 | ==== 925 | .clang++ -O3 -I../include -I ../../.. deep_stack.cpp -fno-exceptions -DBENCHMARK_INLINE=BOOST_NOEXCEPT_INLINE_FORCEINLINE 926 | ---- 927 | Using Noexcept <1> 928 | 929 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = int], count=420000, success %=10: 11ms 930 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = int], count=420000, success %=90: 6ms 931 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = int], count=420000, success %=10: 11ms 932 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = int], count=420000, success %=90: 7ms 933 | 934 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=10: 11ms 935 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=90: 8ms 936 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=10: 12ms 937 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=90: 7ms 938 | ---- 939 | <1> Exception handling *disabled*, inlining *enabled*, using Noexcept to propagate errors across 10 or 30 levels of inlined function calls, with 10% or 90% chance of the operation being successful (vs. failing), returning `int` or `string` in case of success. 940 | ==== 941 | 942 | ==== 943 | .clang++ -O3 -I../include -I ../../.. deep_stack.cpp -DBENCHMARK_INLINE=BOOST_NOEXCEPT_INLINE_FORCEINLINE 944 | ---- 945 | Using C++ exception handling <1> 946 | 947 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = int], count=420000, success %=10: 339ms 948 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = int], count=420000, success %=90: 38ms 949 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = int], count=420000, success %=10: 337ms 950 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = int], count=420000, success %=90: 39ms 951 | 952 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=10: 334ms 953 | void test_case(int, int) [Depth = 10, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=90: 41ms 954 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=10: 335ms 955 | void test_case(int, int) [Depth = 30, ErrorType = my_error, ValueType = std::__1::basic_string], count=420000, success %=90: 40ms 956 | ---- 957 | <1> Exception handling *enabled*, inlining *enabled*, using `throw` to propagate errors across 10 or 30 levels of inlined function calls, with 10% or 90% chance of the operation being successful (vs. failing), returning `int` or `string` in case of success. 958 | ==== 959 | 960 | [[qanda]] 961 | == Q&A 962 | 963 | [qanda] 964 | Does Noexcept allocate memory dynamically?:: No. 965 | 966 | Does Noexcept require RTTI?:: No. 967 | 968 | When using exception handling, there can be multiple active exception objects in each thread. Does Noexcept support multiple active errors?:: Yes, the current error object can be captured using `<>`, at which point the calling thread is left without a current error. 969 | + 970 | However, it is up to the user to call `try_`: the code below is ill-formed, assuming that both function calls may fail -- that is, return using `<>(my_error())`, for some user-defined type `my_error`: 971 | + 972 | auto r1=f1(); 973 | auto r2=f2(); 974 | + 975 | The correct code would ensure that at the time `f2()` is called there is no current error for the calling thread. This could be achieved either by checking the return value of `f1()` (assuming `r1` converts to `bool` to indicate success or failure): 976 | + 977 | if( auto r1=f1() ) { 978 | auto r2=f2(); //okay for f2 to fail (f1 didn't) 979 | .... 980 | } else { 981 | return r1.throw_(); //let the caller deal with failures 982 | } 983 | + 984 | or by capturing `f1` failures using `try_`: 985 | + 986 | auto r1=try_(f1()); //capture possible f1 failures 987 | auto r2=f2(); //okay, even if both f1 and f2 fail 988 | + 989 | NOTE: The above is not unlike when using exception handling: if you want to call `f2` even if `f1` throws, you'd have to call `f1` from within a `try`/`catch`. 990 | + 991 | (Nothrow asserts on `!<>()` at the time an error object is passed to `<>`). 992 | 993 | Does this mean that I should always use `try_`?:: No, only use `try_` if you want to handle errors (see `<>`). In error-neutral contexts, in case of errors simply return `<>()` without argument: 994 | + 995 | if( auto r=f() ) { 996 | //Success -- use r 997 | } else { 998 | return r.throw_(); //Something went wrong 999 | } 1000 | + 1001 | The above assumes that `r` converts to `bool` to indicate success or failure (there are many types than can be used this way). If that is not the case, ideally you would still be able to inspect the returned value to detect failures. If that is also not possible, use `has_current_error()`. 1002 | 1003 | What happens if I forget to check for errors?:: Then you'd be using a bad value. For example, if a function returns a `shared_ptr` and you forget to check for success, attempting to dereference it leads to undefined behavior (segfault). 1004 | + 1005 | NOTE: If control is entering a scope where exception handling is enabled, you can convert Noexcept errors to exceptions by `try_(f()).get()`, which throws on error. 1006 | + 1007 | That said, it is sometimes possible to get away with not checking for errors in error-neutral contexts, see <>. 1008 | 1009 | What happens if `<>` is called without any error being present?:: That is fine, in this case the value passed to `try_` will simply be moved into the returned `<>` object, where it can be accessed using the `<>()` member function. 1010 | 1011 | Has Noexcept been benchmarked?:: Not yet, but performance is an important design goal in Noexcept and I welcome any data or analysis contributions. That said, except in functions that handle errors (see `<>`), Noexcept has no effect on the speed of passing return values up the call chain. 1012 | 1013 | Doesn't Noexcept make it too easy to forget to check for errors? For example, if a function that may fail returns a type without explicit empty state (like `int`), there is nothing to protect the user from ignoring errors by mistake!:: Noexcept does protect the user from this type of mistakes, but it can't do it on the spot; see the <>. 1014 | + 1015 | If this does not suffice, don't write functions that return `int` to indicate success or failure. However, consider that wrapping the `int` in a user-defined type adds complexity which may or may not be appropriate. 1016 | 1017 | Passing errors through thread-local storage doesn't work if the consumption of the result of a function call (success or error) can not be consumed immediately. What if I need to postpone that until later?:: The `< >>` template solves this exact problem. See <>. 1018 | 1019 | == Acknowledgements 1020 | 1021 | Noexcept was inspired by the discussions on the Boost Developers mailing list during the review of the link:https://ned14.github.io/outcome/[Outcome] library by Niall Douglas. 1022 | 1023 | This second evolution of Noexcept incorporates valuable positive and negative feedback received after Noexcept was announced. Special thanks to Bjorn Reese and Peter Dimov for pointing out the previous inability of Noexcept to capture results into a `result` with a hard value-or-error invariant, to Gavin Lambert for helping me discover that that `throw_()` _was_ broken, and to Andrzej Krzemienski whose criticisms lead to improvements in the Noexcept mechanisms for writing error-neutral functions. 1024 | 1025 | *** 1026 | 1027 | [.small]#_(C) 2017-2018 Emil Dotchevski_# 1028 | -------------------------------------------------------------------------------- /adoc/synopses/noexcept.hpp.adoc: -------------------------------------------------------------------------------- 1 | [source,c++] 2 | .#include 3 | ---- 4 | #include 5 | #include 6 | ---- 7 | -------------------------------------------------------------------------------- /adoc/synopses/throw.hpp.adoc: -------------------------------------------------------------------------------- 1 | [source,c++] 2 | .#include 3 | ---- 4 | namespace boost { namespace noexcept_ { 5 | 6 | template 7 | struct throw_return { 8 | 9 | static T value() noexcept; 10 | 11 | } 12 | 13 | class throw_ { 14 | 15 | throw_( throw_ const & )=delete; 16 | throw_ & operator=( throw_ const & )=delete; 17 | 18 | public: 19 | 20 | template 21 | throw_( E && e ) noexcept; 22 | 23 | template 24 | throw_( E && e, char const * file, int line, char const * function ) noexcept; 25 | 26 | throw_() noexcept { } 27 | 28 | template 29 | operator T() noexcept; 30 | 31 | }; 32 | 33 | bool has_current_error() noexcept; 34 | 35 | } } 36 | 37 | #define THROW_(e)\ 38 | ::boost::noexcept::throw_(e,__FILE__,__LINE__,BOOST_CURRENT_FUNCTION) 39 | 40 | #define BOOST_NOEXCEPT_CHECK\ 41 | { if( ::boost::noexcept_::has_current_error() )\ 42 | return ::boost::noexcept_::throw_(); } 43 | 44 | #define BOOST_NOEXCEPT_CHECK_VOID\ 45 | { if( ::boost::noexcept_::has_current_error() )\ 46 | return; } 47 | ---- 48 | -------------------------------------------------------------------------------- /adoc/synopses/try.hpp.adoc: -------------------------------------------------------------------------------- 1 | [source,c++] 2 | .#include 3 | ---- 4 | namespace boost { namespace noexcept_ { 5 | 6 | template 7 | class result { 8 | 9 | result( result const & )=delete; 10 | result & operator=( result const & )=delete; 11 | 12 | public: 13 | 14 | ~result() noexcept; 15 | 16 | bool has_error() const noexcept; 17 | bool has_unhandled_error const noexcept; 18 | 19 | explicit operator bool() const noexcept; 20 | 21 | template 22 | E * catch_() noexcept; 23 | 24 | throw_() noexcept; 25 | 26 | void throw_exception(); 27 | 28 | T const & get() const; 29 | T & get(); 30 | 31 | }; 32 | 33 | template 34 | result try_( T && res ) noexcept; 35 | 36 | result current_error() noexcept; 37 | 38 | } } 39 | ---- 40 | -------------------------------------------------------------------------------- /benchmark/deep_stack.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and BOOST_NOEXCEPT Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | //This benchmark program evaluates the performance of Noexcept depending on 7 | //the number of stack frames error objects need to be propagated across. 8 | 9 | //You can #define BENCHMARK_INLINE as BOOST_NOEXCEPT_INLINE_FORCEINLINE 10 | //to enable inlining, which woulld give you benchmark in the case when all functions are 11 | //inlined (no stack frames, no stack unwinding). 12 | 13 | //Note that to use Noexcept this program should be compiled with exceptions handling 14 | //disabled; if it is enabled, the program propagates errors by throwing exceptions. 15 | 16 | //Below are some example command lines for g++ (assuming cwd=noexcept/benchmark). 17 | 18 | //Use Noexcept, inlining disabled: 19 | //g++ -O3 -std=c++11 -I../include -I ../../.. deep_stack.cpp -fno-exceptions 20 | 21 | //Use Noexcept, forced inlining: 22 | //g++ -O3 -std=c++11 -I../include -I ../../.. deep_stack.cpp -fno-exceptions -DBENCHMARK_INLINE=BOOST_NOEXCEPT_INLINE_FORCEINLINE 23 | 24 | //Use c++ exceptions, inlining disabled: 25 | //g++ -O3 -std=c++11 -I../include -I ../../.. deep_stack.cpp 26 | 27 | //Use C++ exceptions, forced inlining: 28 | //g++ -O3 -std=c++11 -I../include -I ../../.. deep_stack.cpp -DBENCHMARK_INLINE=BOOST_NOEXCEPT_INLINE_FORCEINLINE 29 | 30 | #define BOOST_NOEXCEPT_NO_EXCEPTION_INFO 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #ifndef BENCHMARK_INLINE 42 | #define BENCHMARK_INLINE BOOST_NOEXCEPT_INLINE_NOINLINE 43 | #endif 44 | 45 | namespace noexcept_ = boost::noexcept_; 46 | 47 | struct my_error: std::exception { }; 48 | 49 | ///////////////////////////////////////////// 50 | 51 | template 52 | BOOST_NOEXCEPT_INLINE_FORCEINLINE 53 | T compute_value() 54 | { 55 | return T(rand()); 56 | } 57 | 58 | template <> 59 | BOOST_NOEXCEPT_INLINE_FORCEINLINE 60 | std::string compute_value() 61 | { 62 | return rand()>RAND_MAX/2 ? "foo" : "bar"; 63 | } 64 | 65 | ///////////////////////////////////////////// 66 | 67 | template 68 | struct benchmark 69 | { 70 | static BENCHMARK_INLINE 71 | ValueType test_function( int success_percentage ) 72 | { 73 | return benchmark::test_function(success_percentage); 74 | } 75 | }; 76 | 77 | template 78 | struct benchmark<0,ErrorType,ValueType> 79 | { 80 | static BENCHMARK_INLINE 81 | ValueType test_function( int success_percentage ) 82 | { 83 | if( (rand()%100) > success_percentage ) 84 | #ifdef BOOST_NO_EXCEPTIONS 85 | return noexcept_::throw_(ErrorType()); 86 | #else 87 | throw ErrorType(); 88 | #endif 89 | else 90 | return compute_value(); 91 | } 92 | }; 93 | 94 | ///////////////////////////////////////////// 95 | 96 | template 97 | void test_case( int success_percentage, int count ) 98 | { 99 | auto start = std::chrono::steady_clock::now(); 100 | 101 | #ifdef BOOST_NO_EXCEPTIONS 102 | for( int i=0; i!=count; ++i ) 103 | if( auto r = noexcept_::try_(benchmark::test_function(success_percentage)) ) 104 | ; 105 | else 106 | (void) r.template catch_(); 107 | #else 108 | for( int i=0; i!=count; ++i ) 109 | try 110 | { 111 | benchmark::test_function(success_percentage); 112 | } 113 | catch( ErrorType & ) 114 | { 115 | } 116 | #endif 117 | 118 | auto end = std::chrono::steady_clock::now(); 119 | auto diff = end - start; 120 | std::cout << 121 | BOOST_CURRENT_FUNCTION << ", " 122 | "count=" << count << ", " 123 | "success %=" << success_percentage << ": " << 124 | std::chrono::duration_cast(diff).count() << "ms" << 125 | std::endl; 126 | } 127 | 128 | ///////////////////////////////////////////// 129 | 130 | #ifdef BOOST_NO_EXCEPTIONS 131 | namespace boost 132 | { 133 | void throw_exception( std::exception const & ) 134 | { 135 | std::abort(); 136 | } 137 | } 138 | #endif 139 | 140 | int main( int argc, char const * argv[ ] ) 141 | { 142 | #ifdef BOOST_NO_EXCEPTIONS 143 | std::cout << "Using Noexcept" << std::endl; 144 | #else 145 | std::cout << "Using C++ exception handling" << std::endl; 146 | #endif 147 | 148 | std::cout << std::endl; 149 | srand(0); test_case<10,my_error,int>(10,420000); 150 | srand(0); test_case<10,my_error,int>(90,420000); 151 | srand(0); test_case<30,my_error,int>(10,420000); 152 | srand(0); test_case<30,my_error,int>(90,420000); 153 | 154 | std::cout << std::endl; 155 | srand(0); test_case<10,my_error,std::string>(10,420000); 156 | srand(0); test_case<10,my_error,std::string>(90,420000); 157 | srand(0); test_case<30,my_error,std::string>(10,420000); 158 | srand(0); test_case<30,my_error,std::string>(90,420000); 159 | 160 | return 0; 161 | } 162 | -------------------------------------------------------------------------------- /benchmark/deep_stack_outcome.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and BOOST_NOEXCEPT Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | //This benchmark program evaluates the performance of Noexcept depending on 7 | //the number of stack frames error objects need to be propagated across. 8 | 9 | //You can #define BENCHMARK_INLINE as BOOST_NOEXCEPT_INLINE_FORCEINLINE 10 | //to enable inlining, which woulld give you benchmark in the case when all functions are 11 | //inlined (no stack frames, no stack unwinding). 12 | 13 | //Note that to use Noexcept this program should be compiled with exceptions handling 14 | //disabled; if it is enabled, the program propagates errors by throwing exceptions. 15 | 16 | //Below are some example command lines for g++ (assuming cwd=noexcept/benchmark). 17 | 18 | //Use Noexcept, inlining disabled: 19 | //g++ -O3 -std=c++11 -I../include -I ../../.. deep_stack.cpp -fno-exceptions 20 | 21 | //Use Noexcept, forced inlining: 22 | //g++ -O3 -std=c++11 -I../include -I ../../.. deep_stack.cpp -fno-exceptions -DBENCHMARK_INLINE=BOOST_NOEXCEPT_INLINE_FORCEINLINE 23 | 24 | //Use c++ exceptions, inlining disabled: 25 | //g++ -O3 -std=c++11 -I../include -I ../../.. deep_stack.cpp 26 | 27 | //Use C++ exceptions, forced inlining: 28 | //g++ -O3 -std=c++11 -I../include -I ../../.. deep_stack.cpp -DBENCHMARK_INLINE=BOOST_NOEXCEPT_INLINE_FORCEINLINE 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #ifndef BOOST_NO_EXCEPTIONS 38 | #error Disable exception handling for correct benchmark results 39 | #endif 40 | 41 | #ifndef BOOST_NOEXCEPT_INLINE_FORCEINLINE 42 | # if defined(_MSC_VER) 43 | # define BOOST_NOEXCEPT_INLINE_FORCEINLINE __forceinline 44 | # elif (defined(__GNUC__) && __GNUC__>3) || defined(__SNC__) 45 | # define BOOST_NOEXCEPT_INLINE_FORCEINLINE inline __attribute__ ((always_inline)) 46 | # else 47 | # define BOOST_NOEXCEPT_INLINE_FORCEINLINE inline 48 | # endif 49 | #endif 50 | 51 | #ifndef BOOST_NOEXCEPT_INLINE_NOINLINE 52 | # if defined(_MSC_VER) 53 | # define BOOST_NOEXCEPT_INLINE_NOINLINE __declspec(noinline) 54 | # elif (defined(__GNUC__) && __GNUC__>3) || defined(__SNC__) 55 | # define BOOST_NOEXCEPT_INLINE_NOINLINE inline __attribute__ ((noinline)) 56 | # else 57 | # define BOOST_NOEXCEPT_INLINE_NOINLINE inline 58 | # endif 59 | #endif 60 | 61 | #ifndef BOOST_NOEXCEPT_INLINE 62 | #define BOOST_NOEXCEPT_INLINE inline 63 | #endif 64 | 65 | #ifndef BOOST_NOEXCEPT_INLINE_TRIVIAL 66 | #define BOOST_NOEXCEPT_INLINE_TRIVIAL BOOST_NOEXCEPT_FORCEINLINE 67 | #endif 68 | 69 | #ifndef BOOST_NOEXCEPT_INLINE_CRITICAL 70 | #define BOOST_NOEXCEPT_INLINE_CRITICAL BOOST_NOEXCEPT_FORCEINLINE 71 | #endif 72 | 73 | #ifndef BOOST_NOEXCEPT_INLINE_OPERATIONS 74 | #define BOOST_NOEXCEPT_INLINE_OPERATIONS BOOST_NOEXCEPT_INLINE 75 | #endif 76 | 77 | #ifndef BOOST_NOEXCEPT_INLINE_RECURSION 78 | #define BOOST_NOEXCEPT_INLINE_RECURSION BOOST_NOEXCEPT_INLINE_OPERATIONS 79 | #endif 80 | 81 | #ifndef BENCHMARK_INLINE 82 | #define BENCHMARK_INLINE BOOST_NOEXCEPT_INLINE_NOINLINE 83 | #endif 84 | 85 | namespace outcome = BOOST_OUTCOME_V2_NAMESPACE; 86 | 87 | struct my_error: std::exception { }; 88 | 89 | ///////////////////////////////////////////// 90 | 91 | template 92 | BOOST_NOEXCEPT_INLINE_FORCEINLINE 93 | T compute_value() 94 | { 95 | return T(rand()); 96 | } 97 | 98 | template <> 99 | BOOST_NOEXCEPT_INLINE_FORCEINLINE 100 | std::string compute_value() 101 | { 102 | return rand()>RAND_MAX/2 ? "foo" : "bar"; 103 | } 104 | 105 | ///////////////////////////////////////////// 106 | 107 | template 108 | struct benchmark 109 | { 110 | static BENCHMARK_INLINE 111 | outcome::result test_function( int success_percentage ) 112 | { 113 | return benchmark::test_function(success_percentage); 114 | } 115 | }; 116 | 117 | template 118 | struct benchmark<0,ErrorType,ValueType> 119 | { 120 | static BENCHMARK_INLINE 121 | outcome::result test_function( int success_percentage ) 122 | { 123 | if( (rand()%100) > success_percentage ) 124 | return ErrorType(); 125 | else 126 | return compute_value(); 127 | } 128 | }; 129 | 130 | ///////////////////////////////////////////// 131 | 132 | template 133 | void test_case( int success_percentage, int count ) 134 | { 135 | auto start = std::chrono::steady_clock::now(); 136 | 137 | for( int i=0; i!=count; ++i ) 138 | if( auto r = benchmark::test_function(success_percentage) ) 139 | ; 140 | else 141 | (void) r.error(); 142 | 143 | auto end = std::chrono::steady_clock::now(); 144 | auto diff = end - start; 145 | std::cout << 146 | BOOST_CURRENT_FUNCTION << ", " 147 | "count=" << count << ", " 148 | "success %=" << success_percentage << ": " << 149 | std::chrono::duration_cast(diff).count() << "ms" << 150 | std::endl; 151 | } 152 | 153 | ///////////////////////////////////////////// 154 | 155 | #ifdef BOOST_NO_EXCEPTIONS 156 | namespace boost 157 | { 158 | void throw_exception( std::exception const & ) 159 | { 160 | std::abort(); 161 | } 162 | } 163 | #endif 164 | 165 | int main( int argc, char const * argv[ ] ) 166 | { 167 | std::cout << "Using Outcome" << std::endl; 168 | 169 | std::cout << std::endl; 170 | srand(0); test_case<10,my_error,int>(10,420000); 171 | srand(0); test_case<10,my_error,int>(90,420000); 172 | srand(0); test_case<30,my_error,int>(10,420000); 173 | srand(0); test_case<30,my_error,int>(90,420000); 174 | 175 | std::cout << std::endl; 176 | srand(0); test_case<10,my_error,std::string>(10,420000); 177 | srand(0); test_case<10,my_error,std::string>(90,420000); 178 | srand(0); test_case<30,my_error,std::string>(10,420000); 179 | srand(0); test_case<30,my_error,std::string>(90,420000); 180 | 181 | return 0; 182 | } 183 | -------------------------------------------------------------------------------- /examples/FILE_ptr_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace boost::noexcept_; 6 | 7 | FILE * open_file( char const * name ) noexcept { 8 | if( FILE * f=fopen(name,"rb") ) 9 | return f; 10 | else 11 | return throw_( std::error_code { errno, std::generic_category() } ); //<1> 12 | } 13 | 14 | int main() { 15 | if( auto r=try_(open_file("file_name")) ) { //<2> 16 | //Success! Get the FILE pointer: 17 | FILE * f=r.get(); 18 | //Use the file, then close it 19 | fclose(f); 20 | } else if( std::error_code * err=r.catch_() ) { //<3> 21 | //The file failed to open! Handle error. 22 | } //<4> 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /examples/FILE_ptr_throw_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace boost::noexcept_; 5 | 6 | struct file_open_error { }; 7 | 8 | //Return the FILE pointer on success or throw_s file_open_error. 9 | FILE * open_file( char const * name ) noexcept { 10 | if( FILE * f=fopen(name,"rb") ) 11 | return f; 12 | else 13 | return throw_(file_open_error()); 14 | } 15 | 16 | FILE * open_file_throw_on_error( char const * name ) noexcept { 17 | return try_(open_file(name)).get(); 18 | } 19 | 20 | int main() { 21 | try { 22 | FILE * f=open_file_throw_on_error("file_name"); 23 | //Use the file, then close it 24 | fclose(f); 25 | } catch( file_open_error & ) { 26 | //Handle error 27 | } 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /examples/c_api_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace boost::noexcept_; 7 | 8 | //A C function which may fail, returning different error codes 9 | extern "C" { 10 | #define ERRATIC_ERROR_FOO 1 11 | #define ERRATIC_ERROR_BAR 2 12 | 13 | int erratic( float * answer ) { //<1> 14 | switch( rand() % 3 ) { 15 | default: *answer=42; return 0; 16 | case 1: return ERRATIC_ERROR_FOO; 17 | case 2: return ERRATIC_ERROR_BAR; 18 | } 19 | } 20 | } 21 | 22 | struct erratic_error { int error_code; }; //<2> 23 | 24 | boost::optional erratic_caller() noexcept { //<3> 25 | float answer; 26 | if( int err=erratic(&answer) ) 27 | return throw_(erratic_error{err}); 28 | else 29 | return answer; 30 | } 31 | 32 | int main() { 33 | for( int i=0; i!=10; ++i ) 34 | if( auto r=try_(erratic_caller()) ) //<4> 35 | std::cout << "Answer: " << r.get().value() << std::endl; 36 | else if( erratic_error const * err = r.catch_() ) //<5> 37 | std::cout << "FAILED! error code=" << err->error_code << std::endl; 38 | else 39 | assert(0); 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /examples/ifstream_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace boost::noexcept_; 8 | 9 | std::ifstream open_stream( char const * name ) noexcept { 10 | std::ifstream f(name); 11 | if( f.good() ) 12 | return f; //<1> 13 | else 14 | return throw_( std::error_code{ errno, std::generic_category() } ); //<1> 15 | } 16 | 17 | std::string read_line( std::ifstream && f ) noexcept { 18 | BOOST_NOEXCEPT_CHECK 19 | assert(f.good()); //<2> 20 | std::string s; 21 | getline(f,s); 22 | if( f.good() ) 23 | return s; //<3> 24 | else 25 | return throw_( std::error_code{ errno, std::generic_category() } ); //<3> 26 | } 27 | 28 | std::string read_string_from_file( char const * name ) noexcept { 29 | return read_line(open_stream(name)); //<4> 30 | } 31 | 32 | int main() { 33 | if( auto r=try_(read_string_from_file("test.txt")) ) { 34 | //Success! 35 | std::cout << r.get() << '\n'; 36 | return 0; 37 | } else if( auto err=r.catch_() ) { //<5> 38 | std::cerr << err->message(); 39 | } /*else { //<6> 40 | std::cerr << 41 | "Unhandled error reached main!\n" 42 | "Diagnostic information below:\n" << 43 | boost::diagnostic_information(*r.catch_<>()); 44 | }*/ 45 | return 1; 46 | } 47 | -------------------------------------------------------------------------------- /examples/lua_example.cpp: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | #include "lua.h" 3 | #include "lauxlib.h" 4 | } 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace boost::noexcept_; 10 | 11 | struct do_work_error { }; 12 | int do_work( lua_State * L ) noexcept; 13 | 14 | std::shared_ptr init_lua_state() noexcept { //<1> 15 | std::shared_ptr L(lua_open(),&lua_close); 16 | luaL_dostring( &*L, "\ 17 | \n function call_do_work()\ 18 | \n return do_work()\ 19 | \n end" ); 20 | lua_register( &*L, "do_work", &do_work ); 21 | return L; 22 | } 23 | 24 | int do_work( lua_State * L ) noexcept { 25 | bool success=rand()%2; 26 | if( success ) 27 | return lua_pushnumber(L,42), 1; //<2> 28 | else 29 | return throw_(do_work_error()), lua_error(L); //<3> 30 | } 31 | 32 | int call_lua( lua_State * L ) noexcept { //<4> 33 | lua_getfield( L, LUA_GLOBALSINDEX, "call_do_work" ); 34 | if( int err=lua_pcall(L,0,1,0) ) { 35 | lua_pop(L,1); 36 | return throw_(); 37 | } 38 | else { 39 | int answer=lua_tonumber(L,-1); 40 | lua_pop(L,1); 41 | return answer; 42 | } 43 | } 44 | 45 | int main() { 46 | std::shared_ptr L=init_lua_state(); 47 | for( int i=0; i!=10; ++i ) { 48 | if( auto r=try_(call_lua(&*L)) ) 49 | std::cout << "do_work succeeded, answer=" << r.get() << '\n'; 50 | else if( auto err=r.catch_() ) //<5> 51 | std::cout << "do_work failed!\n"; 52 | } 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /include/boost/noexcept.hpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | -------------------------------------------------------------------------------- /include/boost/noexcept/noexcept_config/assert.hpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef UUID_C675838848D611E7BF4E8FCF7E4E887A 7 | #define UUID_C675838848D611E7BF4E8FCF7E4E887A 8 | 9 | #ifndef BOOST_NOEXCEPT_ASSERT 10 | # include 11 | # define BOOST_NOEXCEPT_ASSERT BOOST_ASSERT 12 | #endif 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /include/boost/noexcept/noexcept_config/inline.hpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and BOOST_NOEXCEPT Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef BOOST_NOEXCEPT_INLINE_FORCEINLINE 7 | # if defined(_MSC_VER) 8 | # define BOOST_NOEXCEPT_INLINE_FORCEINLINE __forceinline 9 | # elif (defined(__GNUC__) && __GNUC__>3) || defined(__SNC__) 10 | # define BOOST_NOEXCEPT_INLINE_FORCEINLINE inline __attribute__ ((always_inline)) 11 | # else 12 | # define BOOST_NOEXCEPT_INLINE_FORCEINLINE inline 13 | # endif 14 | #endif 15 | 16 | #ifndef BOOST_NOEXCEPT_INLINE_NOINLINE 17 | # if defined(_MSC_VER) 18 | # define BOOST_NOEXCEPT_INLINE_NOINLINE __declspec(noinline) 19 | # elif (defined(__GNUC__) && __GNUC__>3) || defined(__SNC__) 20 | # define BOOST_NOEXCEPT_INLINE_NOINLINE inline __attribute__ ((noinline)) 21 | # else 22 | # define BOOST_NOEXCEPT_INLINE_NOINLINE inline 23 | # endif 24 | #endif 25 | 26 | #ifndef BOOST_NOEXCEPT_INLINE 27 | #define BOOST_NOEXCEPT_INLINE inline 28 | #endif 29 | 30 | #ifndef BOOST_NOEXCEPT_INLINE_TRIVIAL 31 | #define BOOST_NOEXCEPT_INLINE_TRIVIAL BOOST_NOEXCEPT_FORCEINLINE 32 | #endif 33 | 34 | #ifndef BOOST_NOEXCEPT_INLINE_CRITICAL 35 | #define BOOST_NOEXCEPT_INLINE_CRITICAL BOOST_NOEXCEPT_FORCEINLINE 36 | #endif 37 | 38 | #ifndef BOOST_NOEXCEPT_INLINE_OPERATIONS 39 | #define BOOST_NOEXCEPT_INLINE_OPERATIONS BOOST_NOEXCEPT_INLINE 40 | #endif 41 | 42 | #ifndef BOOST_NOEXCEPT_INLINE_RECURSION 43 | #define BOOST_NOEXCEPT_INLINE_RECURSION BOOST_NOEXCEPT_INLINE_OPERATIONS 44 | #endif 45 | -------------------------------------------------------------------------------- /include/boost/noexcept/noexcept_config/rtti.hpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef UUID_55A8454C593311E796DF212ECB2E2FD9 7 | #define UUID_55A8454C593311E796DF212ECB2E2FD9 8 | 9 | #ifndef BOOST_NOEXCEPT_NO_RTTI 10 | # include 11 | # ifdef BOOST_NO_RTTI 12 | # define BOOST_NOEXCEPT_NO_RTTI 13 | # endif 14 | # ifndef BOOST_NOEXCEPT_NO_RTTI 15 | # include 16 | # endif 17 | #endif 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /include/boost/noexcept/noexcept_config/thread_local.hpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef UUID_EB80EE1449A411E7879E1EF67F4E887A 7 | #define UUID_EB80EE1449A411E7879E1EF67F4E887A 8 | 9 | #ifndef BOOST_NOEXCEPT_NO_THREADS 10 | # include 11 | # ifdef BOOST_NO_THREADS 12 | # define BOOST_NOEXCEPT_NO_THREADS 13 | # endif 14 | #endif 15 | 16 | #ifdef BOOST_NOEXCEPT_NO_THREADS 17 | #define BOOST_NOEXCEPT_THREAD_LOCAL_ 18 | #else 19 | #define BOOST_NOEXCEPT_THREAD_LOCAL_ thread_local 20 | #endif 21 | 22 | #ifndef BOOST_NOEXCEPT_THREAD_LOCAL 23 | #define BOOST_NOEXCEPT_THREAD_LOCAL(type,object) static BOOST_NOEXCEPT_THREAD_LOCAL_ type object 24 | #endif 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /include/boost/noexcept/noexcept_config/throw_exception.hpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef UUID_FB56599248D611E78192CFCF7E4E887A 7 | #define UUID_FB56599248D611E78192CFCF7E4E887A 8 | 9 | #ifndef BOOST_NOEXCEPT_NO_EXCEPTIONS 10 | # include 11 | # ifdef BOOST_NO_EXCEPTIONS 12 | # define BOOST_NOEXCEPT_NO_EXCEPTIONS 13 | # endif 14 | #endif 15 | 16 | #ifndef BOOST_NOEXCEPT_THROW_EXCEPTION 17 | # include 18 | # define BOOST_NOEXCEPT_THROW_EXCEPTION BOOST_THROW_EXCEPTION 19 | #endif 20 | 21 | #if !defined(BOOST_NOEXCEPT_NORETURN) 22 | # if defined(_MSC_VER) 23 | # define BOOST_NOEXCEPT_NORETURN __declspec(noreturn) 24 | # elif defined(__GNUC__) 25 | # define BOOST_NOEXCEPT_NORETURN __attribute__ ((__noreturn__)) 26 | # elif defined(__has_attribute) && defined(__SUNPRO_CC) 27 | # if __has_attribute(noreturn) 28 | # define BOOST_NOEXCEPT_NORETURN [[noreturn]] 29 | # endif 30 | # elif defined(__has_cpp_attribute) 31 | # if __has_cpp_attribute(noreturn) 32 | # define BOOST_NOEXCEPT_NORETURN [[noreturn]] 33 | # endif 34 | # endif 35 | #endif 36 | 37 | #if !defined(BOOST_NOEXCEPT_NORETURN) 38 | # define BOOST_NOEXCEPT_NORETURN 39 | #endif 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /include/boost/noexcept/noexcept_detail/ceh.hpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef UUID_C95230D44A3311E781E9B0B2AD730A1C 7 | #define UUID_C95230D44A3311E781E9B0B2AD730A1C 8 | 9 | #include 10 | #include 11 | 12 | namespace 13 | boost 14 | { 15 | namespace 16 | noexcept_ 17 | { 18 | namespace 19 | noexcept_detail 20 | { 21 | template ::value> struct default_throw_return; 22 | template 23 | struct 24 | default_throw_return 25 | { 26 | static R value() noexcept { return static_cast(-1); } 27 | }; 28 | template 29 | struct 30 | default_throw_return 31 | { 32 | static R value() noexcept { return R(); } 33 | }; 34 | class error; 35 | class 36 | current_error_holder 37 | { 38 | private: 39 | current_error_holder( current_error_holder const & )=delete; 40 | current_error_holder( current_error_holder && )=delete; 41 | current_error_holder & operator=( current_error_holder const & )=delete; 42 | current_error_holder & operator=( current_error_holder && )=delete; 43 | error_storage storage_; 44 | mover_t * mover_; 45 | error_base * px_; 46 | void 47 | ensure_empty() noexcept 48 | { 49 | BOOST_NOEXCEPT_ASSERT(empty() && "Unhandled error is present at the time a new error is being set()! Calling std::terminate()!"); 50 | if( !empty() ) 51 | std::terminate(); 52 | } 53 | public: 54 | constexpr current_error_holder() noexcept: 55 | storage_(), 56 | mover_(0), 57 | px_(0) 58 | { 59 | } 60 | ~current_error_holder() noexcept 61 | { 62 | BOOST_NOEXCEPT_ASSERT(empty() && "The thread terminates with unhandled error! Calling std::terminate()!"); 63 | if( !empty() ) 64 | std::terminate(); 65 | } 66 | bool 67 | empty() const noexcept 68 | { 69 | return px_==0; 70 | } 71 | //defined in throw.hpp: 72 | template void put( E && ) noexcept; 73 | template void put_with_location( E &&, char const * file, int line, char const * function ) noexcept; 74 | //defined in try.hpp: 75 | error extract() noexcept; 76 | void set( error && ) noexcept; 77 | }; 78 | inline 79 | current_error_holder & 80 | ceh() noexcept 81 | { 82 | BOOST_NOEXCEPT_THREAD_LOCAL(current_error_holder,errh); 83 | return errh; 84 | } 85 | } 86 | inline 87 | bool 88 | has_current_error() noexcept 89 | { 90 | return !noexcept_detail::ceh().empty(); 91 | } 92 | template 93 | struct 94 | throw_return: noexcept_detail::default_throw_return 95 | { 96 | }; 97 | template <> 98 | struct 99 | throw_return 100 | { 101 | static bool value() noexcept { return false; } 102 | }; 103 | } 104 | } 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /include/boost/noexcept/noexcept_detail/eh.hpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef UUID_B77EFEB45BBD11E7B59A8BBA7FA7C656 7 | #define UUID_B77EFEB45BBD11E7B59A8BBA7FA7C656 8 | 9 | #include 10 | #include 11 | #include 12 | #ifdef BOOST_NOEXCEPT_NO_EXCEPTION_INFO 13 | namespace boost { namespace noexcept_ { class exception_info; } } 14 | #else 15 | #include 16 | namespace boost { namespace noexcept_ { typedef boost::exception exception_info; } } 17 | #endif 18 | #include 19 | #include 20 | #include 21 | 22 | namespace 23 | boost 24 | { 25 | namespace 26 | noexcept_ 27 | { 28 | namespace 29 | noexcept_detail 30 | { 31 | enum { sizeof_max_error=128 }; 32 | typedef std::aligned_storage::type error_storage; 33 | template void tid_() { } 34 | /////////////////////////////// 35 | class 36 | error_base 37 | { 38 | virtual void throw_exception_() { }; 39 | public: 40 | BOOST_NOEXCEPT_NORETURN 41 | void 42 | throw_exception() 43 | { 44 | throw_exception_(); 45 | std::terminate(); 46 | } 47 | virtual exception_info * get_exception_info() noexcept { return 0; } 48 | virtual std::exception * get_std_exception() noexcept=0; 49 | virtual void * get_obj( void (*typeid_)() ) noexcept=0; 50 | virtual ~error_base() noexcept { } 51 | }; 52 | /////////////////////////////// 53 | template ::value, 58 | #endif 59 | bool DerivesFromStdException=std::is_base_of::value> 60 | struct class_dispatch; 61 | /////////////////////////////// 62 | #ifndef BOOST_NOEXCEPT_NO_EXCEPTION_INFO 63 | template 64 | struct 65 | class_dispatch 66 | { 67 | class 68 | type: 69 | public exception_info, 70 | public std::exception, 71 | public error_base, 72 | public E 73 | { 74 | void throw_exception_() { BOOST_NOEXCEPT_THROW_EXCEPTION(*this); } 75 | exception_info * get_exception_info() noexcept { return this; } 76 | std::exception * get_std_exception() noexcept { return this; } 77 | void * get_obj( void (*typeid_)() ) noexcept { return typeid_==&tid_?static_cast(this):0; } 78 | public: 79 | explicit 80 | type( E && e ) noexcept: 81 | E(std::move(e)) 82 | { 83 | } 84 | type( type const & ) = default; 85 | type( type && ) = default; 86 | }; 87 | }; 88 | template 89 | struct 90 | class_dispatch 91 | { 92 | class 93 | type: 94 | public exception_info, 95 | public error_base, 96 | public E 97 | { 98 | void throw_exception_() { BOOST_NOEXCEPT_THROW_EXCEPTION(*this); } 99 | exception_info * get_exception_info() noexcept { return this; } 100 | std::exception * get_std_exception() noexcept { return this; } 101 | void * get_obj( void (*typeid_)() ) noexcept { return typeid_==&tid_?static_cast(this):0; } 102 | public: 103 | explicit 104 | type( E && e ) noexcept: 105 | E(std::move(e)) 106 | { 107 | } 108 | type( type const & ) = default; 109 | type( type && ) = default; 110 | }; 111 | }; 112 | #endif 113 | /////////////////////////////// 114 | template 115 | struct 116 | class_dispatch 117 | { 118 | class 119 | type: 120 | public std::exception, 121 | public error_base, 122 | public E 123 | { 124 | void throw_exception_() { BOOST_NOEXCEPT_THROW_EXCEPTION(*this); } 125 | #ifndef BOOST_NOEXCEPT_NO_EXCEPTION_INFO 126 | exception_info * get_exception_info() noexcept { return this; } 127 | #endif 128 | std::exception * get_std_exception() noexcept { return this; } 129 | void * get_obj( void (*typeid_)() ) noexcept { return typeid_==&tid_?static_cast(this):0; } 130 | public: 131 | explicit 132 | type( E && e ) noexcept: 133 | E(std::move(e)) 134 | { 135 | } 136 | type( type const & ) = default; 137 | type( type && ) = default; 138 | }; 139 | }; 140 | template 141 | struct 142 | class_dispatch 143 | { 144 | class 145 | type: 146 | public error_base, 147 | public E 148 | { 149 | void throw_exception_() { BOOST_NOEXCEPT_THROW_EXCEPTION(*this); } 150 | #ifndef BOOST_NOEXCEPT_NO_EXCEPTION_INFO 151 | exception_info * get_exception_info() noexcept { return this; } 152 | #endif 153 | std::exception * get_std_exception() noexcept { return this; } 154 | void * get_obj( void (*typeid_)() ) noexcept { return typeid_==&tid_?static_cast(this):0; } 155 | public: 156 | explicit 157 | type( E && e ) noexcept: 158 | E(std::move(e)) 159 | { 160 | } 161 | type( type const & ) = default; 162 | type( type && ) = default; 163 | }; 164 | }; 165 | /////////////////////////////// 166 | template ::value> struct wrap; 167 | template 168 | struct 169 | wrap 170 | { 171 | class 172 | type: 173 | #ifndef BOOST_NOEXCEPT_NO_EXCEPTION_INFO 174 | public exception_info, 175 | #endif 176 | public std::exception, 177 | public error_base 178 | { 179 | E value_; 180 | #ifndef BOOST_NOEXCEPT_NO_EXCEPTIONS 181 | void throw_exception_() { throw value_; } 182 | #endif 183 | #ifndef BOOST_NOEXCEPT_NO_EXCEPTION_INFO 184 | exception_info * get_exception_info() noexcept { return this; } 185 | #endif 186 | std::exception * get_std_exception() noexcept { return this; } 187 | void * get_obj( void (*typeid_)() ) noexcept { return typeid_==&tid_?&value_:0; } 188 | public: 189 | explicit 190 | type( E && e ) noexcept: 191 | value_(std::move(e)) 192 | { 193 | } 194 | type( type const & ) = default; 195 | type( type && ) = default; 196 | }; 197 | }; 198 | template 199 | struct 200 | wrap 201 | { 202 | typedef typename class_dispatch::type type; 203 | }; 204 | template ::type)>sizeof_max_error)> struct final_type; 205 | template 206 | struct 207 | final_type 208 | { 209 | typedef typename wrap::type type; 210 | }; 211 | /////////////////////////////// 212 | template 213 | void 214 | move_( void * dst, void * src ) noexcept 215 | { 216 | (void) new (dst) T(std::move(*reinterpret_cast(src))); 217 | } 218 | typedef void (mover_t)( void *, void *); 219 | void 220 | move_error( mover_t * & dst_mvr, void * dst, error_base * & dst_px, mover_t * src_mvr, void * src, error_base * & src_px ) noexcept 221 | { 222 | if( src_px ) 223 | { 224 | dst_mvr=src_mvr; 225 | src_mvr(dst,src); 226 | dst_px = reinterpret_cast(reinterpret_cast(dst) + (reinterpret_cast(src_px) - reinterpret_cast(src))); 227 | #ifndef BOOST_NOEXCEPT_NO_RTTI 228 | BOOST_NOEXCEPT_ASSERT(typeid(*dst_px)==typeid(*src_px)); 229 | #endif 230 | src_px->~error_base(); 231 | src_px=0; 232 | } 233 | else 234 | dst_px=0; 235 | } 236 | template 237 | typename final_type::type * 238 | init_error( E && e, mover_t * & mvr, void * storage ) noexcept 239 | { 240 | typedef typename final_type::type T; 241 | T * w = new (storage) T(std::move(e)); 242 | mvr = &move_; 243 | return w; 244 | } 245 | /////////////////////////////// 246 | } 247 | } 248 | } 249 | 250 | #endif 251 | -------------------------------------------------------------------------------- /include/boost/noexcept/noexcept_detail/error.hpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef UUID_4074489A481911E7BE231D4A7F4E887A 7 | #define UUID_4074489A481911E7BE231D4A7F4E887A 8 | 9 | #include 10 | 11 | namespace 12 | boost 13 | { 14 | namespace 15 | noexcept_ 16 | { 17 | namespace 18 | noexcept_detail 19 | { 20 | class 21 | error 22 | { 23 | error( error const & )=delete; 24 | error & operator=( error const & )=delete; 25 | error_storage storage_; 26 | mover_t * mover_; 27 | error_base * px_; 28 | public: 29 | template 30 | explicit 31 | error( E && e ) noexcept: 32 | px_(init_error(std::move(e),mover_,&storage_)) 33 | { 34 | } 35 | template 36 | explicit 37 | error( E && e, char const * file, int line, char const * function ) noexcept 38 | { 39 | auto w=init_error(std::move(e),mover_,&storage_); 40 | px_=w; 41 | #ifndef BOOST_NOEXCEPT_NO_EXCEPTION_INFO 42 | using namespace ::boost::exception_detail; 43 | set_info(*w,throw_file(file)); 44 | set_info(*w,throw_line(line)); 45 | set_info(*w,throw_function(function)); 46 | #endif 47 | } 48 | error( error && x ) noexcept 49 | { 50 | move_error(mover_,&storage_,px_,x.mover_,&x.storage_,x.px_); 51 | BOOST_NOEXCEPT_ASSERT(x.px_==0); 52 | } 53 | error( mover_t * src_mvr, void * src, error_base * & src_px ) noexcept 54 | { 55 | move_error(mover_,&storage_,px_,src_mvr,src,src_px); 56 | } 57 | void 58 | move_to( mover_t * & dst_mvr, void * dst, error_base * & dst_px ) noexcept 59 | { 60 | move_error(dst_mvr,dst,dst_px,mover_,&storage_,px_); 61 | BOOST_NOEXCEPT_ASSERT(px_==0); 62 | } 63 | ~error() noexcept 64 | { 65 | if( px_ ) 66 | px_->~error_base(); 67 | } 68 | BOOST_NOEXCEPT_NORETURN 69 | void 70 | throw_exception() 71 | { 72 | BOOST_NOEXCEPT_ASSERT(px_!=0); 73 | px_->throw_exception(); 74 | } 75 | template E const * get() const noexcept; 76 | template E * get() noexcept; 77 | }; 78 | template <> 79 | std::exception const * 80 | error:: 81 | get() const noexcept 82 | { 83 | return px_?px_->get_std_exception():0; 84 | } 85 | template <> 86 | std::exception const * 87 | error:: 88 | get() const noexcept 89 | { 90 | return px_?px_->get_std_exception():0; 91 | } 92 | template <> 93 | std::exception * 94 | error:: 95 | get() noexcept 96 | { 97 | return px_?px_->get_std_exception():0; 98 | } 99 | template <> 100 | std::exception const * 101 | error:: 102 | get() noexcept 103 | { 104 | return px_?px_->get_std_exception():0; 105 | } 106 | template <> 107 | exception_info const * 108 | error:: 109 | get() const noexcept 110 | { 111 | return px_?px_->get_exception_info():0; 112 | } 113 | template <> 114 | exception_info const * 115 | error:: 116 | get() const noexcept 117 | { 118 | return px_?px_->get_exception_info():0; 119 | } 120 | template <> 121 | exception_info * 122 | error:: 123 | get() noexcept 124 | { 125 | return px_?px_->get_exception_info():0; 126 | } 127 | template <> 128 | exception_info const * 129 | error:: 130 | get() noexcept 131 | { 132 | return px_?px_->get_exception_info():0; 133 | } 134 | template ::value> struct dynamic; 135 | template 136 | struct 137 | dynamic 138 | { 139 | #ifdef BOOST_NOEXCEPT_NO_RTTI 140 | static E * cast( error_base * ) noexcept { return 0; } 141 | #else 142 | static E * cast( error_base * px ) noexcept { return dynamic_cast(px); } 143 | #endif 144 | }; 145 | template 146 | struct 147 | dynamic 148 | { 149 | static E * cast( error_base * ) noexcept { return 0; } 150 | }; 151 | template 152 | E const * 153 | error:: 154 | get() const noexcept 155 | { 156 | if( !px_ ) 157 | return 0; 158 | else if( void const * e=px_->get_obj(&tid_) ) 159 | return reinterpret_cast(e); 160 | else 161 | return dynamic::cast(px_); 162 | } 163 | template 164 | E * 165 | error:: 166 | get() noexcept 167 | { 168 | if( !px_ ) 169 | return 0; 170 | if( void * e=px_->get_obj(&tid_) ) 171 | return reinterpret_cast(e); 172 | else 173 | return dynamic::cast(px_); 174 | } 175 | } 176 | } 177 | } 178 | 179 | #endif 180 | -------------------------------------------------------------------------------- /include/boost/noexcept/throw.hpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef UUID_75B956744A2D11E7AAD921AAAD730A1C 7 | #define UUID_75B956744A2D11E7AAD921AAAD730A1C 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define BOOST_NOEXCEPT_CHECK { if( ::boost::noexcept_::has_current_error() ) return ::boost::noexcept_::throw_(); } 15 | #define BOOST_NOEXCEPT_CHECK_VOID { if( ::boost::noexcept_::has_current_error() ) return; } 16 | #define THROW_(x) ::boost::noexcept_::throw_((x),__FILE__,__LINE__,BOOST_CURRENT_FUNCTION) 17 | 18 | namespace 19 | boost 20 | { 21 | namespace 22 | noexcept_ 23 | { 24 | class 25 | throw_ 26 | { 27 | throw_( throw_ const & )=delete; 28 | throw_ & operator=( throw_ const & )=delete; 29 | public: 30 | template 31 | explicit 32 | throw_( E && e ) noexcept 33 | { 34 | noexcept_detail::ceh().put(std::move(e)); 35 | } 36 | template 37 | explicit 38 | throw_( E && e, char const * file, int line, char const * function ) noexcept 39 | { 40 | noexcept_detail::ceh().put_with_location(std::move(e),file,line,function); 41 | } 42 | throw_() noexcept 43 | { 44 | BOOST_NOEXCEPT_ASSERT(has_current_error()); 45 | } 46 | template 47 | operator R() noexcept 48 | { 49 | BOOST_NOEXCEPT_ASSERT(has_current_error()); 50 | return throw_return::value(); 51 | } 52 | }; 53 | namespace 54 | noexcept_detail 55 | { 56 | template 57 | void 58 | current_error_holder:: 59 | put( E && e ) noexcept 60 | { 61 | ensure_empty(); 62 | px_ = init_error(std::move(e),mover_,&storage_); 63 | } 64 | template 65 | void 66 | current_error_holder:: 67 | put_with_location( E && e, char const * file, int line, char const * function ) noexcept 68 | { 69 | ensure_empty(); 70 | auto w=init_error(std::move(e),mover_,&storage_); 71 | px_=w; 72 | #ifndef BOOST_NOEXCEPT_NO_EXCEPTION_INFO 73 | using namespace ::boost::exception_detail; 74 | set_info(*w,throw_file(file)); 75 | set_info(*w,throw_line(line)); 76 | set_info(*w,throw_function(function)); 77 | #endif 78 | } 79 | } 80 | } 81 | } 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /include/boost/noexcept/try.hpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #ifndef UUID_5872130C482B11E7B84F4C607F4E887A 7 | #define UUID_5872130C482B11E7B84F4C607F4E887A 8 | 9 | #include 10 | #include 11 | 12 | namespace 13 | boost 14 | { 15 | namespace 16 | noexcept_ 17 | { 18 | template class result; 19 | /////////////////////////////////// 20 | template result try_( T && ) noexcept; 21 | template 22 | class 23 | result 24 | { 25 | result( result const & )=delete; 26 | result & operator=( result const & )=delete; 27 | class 28 | pthrow_ 29 | { 30 | pthrow_( pthrow_ const & )=delete; 31 | pthrow_ & operator=( pthrow_ const & )=delete; 32 | public: 33 | template 34 | operator R() noexcept 35 | { 36 | return throw_return::value(); 37 | } 38 | }; 39 | protected: 40 | typedef boost::noexcept_::noexcept_detail::error error; 41 | private: 42 | friend result try_( T && ) noexcept; 43 | union 44 | { 45 | T val_; 46 | mutable error err_; 47 | }; 48 | enum 49 | what_type 50 | { 51 | wh_value, 52 | wh_unhandled_error, 53 | wh_error 54 | }; 55 | what_type what_; 56 | protected: 57 | explicit 58 | result( T && val ) noexcept: 59 | val_(std::move(val)), 60 | what_(wh_value) 61 | { 62 | BOOST_NOEXCEPT_ASSERT(!has_current_error()); 63 | } 64 | explicit 65 | result( noexcept_detail::current_error_holder & ceh ) noexcept: 66 | err_(ceh.extract()), 67 | what_(wh_unhandled_error) 68 | { 69 | } 70 | public: 71 | result( result && x ) noexcept: 72 | what_(x.what_) 73 | { 74 | if( x.has_error() ) 75 | { 76 | (void) new (&err_) error(std::move(x.err_)); 77 | if( x.has_unhandled_error() ) 78 | x.what_=wh_error; 79 | } 80 | else 81 | { 82 | BOOST_NOEXCEPT_ASSERT(!has_error()); 83 | (void) new (&val_) T(std::move(x.val_)); 84 | } 85 | } 86 | ~result() noexcept 87 | { 88 | if( has_error() ) 89 | { 90 | if( has_unhandled_error() ) 91 | noexcept_detail::ceh().set(std::move(err_)); 92 | err_.~error(); 93 | } 94 | else 95 | { 96 | BOOST_NOEXCEPT_ASSERT(!has_error()); 97 | val_.~T(); 98 | } 99 | } 100 | explicit operator bool() const noexcept 101 | { 102 | return what_==wh_value; 103 | } 104 | bool 105 | has_error() const noexcept 106 | { 107 | return what_!=wh_value; 108 | } 109 | bool 110 | has_unhandled_error() const noexcept 111 | { 112 | return what_==wh_unhandled_error; 113 | } 114 | template 115 | E * 116 | catch_() noexcept 117 | { 118 | if( has_error() ) 119 | 120 | if( E * e=err_.get() ) 121 | { 122 | what_=wh_error; 123 | return e; 124 | } 125 | return 0; 126 | } 127 | pthrow_ & 128 | throw_() noexcept 129 | { 130 | BOOST_NOEXCEPT_ASSERT(has_error()); 131 | noexcept_detail::ceh().set(std::move(err_)); 132 | what_=wh_error; 133 | return *reinterpret_cast(this); 134 | } 135 | BOOST_NOEXCEPT_NORETURN 136 | void 137 | throw_exception() 138 | { 139 | BOOST_NOEXCEPT_ASSERT(has_error()); 140 | what_=wh_error; 141 | err_.throw_exception(); 142 | } 143 | T const & 144 | get() const 145 | { 146 | if( has_error() ) 147 | throw_exception(); 148 | else 149 | return val_; 150 | } 151 | T & 152 | get() 153 | { 154 | if( has_error() ) 155 | throw_exception(); 156 | else 157 | return val_; 158 | } 159 | }; 160 | template 161 | result 162 | try_( T && x ) noexcept 163 | { 164 | noexcept_detail::current_error_holder & ceh=noexcept_detail::ceh(); 165 | if( ceh.empty() ) 166 | return result(std::move(x)); 167 | else 168 | return result(ceh); 169 | } 170 | /////////////////////////////////// 171 | result current_error() noexcept; 172 | template <> 173 | class 174 | result: 175 | public result 176 | { 177 | result( result const & )=delete; 178 | result & operator=( result const & )=delete; 179 | friend result current_error() noexcept; 180 | typedef result base; 181 | explicit 182 | result( bool x ): 183 | base(std::move(x)) 184 | { 185 | } 186 | result( noexcept_detail::current_error_holder & ceh ): 187 | base(ceh) 188 | { 189 | } 190 | public: 191 | result( result && ) = default; 192 | }; 193 | inline 194 | result 195 | current_error() noexcept 196 | { 197 | noexcept_detail::current_error_holder & ceh=noexcept_detail::ceh(); 198 | if( ceh.empty() ) 199 | return result(true); 200 | else 201 | return result(ceh); 202 | } 203 | /////////////////////////////////// 204 | namespace 205 | noexcept_detail 206 | { 207 | inline 208 | error 209 | current_error_holder:: 210 | extract() noexcept 211 | { 212 | return error(mover_,&storage_,px_); 213 | } 214 | inline 215 | void 216 | current_error_holder:: 217 | set( error && e ) noexcept 218 | { 219 | ensure_empty(); 220 | e.move_to(mover_,&storage_,px_); 221 | } 222 | } 223 | } 224 | } 225 | 226 | #endif 227 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | # Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | project('noexcept','cpp',default_options : ['b_pch=false']) 7 | compiler = meson.get_compiler('cpp') 8 | compiler_id = compiler.get_id() 9 | 10 | if not meson.is_subproject() 11 | if compiler_id=='clang' 12 | add_global_arguments( 13 | '-Wno-unused-local-typedefs', 14 | '-Wno-non-virtual-dtor', 15 | '-Wno-dangling-else', 16 | '-std=c++11', 17 | language:'cpp' ) 18 | elif compiler_id=='gcc' 19 | add_global_arguments( 20 | '-Wno-unused-local-typedefs', 21 | '-Wno-non-virtual-dtor', 22 | '-std=c++11', 23 | language:'cpp' ) 24 | elif compiler_id=='msvc' 25 | endif 26 | endif 27 | 28 | opt_throw=get_option('throw') 29 | if opt_throw=='std' 30 | add_global_arguments( '-DBOOST_NOEXCEPT_THROW_EXCEPTION(x)=throw(x)', language:'cpp' ) 31 | elif opt_throw=='terminate' 32 | add_global_arguments( '-DBOOST_NOEXCEPT_THROW_EXCEPTOIN(x)=std::terminate()', language:'cpp' ) 33 | endif 34 | 35 | opt_exception_info=get_option('exception_info') 36 | if not opt_exception_info 37 | add_global_arguments( '-DBOOST_NOEXCEPT_NO_EXCEPTION_INFO', language:'cpp' ) 38 | endif 39 | 40 | opt_rtti=get_option('rtti') 41 | if opt_rtti=='off' 42 | add_global_arguments( '-DBOOST_NOEXCEPT_NO_RTTI', language:'cpp' ) 43 | endif 44 | 45 | opt_tls=get_option('tls') 46 | if opt_tls=='boost' 47 | add_global_arguments( '-DBOOST_NOEXCEPT_USE_BOOST_TSS', language:'cpp' ) 48 | deps = declare_dependency( include_directories:include_directories('include'), dependencies: subproject('boost').get_variable('thread') ) 49 | else 50 | if opt_tls=='static' 51 | add_global_arguments( '-DBOOST_NOEXCEPT_NO_THREADS', language:'cpp' ) 52 | endif 53 | deps = declare_dependency( include_directories:include_directories('include'), dependencies: subproject('boost').get_variable('headers') ) 54 | endif 55 | 56 | lua=subproject('lua').get_variable('all') 57 | 58 | executable( 'c_api_example', 59 | 'examples/c_api_example.cpp', 60 | dependencies: deps ) 61 | 62 | executable( 'file_ptr_example', 63 | 'examples/file_ptr_example.cpp', 64 | dependencies: deps ) 65 | 66 | executable( 'file_ptr_throw_example', 67 | 'examples/file_ptr_throw_example.cpp', 68 | dependencies: deps ) 69 | 70 | executable( 'ifstream_example', 71 | 'examples/ifstream_example.cpp', 72 | dependencies: deps ) 73 | 74 | executable( 'lua_example', 75 | 'examples/lua_example.cpp', 76 | dependencies: [ deps, lua ] ) 77 | 78 | test( 'check_test', 79 | executable( 'check_test', 80 | 'test/check_test.cpp', 81 | dependencies: deps ) ) 82 | 83 | if opt_exception_info 84 | test( 'error_info_test', 85 | executable( 'error_info_test', 86 | 'test/error_info_test.cpp', 87 | dependencies: deps ) ) 88 | endif 89 | 90 | test( 'diagnostic_information_test', 91 | executable( 'diagnostic_information_test', 92 | 'test/diagnostic_information_test.cpp', 93 | dependencies: deps ) ) 94 | 95 | test( 'error_test', 96 | executable( 'error_test', 97 | 'test/error_test.cpp', 98 | dependencies: deps ) ) 99 | 100 | test( 'result_test', 101 | executable( 'result_test', 102 | 'test/result_test.cpp', 103 | dependencies: deps ) ) 104 | 105 | test( 'has_current_error_test', 106 | executable( 'has_current_error_test', 107 | 'test/has_current_error_test.cpp', 108 | dependencies: deps ) ) 109 | 110 | test( 'multiple_active_errors_test', 111 | executable( 'multiple_active_errors_test', 112 | 'test/multiple_active_errors_test.cpp', 113 | dependencies: deps ) ) 114 | 115 | test( 'result_mt_test', 116 | executable( 'result_mt_test', 117 | 'test/result_mt_test.cpp', 118 | dependencies: deps ) ) 119 | 120 | test( 'throw_exception_test', 121 | executable( 'throw_exception_test', 122 | 'test/throw_exception_test.cpp', 123 | dependencies: deps ) ) 124 | 125 | test( 'throw_return_test', 126 | executable( 'throw_return_test', 127 | 'test/throw_return_test.cpp', 128 | dependencies: deps ) ) 129 | 130 | test( 'throw_test', 131 | executable( 'throw_test', 132 | 'test/throw_test.cpp', 133 | dependencies: deps ) ) 134 | 135 | test( '_rethrow_test', 136 | executable( '_rethrow_test', 137 | 'test/_rethrow_test.cpp', 138 | dependencies: deps ), 139 | should_fail: true ) 140 | 141 | test( '_unhandled_error_test1', 142 | executable( '_unhandled_error_test1', 143 | 'test/_unhandled_error_test1.cpp', 144 | dependencies: deps ), 145 | should_fail: true ) 146 | 147 | test( '_unhandled_error_test2', 148 | executable( '_unhandled_error_test2', 149 | 'test/_unhandled_error_test2.cpp', 150 | dependencies: deps ), 151 | should_fail: true ) 152 | 153 | test( '_unhandled_error_test3', 154 | executable( '_unhandled_error_test3', 155 | 'test/_unhandled_error_test3.cpp', 156 | dependencies: deps ), 157 | should_fail: true ) 158 | 159 | test( '_unhandled_error_test4', 160 | executable( '_unhandled_error_test4', 161 | 'test/_unhandled_error_test4.cpp', 162 | dependencies: deps ), 163 | should_fail: true ) 164 | 165 | test( '_unhandled_error_test5', 166 | executable( '_unhandled_error_test5', 167 | 'test/_unhandled_error_test5.cpp', 168 | dependencies: deps ), 169 | should_fail: true ) 170 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('tls', type : 'combo', choices : ['std', 'boost', 'static'], value : 'std') 2 | option('throw', type : 'combo', choices : ['std', 'boost', 'terminate'], value : 'boost') 3 | option('exception_info', type : 'boolean', value : 'true') 4 | option('rtti', type : 'combo', choices : ['off', 'boost_config'], value : 'boost_config') 5 | -------------------------------------------------------------------------------- /subprojects/boost.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory=boost_1_64_0 3 | 4 | source_url = https://dl.bintray.com/boostorg/release/1.64.0/source/boost_1_64_0.tar.gz 5 | source_filename = boost_1_64_0.tar.gz 6 | source_hash = 0445c22a5ef3bd69f5dfb48354978421a85ab395254a26b1ffb0aa1bfd63a108 7 | 8 | patch_url = https://github.com/zajo/meson_wraps/blob/master/boost/subprojects/boost.tar.gz?raw=true 9 | patch_filename = boost.tar.gz 10 | patch_hash = 4309c932fe21eeeb61f7319f9e97c1c7090ad5e9ed9d0f5c299514b2db948e76 11 | -------------------------------------------------------------------------------- /subprojects/lua.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory=lua-5.1.5 3 | 4 | source_url = http://www.lua.org/ftp/lua-5.1.5.tar.gz 5 | source_filename = lua-5.1.5.tar.gz 6 | source_hash = 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333 7 | 8 | patch_url = https://github.com/zajo/meson_wraps/blob/master/lua/subprojects/lua.tar.gz?raw=true 9 | patch_filename = lua.tar.gz 10 | patch_hash = 51cd288c6ce32cd338e39cf9a1343dcb2ea9f05293055c96dcce87781d3b0afb 11 | -------------------------------------------------------------------------------- /test/Jamfile.v2: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | # Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | import testing ; 7 | import path ; 8 | 9 | project 10 | : requirements 11 | static 12 | -std=c++11 13 | gcc:-pthread 14 | # -DBOOST_NOEXCEPT_THROW_EXCEPTION(x)=throw(x) 15 | ; 16 | 17 | rule 18 | headers-compile-test ( headers * : requirements * : target-tag ? ) 19 | { 20 | local test-names = ; 21 | for local header in $(headers) 22 | { 23 | local target = hdr$(target-tag)-$(header:D=) ; 24 | compile header-test.cpp : $(requirements) REVERGE_HEADER=\"\\" $(header) : $(target) ; 25 | test-names += $(target) ; 26 | } 27 | alias hdrtest$(target-tag) : $(test-names) ; 28 | } 29 | 30 | headers-compile-test [ glob ../include/boost/noexcept/*.hpp ] 31 | : # requirements 32 | : # target-tag 33 | noexcept 34 | ; 35 | 36 | run-fail _rethrow_test.cpp ; 37 | run-fail _unhandled_error_test1.cpp ; 38 | run-fail _unhandled_error_test2.cpp ; 39 | run-fail _unhandled_error_test3.cpp ; 40 | run-fail _unhandled_error_test4.cpp ; 41 | run-fail _unhandled_error_test5.cpp ; 42 | 43 | run check_test.cpp ; 44 | run diagnostic_information_test.cpp ; 45 | run error_info_test.cpp ; 46 | run error_test.cpp ; 47 | run has_current_error_test.cpp ; 48 | run multiple_active_errors_test.cpp ; 49 | run result_mt_test.cpp ; 50 | run result_test.cpp ; 51 | run throw_exception_test.cpp ; 52 | run throw_return_test.cpp ; 53 | run throw_test.cpp ; 54 | 55 | compile ../examples/c_api_example.cpp ; 56 | compile ../examples/FILE_ptr_example.cpp ; 57 | compile ../examples/FILE_ptr_throw_example.cpp ; 58 | compile ../examples/ifstream_example.cpp ; 59 | # compile ../examples/lua_example.cpp ; 60 | -------------------------------------------------------------------------------- /test/_rethrow_test.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | 8 | using namespace boost::noexcept_; 9 | 10 | int 11 | main() 12 | { 13 | throw_(); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /test/_unhandled_error_test1.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | 9 | using namespace boost::noexcept_; 10 | 11 | struct my_error { }; 12 | 13 | boost::optional 14 | fail() noexcept 15 | { 16 | return throw_(my_error()); 17 | } 18 | int 19 | main() 20 | { 21 | (void) fail(); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /test/_unhandled_error_test2.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace boost::noexcept_; 11 | 12 | struct my_error { }; 13 | 14 | boost::optional 15 | fail1() noexcept 16 | { 17 | return throw_(my_error()); 18 | } 19 | int 20 | fail2() noexcept 21 | { 22 | (void) try_(fail1()); 23 | return 42; 24 | } 25 | int 26 | main() 27 | { 28 | (void) fail2(); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /test/_unhandled_error_test3.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace boost::noexcept_; 11 | 12 | struct my_error { }; 13 | struct wrong_error { }; 14 | 15 | boost::optional 16 | fail1() noexcept 17 | { 18 | return throw_(my_error()); 19 | } 20 | int 21 | fail2() noexcept 22 | { 23 | auto r=try_(fail1()); 24 | (void) r.catch_(); 25 | return 42; 26 | } 27 | int 28 | main() 29 | { 30 | (void) fail2(); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /test/_unhandled_error_test4.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace boost::noexcept_; 11 | 12 | struct my_error { }; 13 | 14 | boost::optional 15 | fail1() noexcept 16 | { 17 | return throw_(my_error()); 18 | } 19 | int 20 | buggy() noexcept 21 | { 22 | auto r1=fail1(); 23 | auto r2=fail1(); 24 | //Correct code would be: 25 | //auto r1=try_(fail1()); 26 | //auto r2=fail2(); 27 | return 0; 28 | } 29 | int 30 | main() 31 | { 32 | (void) buggy(); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /test/_unhandled_error_test5.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace boost::noexcept_; 11 | 12 | struct my_error { }; 13 | 14 | boost::optional 15 | fail1() noexcept 16 | { 17 | return throw_(my_error()); 18 | } 19 | boost::optional 20 | buggy() noexcept 21 | { 22 | auto r1=try_(fail1()); 23 | //(void) r1.catch_<>(); //<--use this to ignore the my_error in r1 24 | return fail1(); 25 | } 26 | int 27 | main() 28 | { 29 | (void) buggy(); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /test/check_test.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace boost::noexcept_; 13 | 14 | struct get_int_failed { }; 15 | struct negate_failed { }; 16 | struct convert_to_string_failed { }; 17 | struct reverse_string_failed { }; 18 | struct do_work1_failed { }; 19 | struct do_work2_failed { }; 20 | struct do_work3_failed { }; 21 | 22 | static int in_get_int_=0; 23 | static int in_negate_=0; 24 | static int in_convert_to_string_=0; 25 | static int in_reverse_string_=0; 26 | static int in_do_work1_=0; 27 | static int in_do_work2_=0; 28 | static int in_do_work3_=0; 29 | 30 | bool 31 | check_counters( int in_get_int, int in_negate, int in_convert_to_string, int in_reverse_string, bool clear=true ) noexcept 32 | { 33 | bool r = in_get_int==in_get_int_ && in_negate==in_negate_ && in_convert_to_string==in_convert_to_string_ && in_reverse_string==in_reverse_string_; 34 | if( clear ) 35 | in_get_int_=in_negate_=in_convert_to_string_=in_reverse_string_=0; 36 | return r; 37 | } 38 | int 39 | get_int( bool succeed ) noexcept 40 | { 41 | ++in_get_int_; 42 | if( succeed ) 43 | return 42; 44 | else 45 | return throw_(get_int_failed()); 46 | } 47 | int 48 | negate( bool succeed, int x ) noexcept 49 | { 50 | BOOST_NOEXCEPT_CHECK 51 | ++in_negate_; 52 | if( succeed ) 53 | return -x; 54 | else 55 | return throw_(negate_failed()); 56 | } 57 | std::string 58 | convert_to_string( bool succeed, int x ) noexcept 59 | { 60 | BOOST_NOEXCEPT_CHECK 61 | ++in_convert_to_string_; 62 | if( succeed ) 63 | return std::to_string(x); 64 | else 65 | return throw_(convert_to_string_failed()); 66 | } 67 | std::string 68 | reverse_string( bool succeed, std::string x ) noexcept 69 | { 70 | BOOST_NOEXCEPT_CHECK 71 | ++in_reverse_string_; 72 | if( succeed ) 73 | { 74 | std::reverse(x.begin(),x.end()); 75 | return x; 76 | } 77 | else 78 | return throw_(reverse_string_failed()); 79 | } 80 | bool 81 | check_do_work_counters( int in_do_work1, int in_do_work2, int in_do_work3, bool clear=true ) noexcept 82 | { 83 | bool r = in_do_work1==in_do_work1_ && in_do_work2==in_do_work2_ && in_do_work3==in_do_work3_; 84 | if( clear ) 85 | in_do_work1_=in_do_work2_=in_do_work3_=0; 86 | return r; 87 | } 88 | bool 89 | do_work1( bool succeed ) noexcept 90 | { 91 | BOOST_NOEXCEPT_CHECK 92 | ++in_do_work1_; 93 | if( succeed ) 94 | return true; 95 | else 96 | return throw_(do_work1_failed()); 97 | } 98 | bool 99 | do_work2( bool succeed ) noexcept 100 | { 101 | BOOST_NOEXCEPT_CHECK 102 | ++in_do_work2_; 103 | if( succeed ) 104 | return true; 105 | else 106 | return throw_(do_work2_failed()); 107 | } 108 | bool 109 | do_work3( bool succeed ) noexcept 110 | { 111 | BOOST_NOEXCEPT_CHECK 112 | ++in_do_work3_; 113 | if( succeed ) 114 | return true; 115 | else 116 | return throw_(do_work3_failed()); 117 | } 118 | bool 119 | do_work( bool succeed1, bool succeed2, bool succeed3 ) noexcept 120 | { 121 | return 122 | do_work1(succeed1) && 123 | do_work2(succeed2) && 124 | do_work3(succeed3); 125 | } 126 | int 127 | main() 128 | { 129 | BOOST_TEST( 130 | reverse_string( true, 131 | convert_to_string( true, 132 | negate( true, 133 | get_int( true ) ) ) ) == "24-" ); 134 | BOOST_TEST(!has_current_error()); 135 | BOOST_TEST(check_counters(1,1,1,1)); 136 | if( auto r=try_( 137 | reverse_string( true, 138 | convert_to_string( true, 139 | negate( true, 140 | get_int( false ) ) ) ) ) ) 141 | BOOST_TEST(false); 142 | else 143 | { 144 | BOOST_TEST(check_counters(1,0,0,0)); 145 | BOOST_TEST(r.catch_()!=0); 146 | } 147 | if( auto r=try_( 148 | reverse_string( true, 149 | convert_to_string( true, 150 | negate( false, 151 | get_int( true ) ) ) ) ) ) 152 | BOOST_TEST(false); 153 | else 154 | { 155 | BOOST_TEST(check_counters(1,1,0,0)); 156 | BOOST_TEST(r.catch_()!=0); 157 | } 158 | if( auto r=try_( 159 | reverse_string( true, 160 | convert_to_string( false, 161 | negate( true, 162 | get_int( true ) ) ) ) ) ) 163 | BOOST_TEST(false); 164 | else 165 | { 166 | BOOST_TEST(check_counters(1,1,1,0)); 167 | BOOST_TEST(r.catch_()!=0); 168 | } 169 | if( auto r=try_( 170 | reverse_string( false, 171 | convert_to_string( true, 172 | negate( true, 173 | get_int( true ) ) ) ) ) ) 174 | BOOST_TEST(false); 175 | else 176 | { 177 | BOOST_TEST(check_counters(1,1,1,1)); 178 | BOOST_TEST(r.catch_()!=0); 179 | } 180 | if( auto r=try_(do_work(false,true,true)) ) 181 | BOOST_TEST(false); 182 | else 183 | { 184 | BOOST_TEST(check_do_work_counters(1,0,0)); 185 | BOOST_TEST(r.catch_()!=0); 186 | } 187 | if( auto r=try_(do_work(true,false,true)) ) 188 | BOOST_TEST(false); 189 | else 190 | { 191 | BOOST_TEST(check_do_work_counters(1,1,0)); 192 | BOOST_TEST(r.catch_()!=0); 193 | } 194 | if( auto r=try_(do_work(true,true,false)) ) 195 | BOOST_TEST(false); 196 | else 197 | { 198 | BOOST_TEST(check_do_work_counters(1,1,1)); 199 | BOOST_TEST(r.catch_()!=0); 200 | } 201 | { 202 | auto r=try_(do_work(true,true,true)); 203 | BOOST_TEST(r); 204 | check_do_work_counters(1,1,1); 205 | } 206 | return boost::report_errors(); 207 | } 208 | -------------------------------------------------------------------------------- /test/diagnostic_information_test.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace boost::noexcept_; 12 | 13 | typedef boost::error_info answer; 14 | 15 | class 16 | my_error: 17 | public std::exception 18 | { 19 | char const * what() const noexcept { return "my_error"; } 20 | public: 21 | my_error() noexcept { }; 22 | }; 23 | int 24 | f1() noexcept 25 | { 26 | return THROW_(my_error()); 27 | } 28 | int 29 | f2() noexcept 30 | { 31 | if( auto r=try_(f1()) ) 32 | return r.get(); 33 | else 34 | { 35 | if( auto xi=r.catch_() ) 36 | *xi << answer(42); 37 | return r.throw_(); 38 | } 39 | } 40 | int 41 | main( int argc, char const * argv[ ] ) 42 | { 43 | if( auto r=try_(f2()) ) 44 | BOOST_TEST(false); 45 | else 46 | { 47 | auto & err=*r.catch_(); 48 | std::cout << boost::diagnostic_information(err) << '\n'; 49 | } 50 | return boost::report_errors(); 51 | } 52 | -------------------------------------------------------------------------------- /test/error_info_test.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace boost::noexcept_; 12 | 13 | struct my_error { }; 14 | typedef boost::error_info answer; 15 | 16 | int 17 | f1() noexcept 18 | { 19 | return THROW_(my_error()); 20 | } 21 | int 22 | f2() noexcept 23 | { 24 | if( auto r=try_(f1()) ) 25 | return r.get(); 26 | else 27 | { 28 | (*r.catch_()) << answer(42); 29 | return r.throw_(); 30 | } 31 | } 32 | int 33 | main( int argc, char const * argv[ ] ) 34 | { 35 | if( auto r=try_(f2()) ) 36 | BOOST_TEST(false); 37 | else 38 | { 39 | auto & info=*r.catch_(); 40 | BOOST_TEST(boost::get_error_info(info) && *boost::get_error_info(info)==42); 41 | } 42 | return boost::report_errors(); 43 | } 44 | -------------------------------------------------------------------------------- /test/error_test.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace boost::noexcept_::noexcept_detail; 13 | 14 | struct 15 | test_base 16 | { 17 | std::shared_ptr track_; 18 | protected: 19 | test_base( std::weak_ptr & track ) noexcept: 20 | track_(std::make_shared(42)) 21 | { 22 | track=track_; 23 | } 24 | virtual 25 | ~test_base() 26 | { 27 | } 28 | test_base( test_base const & ) = default; 29 | test_base( test_base && ) = default; 30 | }; 31 | struct 32 | test_type00: 33 | test_base 34 | { 35 | test_type00( std::weak_ptr & track ) noexcept: test_base(track) { } 36 | test_type00( test_type00 const & ) = default; 37 | test_type00( test_type00 && ) = default; 38 | }; 39 | struct 40 | test_type01: 41 | test_base, 42 | std::exception 43 | { 44 | test_type01( std::weak_ptr & track ) noexcept: test_base(track) { } 45 | test_type01( test_type01 const & ) = default; 46 | test_type01( test_type01 && ) = default; 47 | }; 48 | #ifndef BOOST_NOEXCEPT_NO_EXCEPTION_INFO 49 | struct 50 | test_type10: 51 | test_base, 52 | boost::noexcept_::exception_info 53 | { 54 | test_type10( std::weak_ptr & track ) noexcept: test_base(track) { } 55 | test_type10( test_type10 const & ) = default; 56 | test_type10( test_type10 && ) = default; 57 | }; 58 | struct 59 | test_type11: 60 | test_base, 61 | std::exception, 62 | boost::noexcept_::exception_info 63 | { 64 | test_type11( std::weak_ptr & track ) noexcept: test_base(track) { } 65 | test_type11( test_type11 const & ) = default; 66 | test_type11( test_type11 && ) = default; 67 | }; 68 | #endif 69 | template 70 | bool 71 | check( error_base * eb, bool dynamic ) noexcept 72 | { 73 | return 74 | eb && 75 | eb->get_std_exception() && 76 | dynamic_cast(eb->get_std_exception())==eb && 77 | #ifdef BOOST_NOEXCEPT_NO_EXCEPTION_INFO 78 | !eb->get_exception_info() && 79 | #else 80 | eb->get_exception_info() && dynamic_cast(eb->get_exception_info())==eb && 81 | #endif 82 | eb->get_obj(&tid_) && 83 | (!dynamic || dynamic_cast(static_cast(eb->get_obj(&tid_)))==eb->get_std_exception()); 84 | } 85 | template 86 | void 87 | test_lifetime() noexcept 88 | { 89 | error_storage st1; 90 | error_base * err1=0; 91 | mover_t * mv1=0; 92 | std::weak_ptr tr; 93 | { 94 | error e1((T(tr))); 95 | BOOST_TEST(e1.get() && e1.get()->track_ && tr.lock()==e1.get()->track_); 96 | error e2(std::move(e1)); 97 | BOOST_TEST(e1.get()==0); 98 | BOOST_TEST(e2.get() && e2.get()->track_ && tr.lock()==e2.get()->track_); 99 | e2.move_to(mv1,&st1,err1); 100 | BOOST_TEST(e1.get()==0); 101 | BOOST_TEST(e2.get()==0); 102 | BOOST_TEST(!tr.expired()); 103 | } 104 | BOOST_TEST(!tr.expired()); 105 | BOOST_TEST(err1!=0); 106 | BOOST_TEST(mv1==&move_::type>); 107 | BOOST_TEST(check(err1,true)); 108 | { 109 | error e1(mv1,&st1,err1); 110 | BOOST_TEST(err1==0); 111 | BOOST_TEST(e1.get() && e1.get()->track_ && tr.lock()==e1.get()->track_); 112 | error e2(std::move(e1)); 113 | BOOST_TEST(e1.get()==0); 114 | BOOST_TEST(e2.get() && e2.get()->track_ && tr.lock()==e2.get()->track_); 115 | BOOST_TEST(!tr.expired()); 116 | } 117 | BOOST_TEST(tr.expired()); 118 | } 119 | template <> 120 | void 121 | test_lifetime() noexcept 122 | { 123 | error_storage st1; 124 | error_base * err1=0; 125 | mover_t * mv1=0; 126 | { 127 | error e1(42); 128 | BOOST_TEST(e1.get()!=0); 129 | error e2(std::move(e1)); 130 | BOOST_TEST(e1.get()==0); 131 | BOOST_TEST(e2.get()!=0); 132 | e2.move_to(mv1,&st1,err1); 133 | BOOST_TEST(e1.get()==0); 134 | BOOST_TEST(e2.get()==0); 135 | } 136 | BOOST_TEST(err1!=0); 137 | BOOST_TEST(mv1==&move_::type>); 138 | BOOST_TEST(check(err1,false)); 139 | { 140 | error e1(mv1,&st1,err1); 141 | BOOST_TEST(err1==0); 142 | BOOST_TEST(e1.get()!=0); 143 | error e2(std::move(e1)); 144 | BOOST_TEST(e1.get()==0); 145 | BOOST_TEST(e2.get()!=0); 146 | } 147 | } 148 | template 149 | void 150 | test_ceh() noexcept 151 | { 152 | std::weak_ptr tr; 153 | ceh().put(T(tr)); 154 | BOOST_TEST(tr.lock().use_count()==2); 155 | BOOST_TEST(ceh().extract().get()!=0); 156 | } 157 | template <> 158 | void 159 | test_ceh() noexcept 160 | { 161 | ceh().put(42); 162 | BOOST_TEST(*ceh().extract().get()==42); 163 | } 164 | template 165 | void 166 | test_throw_exception() 167 | { 168 | std::weak_ptr tr; 169 | error e((T(tr))); 170 | BOOST_TEST(!tr.expired()); 171 | try 172 | { 173 | e.throw_exception(); 174 | BOOST_TEST(false); 175 | } 176 | catch( T & e ) 177 | { 178 | BOOST_TEST(tr.lock()==e.track_); 179 | BOOST_TEST(check(dynamic_cast(&e),true)); 180 | } 181 | try 182 | { 183 | e.throw_exception(); 184 | BOOST_TEST(false); 185 | } 186 | catch( std::exception & e ) 187 | { 188 | BOOST_TEST(check(dynamic_cast(&e),true)); 189 | } 190 | #ifndef BOOST_NOEXCEPT_NO_EXCEPTION_INFO 191 | try 192 | { 193 | e.throw_exception(); 194 | BOOST_TEST(false); 195 | } 196 | catch( boost::noexcept_::exception_info & e ) 197 | { 198 | BOOST_TEST(check(dynamic_cast(&e),true)); 199 | } 200 | #endif 201 | } 202 | template <> 203 | void 204 | test_throw_exception() 205 | { 206 | error e(42); 207 | try 208 | { 209 | e.throw_exception(); 210 | BOOST_TEST(false); 211 | } 212 | catch( int & e ) 213 | { 214 | BOOST_TEST(e==42); 215 | } 216 | } 217 | int 218 | main() 219 | { 220 | test_lifetime(); 221 | test_lifetime(); 222 | #ifndef BOOST_NOEXCEPT_NO_EXCEPTION_INFO 223 | test_lifetime(); 224 | test_lifetime(); 225 | #endif 226 | test_lifetime(); 227 | test_throw_exception(); 228 | test_throw_exception(); 229 | #ifndef BOOST_NOEXCEPT_NO_EXCEPTION_INFO 230 | test_throw_exception(); 231 | test_throw_exception(); 232 | #endif 233 | test_throw_exception(); 234 | test_ceh(); 235 | test_ceh(); 236 | #ifndef BOOST_NOEXCEPT_NO_EXCEPTION_INFO 237 | test_ceh(); 238 | test_ceh(); 239 | #endif 240 | test_ceh(); 241 | return boost::report_errors(); 242 | } 243 | -------------------------------------------------------------------------------- /test/has_current_error_test.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace boost::noexcept_; 11 | 12 | struct my_error { }; 13 | 14 | int 15 | main() 16 | { 17 | BOOST_TEST(!has_current_error()); 18 | void * a=throw_(my_error()); 19 | BOOST_TEST(!a); 20 | BOOST_TEST(has_current_error()); 21 | { 22 | auto r=try_(std::move(a)); 23 | BOOST_TEST(!has_current_error()); 24 | } 25 | BOOST_TEST(has_current_error()); 26 | { 27 | auto r=try_(std::move(a)); 28 | BOOST_TEST(!has_current_error()); 29 | BOOST_TEST(r.catch_<>()!=0); 30 | } 31 | BOOST_TEST(!has_current_error()); 32 | return boost::report_errors(); 33 | } 34 | -------------------------------------------------------------------------------- /test/header-test.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include REVERGE_HEADER 7 | #include REVERGE_HEADER 8 | -------------------------------------------------------------------------------- /test/multiple_active_errors_test.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace boost::noexcept_; 13 | 14 | struct tag1; 15 | struct tag2; 16 | struct tag3; 17 | template struct compute_int_failed { }; 18 | 19 | template 20 | boost::optional 21 | compute_int( bool should_succeed ) noexcept 22 | { 23 | if( should_succeed ) 24 | return 42; 25 | else 26 | return throw_(compute_int_failed()); 27 | } 28 | void 29 | test1() noexcept 30 | { 31 | if( auto err1=try_(compute_int(false)) ) 32 | BOOST_TEST(false); 33 | else 34 | { 35 | if( auto err2=try_(compute_int(false)) ) 36 | BOOST_TEST(false); 37 | else 38 | { 39 | BOOST_TEST(!err1); 40 | if( auto err3=try_(compute_int(false)) ) 41 | BOOST_TEST(false); 42 | else 43 | { 44 | BOOST_TEST(!err1); 45 | BOOST_TEST(!err2); 46 | BOOST_TEST(err3.catch_ >()!=0); 47 | BOOST_TEST(!err3); 48 | } 49 | BOOST_TEST(err2.catch_ >()!=0); 50 | BOOST_TEST(!err2); 51 | } 52 | BOOST_TEST(err1.catch_ >()!=0); 53 | BOOST_TEST(!err1); 54 | } 55 | } 56 | void 57 | test2() noexcept 58 | { 59 | auto err1=try_(compute_int(false)); 60 | auto err2=try_(compute_int(false)); 61 | auto err3=try_(compute_int(false)); 62 | BOOST_TEST(!err1); 63 | BOOST_TEST(!err2); 64 | BOOST_TEST(!err3); 65 | BOOST_TEST(err1.catch_ >()!=0); 66 | BOOST_TEST(err2.catch_ >()!=0); 67 | BOOST_TEST(err3.catch_ >()!=0); 68 | } 69 | int 70 | main() 71 | { 72 | test1(); 73 | test2(); 74 | return boost::report_errors(); 75 | } 76 | -------------------------------------------------------------------------------- /test/result_mt_test.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace boost::noexcept_; 16 | 17 | struct compute_error { }; 18 | 19 | int 20 | compute() noexcept 21 | { 22 | if( rand()%2 ) 23 | return 42; 24 | else 25 | return throw_(compute_error()); 26 | } 27 | 28 | int 29 | main() 30 | { 31 | std::vector > > fut; 32 | std::generate_n( std::inserter(fut,fut.end()), 100, [ ] 33 | { 34 | std::packaged_task()> task( [ ] { return try_(compute()); } ); 35 | auto f=task.get_future(); 36 | std::thread(std::move(task)).detach(); 37 | return f; 38 | } ); 39 | std::cout << "Results:\n"; 40 | for( auto & f:fut ) 41 | { 42 | f.wait(); 43 | if( auto r=f.get() ) 44 | std::cout << "Success! Answer=" << r.get() << '\n'; 45 | else if( auto err=r.catch_() ) 46 | std::cout << "Failure!\n"; 47 | } 48 | return boost::report_errors(); 49 | } 50 | -------------------------------------------------------------------------------- /test/result_test.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace boost::noexcept_; 11 | 12 | template struct my_error { }; 13 | 14 | int 15 | main( int argc, char const * argv[ ] ) 16 | { 17 | { 18 | result h=try_(42); 19 | BOOST_TEST(!h.has_error()); 20 | BOOST_TEST(!h.has_unhandled_error()); 21 | BOOST_TEST(!h.catch_ >()); 22 | BOOST_TEST(h.get()==42); 23 | } 24 | BOOST_TEST(!has_current_error()); 25 | (void) throw_(my_error<1>()); 26 | BOOST_TEST(has_current_error()); 27 | { 28 | auto h=current_error(); 29 | BOOST_TEST(!has_current_error()); 30 | BOOST_TEST(h.has_error()); 31 | BOOST_TEST(h.has_unhandled_error()); 32 | } 33 | BOOST_TEST(has_current_error()); 34 | { 35 | auto h=current_error(); 36 | BOOST_TEST(!has_current_error()); 37 | BOOST_TEST(h.has_error()); 38 | BOOST_TEST(h.has_unhandled_error()); 39 | BOOST_TEST(h.catch_ >()!=0); 40 | BOOST_TEST(h.has_error()); 41 | BOOST_TEST(!h.has_unhandled_error()); 42 | } 43 | BOOST_TEST(!has_current_error()); 44 | (void) throw_(my_error<1>()); 45 | BOOST_TEST(has_current_error()); 46 | { 47 | auto h1=current_error(); 48 | BOOST_TEST(h1.has_error()); 49 | BOOST_TEST(h1.has_unhandled_error()); 50 | (void) throw_(my_error<2>()); 51 | BOOST_TEST(has_current_error()); 52 | { 53 | auto h2=current_error(); 54 | BOOST_TEST(!has_current_error()); 55 | BOOST_TEST(h2.has_error()); 56 | BOOST_TEST(h2.has_unhandled_error()); 57 | BOOST_TEST(!h2.catch_ >()); 58 | BOOST_TEST(h2.has_error()); 59 | BOOST_TEST(h2.has_unhandled_error()); 60 | BOOST_TEST(h2.catch_ >()!=0); 61 | BOOST_TEST(h2.has_error()); 62 | BOOST_TEST(!h2.has_unhandled_error()); 63 | } 64 | BOOST_TEST(!has_current_error()); 65 | BOOST_TEST(!h1.catch_ >()); 66 | BOOST_TEST(h1.has_error()); 67 | BOOST_TEST(h1.has_unhandled_error()); 68 | BOOST_TEST(h1.catch_ >()!=0); 69 | BOOST_TEST(h1.has_error()); 70 | BOOST_TEST(!h1.has_unhandled_error()); 71 | } 72 | BOOST_TEST(!has_current_error()); 73 | return boost::report_errors(); 74 | } 75 | -------------------------------------------------------------------------------- /test/throw_exception_test.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace boost::noexcept_; 12 | 13 | struct failure { }; 14 | 15 | boost::optional 16 | succeeds() noexcept 17 | { 18 | return 42; 19 | } 20 | boost::optional 21 | fails() noexcept 22 | { 23 | return throw_(failure()); 24 | } 25 | int 26 | main() 27 | { 28 | BOOST_TEST(succeeds().value()==42); 29 | BOOST_TEST(try_(succeeds()).get().value()==42); 30 | try 31 | { 32 | (void) try_(fails()).get(); 33 | BOOST_TEST(false); 34 | } 35 | catch( failure & ) 36 | { 37 | } 38 | return boost::report_errors(); 39 | } 40 | -------------------------------------------------------------------------------- /test/throw_return_test.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace boost::noexcept_; 12 | 13 | template T test_throw_return(); 14 | template <> bool test_throw_return() { return throw_(); } 15 | template <> char test_throw_return() { return throw_(); } 16 | template <> signed char test_throw_return() { return throw_(); } 17 | template <> unsigned char test_throw_return() { return throw_(); } 18 | template <> wchar_t test_throw_return() { return throw_(); } 19 | template <> char16_t test_throw_return() { return throw_(); } 20 | template <> char32_t test_throw_return() { return throw_(); } 21 | template <> short test_throw_return() { return throw_(); } 22 | template <> unsigned short test_throw_return() { return throw_(); } 23 | template <> int test_throw_return() { return throw_(); } 24 | template <> unsigned int test_throw_return() { return throw_(); } 25 | template <> long test_throw_return() { return throw_(); } 26 | template <> unsigned long test_throw_return() { return throw_(); } 27 | template <> long long test_throw_return() { return throw_(); } 28 | template <> unsigned long long test_throw_return() { return throw_(); } 29 | template <> int * test_throw_return() { return throw_(); } 30 | template <> boost::shared_ptr test_throw_return >() { return throw_(); } 31 | 32 | struct my_error { }; 33 | int 34 | throw_error() noexcept 35 | { 36 | return throw_(my_error()); 37 | } 38 | 39 | int 40 | main() 41 | { 42 | throw_error(); 43 | BOOST_TEST((test_throw_return()==false)); 44 | BOOST_TEST((test_throw_return()==static_cast(-1))); 45 | BOOST_TEST((test_throw_return()==static_cast(-1))); 46 | BOOST_TEST((test_throw_return()==static_cast(-1))); 47 | BOOST_TEST((test_throw_return()==static_cast(-1))); 48 | BOOST_TEST((test_throw_return()==static_cast(-1))); 49 | BOOST_TEST((test_throw_return()==static_cast(-1))); 50 | BOOST_TEST((test_throw_return()==static_cast(-1))); 51 | BOOST_TEST((test_throw_return()==static_cast(-1))); 52 | BOOST_TEST((test_throw_return()==static_cast(-1))); 53 | BOOST_TEST((test_throw_return()==static_cast(-1))); 54 | BOOST_TEST((test_throw_return()==static_cast(-1))); 55 | BOOST_TEST((test_throw_return()==static_cast(-1))); 56 | BOOST_TEST((test_throw_return()==static_cast(-1))); 57 | BOOST_TEST((test_throw_return()==static_cast(-1))); 58 | BOOST_TEST((test_throw_return()==0)); 59 | BOOST_TEST((test_throw_return >()==boost::shared_ptr())); 60 | current_error().catch_<>(); 61 | return boost::report_errors(); 62 | } 63 | -------------------------------------------------------------------------------- /test/throw_test.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017-2018 Emil Dotchevski and Reverge Studios, Inc. 2 | 3 | //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace boost::noexcept_; 12 | 13 | struct 14 | f1_failed 15 | { 16 | int const val; 17 | explicit 18 | f1_failed( int val ) noexcept: 19 | val(val) 20 | { 21 | } 22 | }; 23 | struct 24 | f4_failed: 25 | f1_failed 26 | { 27 | explicit 28 | f4_failed( int val ) noexcept: 29 | f1_failed(val) 30 | { 31 | } 32 | }; 33 | int 34 | f1() noexcept 35 | { 36 | return throw_(f1_failed(1)); 37 | } 38 | int 39 | f2() noexcept 40 | { 41 | return f1(); 42 | } 43 | int 44 | f3() noexcept 45 | { 46 | auto r=try_(f2()); 47 | BOOST_TEST(!r); 48 | return r.throw_(); 49 | } 50 | int 51 | f4() noexcept 52 | { 53 | auto r=try_(f3()); 54 | BOOST_TEST(!r); 55 | BOOST_TEST(r.catch_()->val==1); 56 | return throw_(f4_failed(2)); 57 | } 58 | int 59 | f5() noexcept 60 | { 61 | BOOST_TEST(f4()==-1); 62 | return throw_(); 63 | } 64 | void 65 | f6_a() noexcept 66 | { 67 | auto r=try_(f5()); 68 | BOOST_TEST(!r); 69 | BOOST_TEST(r.catch_()->val==2); 70 | } 71 | void 72 | f6_b() noexcept 73 | { 74 | auto r=try_(f5()); 75 | BOOST_TEST(!r); 76 | if( auto v=r.catch_() ) 77 | BOOST_TEST(v->val==2); 78 | else 79 | (void) r.catch_(); //In case of no RTTI 80 | } 81 | struct derives_from_std_exception: std::exception { }; 82 | int 83 | throw_std_exception() noexcept 84 | { 85 | return throw_(derives_from_std_exception()); 86 | } 87 | void 88 | std_exception_test() noexcept 89 | { 90 | if( auto r=try_(throw_std_exception()) ) 91 | BOOST_TEST(false); 92 | else 93 | BOOST_TEST(r.catch_<>()!=0); 94 | } 95 | int 96 | rethrow_fn() noexcept 97 | { 98 | auto r=try_(f1()); 99 | BOOST_TEST(!r); 100 | BOOST_TEST(!has_current_error()); 101 | BOOST_TEST(r.catch_<>()!=0); 102 | return r.throw_(); 103 | } 104 | void 105 | rethrow_test() noexcept 106 | { 107 | auto r=try_(rethrow_fn()); 108 | BOOST_TEST(!r); 109 | BOOST_TEST(!has_current_error()); 110 | BOOST_TEST(r.catch_<>()!=0); 111 | } 112 | void 113 | throw_void_test() noexcept 114 | { 115 | return (void) throw_(f1_failed(42)); 116 | } 117 | int 118 | throw_error_code_test() 119 | { 120 | return throw_(std::error_code{42,std::system_category()}); 121 | } 122 | int 123 | main() 124 | { 125 | f6_a(); 126 | f6_b(); 127 | std_exception_test(); 128 | rethrow_test(); 129 | { 130 | throw_void_test(); 131 | auto r=current_error(); 132 | BOOST_TEST(r.has_error()); 133 | BOOST_TEST(r.catch_() && r.catch_()->val==42); 134 | } 135 | { 136 | auto r=try_(throw_error_code_test()); 137 | BOOST_TEST(!r); 138 | BOOST_TEST(r.catch_()->value()==42); 139 | } 140 | return boost::report_errors(); 141 | } 142 | --------------------------------------------------------------------------------