From 10eb45ca7d11a35df14e0fa89939a3e3185c0aa9 Mon Sep 17 00:00:00 2001 From: "Niall Douglas (s [underscore] sourceforge {at} nedprod [dot] com)" Date: Tue, 16 Jul 2024 21:33:35 +0100 Subject: [PATCH] - Outcome.Experimental has had C representation support since the beginning, however it had been mainly intended that C++ would originate Results, they would pass through C, and back into C++. It hadn't really been expected that C would want to do much with Results other than inspect them for happy or sad path. It turns out there is more demand than expected for a more functional Result from within C, so this release adds the power to create Results in success and two types of failure, semantic comparison of Results, and printing of Result messages. You can also wrap a C enum into a quick status code from enum, allowing easy custom C error coding from 100% within C. [The documentation for the C support]({{% relref "../experimental/c-api" %}}) has been updated to reflect the new facilities. --- cmake/headers.cmake | 1 + cmake/tests.cmake | 1 + doc/src/content/_index.md | 20 +- doc/src/content/changelog/_index.md | 24 +- doc/src/content/experimental/advantages.md | 3 + .../experimental/c-api/from-c/_index.md | 50 +++ .../experimental/c-api/from-c/declare.md | 11 + .../experimental/c-api/from-c/system_code.md | 86 +++++ .../content/experimental/c-api/from-c/try.md | 18 + .../content/experimental/c-api/from-c/use.md | 14 + .../{limitations.md => from-cxx/_index.md} | 4 +- .../c-api/{ => from-cxx}/example.md | 0 .../c-api/{ => from-cxx}/example2.md | 0 .../content/experimental/c-api/reference.md | 98 ------ doc/src/content/experimental/outcome.md | 2 +- .../worked-example-long/_index.md | 36 ++ .../constructor.md | 0 .../implicit_conversion.md | 2 +- .../message.md | 0 .../preamble.md | 0 .../source.md | 2 +- .../string_ref.md | 0 .../value_type.md | 0 .../experimental/worked-example/_index.md | 49 ++- .../worked-example/implicit-construction.md | 13 + doc/src/snippets/c_api.c | 2 +- doc/src/snippets/c_api2.cpp | 101 ++++++ doc/src/snippets/intro_c_example.cpp | 72 ++++ .../snippets/quick_status_code_from_enum.cpp | 107 ++++++ include/outcome/detail/revision.hpp | 6 +- include/outcome/detail/try.h | 98 ++++++ include/outcome/experimental/result.h | 316 +++++++++++++++++- include/outcome/try.hpp | 56 +--- single-header/outcome-basic.hpp | 92 +++-- single-header/outcome-experimental.hpp | 92 +++-- single-header/outcome.hpp | 92 +++-- test/tests/experimental-c-result.cpp | 197 +++++++++++ 37 files changed, 1320 insertions(+), 345 deletions(-) create mode 100644 doc/src/content/experimental/c-api/from-c/_index.md create mode 100644 doc/src/content/experimental/c-api/from-c/declare.md create mode 100644 doc/src/content/experimental/c-api/from-c/system_code.md create mode 100644 doc/src/content/experimental/c-api/from-c/try.md create mode 100644 doc/src/content/experimental/c-api/from-c/use.md rename doc/src/content/experimental/c-api/{limitations.md => from-cxx/_index.md} (98%) rename doc/src/content/experimental/c-api/{ => from-cxx}/example.md (100%) rename doc/src/content/experimental/c-api/{ => from-cxx}/example2.md (100%) delete mode 100644 doc/src/content/experimental/c-api/reference.md create mode 100644 doc/src/content/experimental/worked-example-long/_index.md rename doc/src/content/experimental/{worked-example => worked-example-long}/constructor.md (100%) rename doc/src/content/experimental/{worked-example => worked-example-long}/implicit_conversion.md (86%) rename doc/src/content/experimental/{worked-example => worked-example-long}/message.md (100%) rename doc/src/content/experimental/{worked-example => worked-example-long}/preamble.md (100%) rename doc/src/content/experimental/{worked-example => worked-example-long}/source.md (81%) rename doc/src/content/experimental/{worked-example => worked-example-long}/string_ref.md (100%) rename doc/src/content/experimental/{worked-example => worked-example-long}/value_type.md (100%) create mode 100644 doc/src/content/experimental/worked-example/implicit-construction.md create mode 100644 doc/src/snippets/c_api2.cpp create mode 100644 doc/src/snippets/intro_c_example.cpp create mode 100644 doc/src/snippets/quick_status_code_from_enum.cpp create mode 100644 include/outcome/detail/try.h create mode 100644 test/tests/experimental-c-result.cpp diff --git a/cmake/headers.cmake b/cmake/headers.cmake index f97ef9b6d1c..734e7d0023a 100644 --- a/cmake/headers.cmake +++ b/cmake/headers.cmake @@ -20,6 +20,7 @@ set(outcome_HEADERS "include/outcome/detail/revision.hpp" "include/outcome/detail/trait_std_error_code.hpp" "include/outcome/detail/trait_std_exception.hpp" + "include/outcome/detail/try.h" "include/outcome/detail/value_storage.hpp" "include/outcome/detail/version.hpp" "include/outcome/experimental/coroutine_support.hpp" diff --git a/cmake/tests.cmake b/cmake/tests.cmake index 3c0ab3b448d..2e0430a21d0 100644 --- a/cmake/tests.cmake +++ b/cmake/tests.cmake @@ -9,6 +9,7 @@ set(outcome_TESTS "test/tests/core-result.cpp" "test/tests/coroutine-support.cpp" "test/tests/default-construction.cpp" + "test/tests/experimental-c-result.cpp" "test/tests/experimental-core-outcome-status.cpp" "test/tests/experimental-core-result-status.cpp" "test/tests/experimental-p0709a.cpp" diff --git a/doc/src/content/_index.md b/doc/src/content/_index.md index 911093ef723..3f70832f3e0 100644 --- a/doc/src/content/_index.md +++ b/doc/src/content/_index.md @@ -34,6 +34,8 @@ Outcome is a set of tools for reporting and handling function failures in contex - where interoperation with C code, without having to resort to C++ exception wrapper shims, is important. + - where your mostly C code base needs exception-like error handling, and the subset of Outcome's functionality available in C is sufficient for your needs. + Outcome addresses failure handling through returning a special type from functions, which is able to store either a successfully computed value (or `void`), or the information about failure. Outcome also comes with a set of idioms for dealing with such types. Particular care has been taken to ensure that Outcome has the lowest possible impact on build times, @@ -44,7 +46,7 @@ Fully deterministic all-`noexcept` C++ Coroutine support in Outcome is particula supply Outcome-optimising {{< api "eager/atomic_eager" >}}, {{< api "lazy/atomic_lazy" >}} and {{" >}} awaitables which work for any user type. -## Sample usage +## Sample usage (C++) The main workhorse in the Outcome library is `result`: it represents either a successfully computed value of type `T`, or a `std::error_code`/`boost::system::error_code`[^2] representing the reason for failure. You use it in the function's return type: @@ -61,6 +63,22 @@ Or, if this function is called in another function that also returns `result` `OUTCOME_TRY` is a control statement. If the returned `result` object contains an error information, the enclosing function is immediately returned with `result` containing the same failure information; otherwise an automatic object of type `T` is available in scope. +## Sample usage \(C) + +Equivalent to the C++ API: `CXX_DECLARE_RESULT_SYSTEM(ident, T)` declares the C type, thereafter `CXX_RESULT_SYSTEM(ident)` refers to it. You use it in the function's return type: + +{{% snippet "intro_c_example.cpp" "signature" %}} + +It is possible to inspect the state manually: + +{{% snippet "intro_c_example.cpp" "inspect" %}} + +Or, if this function is called in another function that also returns `CXX_RESULT_SYSTEM(ident)`, you can use a dedicated control statement: + +{{% snippet "intro_c_example.cpp" "implementation" %}} + +The C Result is guaranteed to be layout identical to its C++ equivalent. Convenience conversion functions are available, but you can reinterpret cast too. + {{% notice note %}} This library joined [the Boost C++ libraries](https://www.boost.org/doc/libs/develop/libs/outcome/doc/html/index.html) in the 1.70 release (Spring 2019). [It can be grafted into much older Boost releases if desired](https://github.com/boostorg/outcome). {{% /notice %}} diff --git a/doc/src/content/changelog/_index.md b/doc/src/content/changelog/_index.md index 5c09a060f17..9bd2a0cb165 100644 --- a/doc/src/content/changelog/_index.md +++ b/doc/src/content/changelog/_index.md @@ -4,7 +4,27 @@ weight = 80 +++ --- -## v2.2.10 ? (Boost 1.86) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.10) +## v2.2.11 ? (Boost 1.87) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.11) + +### Enhancements: + +- Outcome.Experimental has had C representation support since the beginning, however it had +been mainly intended that C++ would originate Results, they would pass through C, and back +into C++. It hadn't really been expected that C would want to do much with Results other than +inspect them for happy or sad path. + + It turns out there is more demand than expected for a more functional Result from within C, +so this release adds the power to create Results in success and two types of failure, semantic +comparison of Results, and printing of Result messages. You can also wrap a C enum into a +quick status code from enum, allowing easy custom C error coding from 100% within C. + + [The documentation for the C support]({{% relref "../experimental/c-api" %}}) has been updated +to reflect the new facilities. + +### Bug fixes: + +--- +## v2.2.10 14th August 2024 (Boost 1.86) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.10) ### Enhancements: @@ -16,8 +36,6 @@ working with that type, as it will print the error message in GDB. Experimental Outcome's `status_code` has also gained its own auto-loading GDB pretty printer with display of `strerror()` if the code domain is POSIX or generic. -### Bug fixes: - --- ## v2.2.9 15th April 2024 (Boost 1.85) [[release]](https://github.com/ned14/outcome/releases/tag/v2.2.9) diff --git a/doc/src/content/experimental/advantages.md b/doc/src/content/experimental/advantages.md index b5d5c936aad..5dda9a009b4 100644 --- a/doc/src/content/experimental/advantages.md +++ b/doc/src/content/experimental/advantages.md @@ -28,6 +28,9 @@ and the dual target source code, being written to tighter discipline, is faster and more deterministic in the default target than it was before the (non-trivial) port to ``. +5. If you want 'official' C support, experimental Outcome is able to +provide that in a way not possible for default Outcome which cannot make +sufficiently strong C compatibility assumptions about `std::error_code`. If you are building a codebase on top of Outcome expecting long term maintenance, the author's personal recommendation is that you design, write, test and diff --git a/doc/src/content/experimental/c-api/from-c/_index.md b/doc/src/content/experimental/c-api/from-c/_index.md new file mode 100644 index 00000000000..737c4576152 --- /dev/null +++ b/doc/src/content/experimental/c-api/from-c/_index.md @@ -0,0 +1,50 @@ ++++ +title = "C Results" +description = "Outcome's C Result support" +weight = 30 ++++ + +The C macro API header `` has some macros for working with any kind of Result: + +
+
CXX_DECLARE_RESULT(ident, T, E) +
Declares to C a basic_result type uniquely +identified by ident. T is available at the +member variable .value, and E is available +at the member variable .error. + +
CXX_RESULT(ident) +
A reference to a previously declared result type with +unique ident. + +
CXX_RESULT_HAS_VALUE(r) +
Evaluates to 1 (true) if the input result has a value. + +
CXX_RESULT_HAS_ERROR(r) +
Evaluates to 1 (true) if the input result has an error. + +
CXX_RESULT_ERROR_IS_ERRNO(r) +
Evaluates to 1 (true) if the input result's error value +is a code in the POSIX errno domain. +
+ +The above let you work, somewhat awkwardly, with any C-compatible +`basic_result`. `basic_result` is trivially copyable and +standard layout if its `T` and `E` are both so, and it has the C layout: + +```c++ +struct cxx_result_##ident +{ + union + { + T value; + E error; + }; + unsigned flags; +}; +``` + +Note that this layout is different to that of [`CXX_DECLARE_STATUS_CODE`]({{% relref "../from-c" %}}) +as the C++ `result` has a different layout if `E` is a status code. + + diff --git a/doc/src/content/experimental/c-api/from-c/declare.md b/doc/src/content/experimental/c-api/from-c/declare.md new file mode 100644 index 00000000000..1e53443a9b4 --- /dev/null +++ b/doc/src/content/experimental/c-api/from-c/declare.md @@ -0,0 +1,11 @@ ++++ +title = "Declare a Result" +description = "Declaring a C Result" +weight = 20 ++++ + +{{% snippet "c_api2.cpp" "preamble" %}} + +The key to making C programming easy is to alias the long complex things +into short easy thing. Obviously `SUCCESS(expr)` and `FAILURE(expr)` is too +generic, but for the purposes of this documentation it makes thing easier. diff --git a/doc/src/content/experimental/c-api/from-c/system_code.md b/doc/src/content/experimental/c-api/from-c/system_code.md new file mode 100644 index 00000000000..3efc5ae6bfd --- /dev/null +++ b/doc/src/content/experimental/c-api/from-c/system_code.md @@ -0,0 +1,86 @@ ++++ +title = "C system error results" +description = "Status code's `std::error` in C" +weight = 10 ++++ + +In v2.2.11, C Result support went from second tier to first tier status, and +now you can create, query and manipulate a subset of Result types entirely from +within C by including ``. + +The subset supported are those `result` which are [a `status_result`]({{% relref "../../status_result" %}}) +i.e. the `E` is hardcoded to `experimental::error` which is the type erased runtime +polymorphic holder for any errored `status_code` whose payload is not bigger +than an `intptr_t`. This is the most useful subset of Outcome Experimental's +possible Result types, allowing arbitrary custom error coding schemes from +any unknown source to work seamlessly with all others, including errors from +the system or third party libraries. + +The operations available to C are: + +
+
CXX_DECLARE_RESULT_SYSTEM(ident, T) +
Declares to C a status_result type uniquely +identified by ident. T is available at the +member variable .value, and struct cxx_status_code_system +is available at the member variable .error. If in C++, +implements C extern functions for making successful and failure results +of this type. + +
CXX_RESULT_SYSTEM(ident) +
A reference to a previously declared status_result type with +unique ident. + +
CXX_MAKE_RESULT_SYSTEM_SUCCESS(ident, expr) (needs C++ counterpart linked into final binary) +
This invokes the aforementioned extern function which creates a status_result +with a successful value of type T. +
CXX_MAKE_RESULT_SYSTEM_FAILURE_POSIX(ident, expr) (needs C++ counterpart linked into final binary) +
This invokes the aforementioned extern function which creates a status_result +with a failure of type posix_code representing a POSIX errno. +
CXX_MAKE_RESULT_SYSTEM_FAILURE_SYSTEM(ident, expr) (needs C++ counterpart linked into final binary) +
This invokes the aforementioned extern function which creates a status_result +with a failure of type posix_code representing a POSIX errno +if on POSIX; if on Windows then a failure of type win32_code +representing a Win32 error code from a Windows API. + +

+
CXX_RESULT_HAS_VALUE(r) +
Evaluates to 1 (true) if the input result has a value. + +
CXX_RESULT_HAS_ERROR(r) +
Evaluates to 1 (true) if the input result has an error. + +
CXX_RESULT_ERROR_IS_ERRNO(r) +
Evaluates to 1 (true) if the input result's error value +is a code in the POSIX errno domain. +

+
CXX_RESULT_SYSTEM_TRY(expr) +
If the status_result returned by expr is +errored, exit the current function returning the result. This obviously +requires that the return type of the current function matches that of +expr. + +
CXX_RESULT_SYSTEM_TRY(cleanup, expr) +
Same as the above, but execute cleanup just before exiting the function +if returning failure. + +
CXX_RESULT_SYSTEM_TRY(var, cleanup, expr) +
Same as the above, but set var equal to the result's .value on success. + +
CXX_RESULT_SYSTEM_TRY(var, ident, cleanup, expr) +
Same as the above, but use ident as the return type instead. This allows +the return type of the calling function to differ from that of expr. +

+
CXX_DECLARE_RESULT_SYSTEM_FROM_ENUM(ident, enum_name, uuid, {enum mapping-sequence, ...}) +
This declares to C an extern function which creates a status_result +from a C enum. If in C++, it implements a quick_status_code_from_enum for +the C enum and the associated extern function, and you will need to supply uuid +and the appropriate enum value mapping sequence +as per the quick_status_code_from_enum documentation. +
CXX_MAKE_RESULT_SYSTEM_FROM_ENUM(ident, enum_name, expr) (needs C++ counterpart linked into final binary) +
This invokes the aforementioned extern function which creates a status_result +from a C enum. +
+ +Using the above you can write C code using Outcome.Experimental's Result type +quite effectively. Let's look at an example of use next. diff --git a/doc/src/content/experimental/c-api/from-c/try.md b/doc/src/content/experimental/c-api/from-c/try.md new file mode 100644 index 00000000000..df5d2857968 --- /dev/null +++ b/doc/src/content/experimental/c-api/from-c/try.md @@ -0,0 +1,18 @@ ++++ +title = "TRY a C Result" +description = "Operation TRY on a C Result" +weight = 40 ++++ + +Thanks to much of the magic of {{< api "OUTCOME_TRY(var, expr)" >}} being implemented +using C preprocessor metaprogramming, we can offer a very similar experience for the +C try operation and without needing anything compiled in C++ as support functions: + +{{% snippet "c_api2.cpp" "try" %}} + +The principle difference is that you can specify a cleanup routine to perform if +failure is encountered. This is especially useful in C, which has no stack unwinding. + +Also due to lack of type sugaring and user defined implicit conversions, if your +callers result type isn't your callee's, you may need to specify what your caller's +result type is so the error state can be correctly propagated. diff --git a/doc/src/content/experimental/c-api/from-c/use.md b/doc/src/content/experimental/c-api/from-c/use.md new file mode 100644 index 00000000000..2861814133e --- /dev/null +++ b/doc/src/content/experimental/c-api/from-c/use.md @@ -0,0 +1,14 @@ ++++ +title = "Using a Result" +description = "Using a C Result" +weight = 30 ++++ + +This models [the earlier C++ example of use]({{% relref "/experimental/worked-example/implicit-construction" %}}), +and its C equivalent isn't much more verbose thanks to our helper typedefs and macros: + +{{% snippet "c_api2.cpp" "using" %}} + +For this to link, the `CXX_DECLARE_RESULT_SYSTEM_FROM_ENUM` macro needs to be +compiled at least once within C++ within the final binary to emit the extern +functions needed by C. diff --git a/doc/src/content/experimental/c-api/limitations.md b/doc/src/content/experimental/c-api/from-cxx/_index.md similarity index 98% rename from doc/src/content/experimental/c-api/limitations.md rename to doc/src/content/experimental/c-api/from-cxx/_index.md index 5b71c426a58..adecf406348 100644 --- a/doc/src/content/experimental/c-api/limitations.md +++ b/doc/src/content/experimental/c-api/from-cxx/_index.md @@ -1,7 +1,7 @@ +++ -title = "Limitations" +title = "Calling C++ from C" description = "" -weight = 10 +weight = 20 +++ C++ has excellent two-way compatibility with the C ABI, but there are some diff --git a/doc/src/content/experimental/c-api/example.md b/doc/src/content/experimental/c-api/from-cxx/example.md similarity index 100% rename from doc/src/content/experimental/c-api/example.md rename to doc/src/content/experimental/c-api/from-cxx/example.md diff --git a/doc/src/content/experimental/c-api/example2.md b/doc/src/content/experimental/c-api/from-cxx/example2.md similarity index 100% rename from doc/src/content/experimental/c-api/example2.md rename to doc/src/content/experimental/c-api/from-cxx/example2.md diff --git a/doc/src/content/experimental/c-api/reference.md b/doc/src/content/experimental/c-api/reference.md deleted file mode 100644 index fa3ff5ef444..00000000000 --- a/doc/src/content/experimental/c-api/reference.md +++ /dev/null @@ -1,98 +0,0 @@ -+++ -title = "C Macro API Reference" -weight = 50 -+++ - -The C macro API header `` consists of these macros: - -
-
CXX_DECLARE_RESULT(ident, T, E) -
Declares to C a basic_result type uniquely -identified by ident. T is available at the -member variable .value, and E is available -at the member variable .error. - -
CXX_RESULT(ident) -
A reference to a previously declared result type with -unique ident. - -
CXX_RESULT_HAS_VALUE(r) -
Evaluates to 1 (true) if the input result has a value. - -
CXX_RESULT_HAS_ERROR(r) -
Evaluates to 1 (true) if the input result has an error. - -
CXX_RESULT_ERROR_IS_ERRNO(r) -
Evaluates to 1 (true) if the input result's error value -is a code in the POSIX errno domain. -
- -The above let you work, somewhat awkwardly, with any C-compatible -`basic_result`. `basic_result` is trivially copyable and -standard layout if its `T` and `E` are both so, and it has the C layout: - -```c++ -struct cxx_result_##ident -{ - union - { - T value; - E error; - }; - unsigned flags; -}; -``` - -### `` support - -Because erased status codes are not trivially copyable and -therefore do not have union based storage, we have separate C macros -for results whose `E` is an erased status code: - -
-
CXX_DECLARE_STATUS_CODE(ident, value_type) -
Declares to C a status code type with domain value_type -available at the member variable .value. The ident -must be any identifier fragment unique in this translation unit. It is -used to uniquely identify this status code type in other macros. - -
CXX_STATUS_CODE(ident) -
A reference to a previously declared status code type with unique -ident. - -
CXX_DECLARE_RESULT_STATUS_CODE(ident, T, E) -
Declares to C a basic_result type uniquely -identified by ident. T is available at the -member variable .value, and E is available -at the member variable .error. - -
CXX_RESULT_STATUS_CODE(ident) -
A reference to a previously declared result type with -unique ident. -
- -There is a high likelihood that C++ functions regularly called by C -code will return their failures either in erased `system_code` -or in `posix_code` (i.e. `errno` code domain). Via querying the -returned value using `CXX_RESULT_ERROR_IS_ERRNO(r)`, one can determine -if the returned code is in the `errno` code domain, and thus can be -fed to `strerror()` and so on. Therefore there are -convenience macro APIs for those particular use cases. - -
-
CXX_DECLARE_RESULT_ERRNO(ident, T) -
Declares to C a basic_result<T, posix_code> -type uniquely identified by ident. - -
CXX_RESULT_ERRNO(ident) -
A reference to a previously declared basic_result<T, posix_code> -type with unique ident. - -
CXX_DECLARE_RESULT_SYSTEM(ident, T) -
Declares to C a basic_result<T, system_code> -type uniquely identified by ident. - -
CXX_RESULT_SYSTEM(ident) -
A reference to a previously declared basic_result<T, system_code> -type with unique ident. -
diff --git a/doc/src/content/experimental/outcome.md b/doc/src/content/experimental/outcome.md index e35a2277e36..2000d51bdfc 100644 --- a/doc/src/content/experimental/outcome.md +++ b/doc/src/content/experimental/outcome.md @@ -1,6 +1,6 @@ +++ title = "Tying it all together" -weight = 80 +weight = 75 +++ Firstly let's alias a more convenient form of `status_result`: diff --git a/doc/src/content/experimental/worked-example-long/_index.md b/doc/src/content/experimental/worked-example-long/_index.md new file mode 100644 index 00000000000..aa18f0d4d4f --- /dev/null +++ b/doc/src/content/experimental/worked-example-long/_index.md @@ -0,0 +1,36 @@ ++++ +title = "Worked example: Custom domain (the long way)" +weight = 85 ++++ + +Here follows a longer worked example of use of Experimental Outcome. It presents +the same sample program I sent to the San Diego 2018 WG21 standards meeting +after I was asked by the committee to demonstrate how P1095 implements P0709 +in a working code example they could study and discuss. + +We will walk through this worked example, step by step, explaining how each +part works in detail. This will help you implement your own code based on +Experimental Outcome. + +Most users will not need this level of customisation, and for them the preceding +[quick and easy approach]({{% relref "../worked-example" %}}) will be much easier. + +You may find it useful to open now in a separate browser tab the reference API +documentation for proposed `` at https://ned14.github.io/status-code/ +(scroll half way down). The references in the comments to P1028 are to +[P1028 *SG14 status_code and standard error object for P0709 Zero-overhead +deterministic exceptions*](http://wg21.link/P1028), which is the WG21 proposal +paper for potential ``. + +### Goal of this section + +We are going to define a simple custom code domain which defines that +the status code's payload will consist of a POSIX error code, and the +`__FILE__` and `__LINE__` where the failure occurred. This custom status +code will have an implicit conversion to type erased `error` defined, which dynamically +allocates memory for the original status code, and outputs an `error` +which manages that dynamic allocation, indirecting all queries etc +to the erased custom status code type such that the `error` instance +quacks as if just like the original. This demonstrates that `error` could +just as equally convey a `std::exception_ptr`, for example, or indeed +manage the lifetime of any pointer. diff --git a/doc/src/content/experimental/worked-example/constructor.md b/doc/src/content/experimental/worked-example-long/constructor.md similarity index 100% rename from doc/src/content/experimental/worked-example/constructor.md rename to doc/src/content/experimental/worked-example-long/constructor.md diff --git a/doc/src/content/experimental/worked-example/implicit_conversion.md b/doc/src/content/experimental/worked-example-long/implicit_conversion.md similarity index 86% rename from doc/src/content/experimental/worked-example/implicit_conversion.md rename to doc/src/content/experimental/worked-example-long/implicit_conversion.md index 5c65c883099..1a516d42c9b 100644 --- a/doc/src/content/experimental/worked-example/implicit_conversion.md +++ b/doc/src/content/experimental/worked-example-long/implicit_conversion.md @@ -3,7 +3,7 @@ title = "Implicit conversion" weight = 70 +++ -Back in [The payload]({{< relref "/experimental/worked-example/value_type" >}}), we +Back in [The payload]({{< relref "value_type" >}}), we mentioned that there was no default implicit conversion of `file_io_error` (`status_code<_file_io_error_domain>`) to `error`, as `error` is too small to hold `_file_io_error_domain::value_type`. diff --git a/doc/src/content/experimental/worked-example/message.md b/doc/src/content/experimental/worked-example-long/message.md similarity index 100% rename from doc/src/content/experimental/worked-example/message.md rename to doc/src/content/experimental/worked-example-long/message.md diff --git a/doc/src/content/experimental/worked-example/preamble.md b/doc/src/content/experimental/worked-example-long/preamble.md similarity index 100% rename from doc/src/content/experimental/worked-example/preamble.md rename to doc/src/content/experimental/worked-example-long/preamble.md diff --git a/doc/src/content/experimental/worked-example/source.md b/doc/src/content/experimental/worked-example-long/source.md similarity index 81% rename from doc/src/content/experimental/worked-example/source.md rename to doc/src/content/experimental/worked-example-long/source.md index 20a56f3f627..815e8f94328 100644 --- a/doc/src/content/experimental/worked-example/source.md +++ b/doc/src/content/experimental/worked-example-long/source.md @@ -3,7 +3,7 @@ title = "Constexpr domain source" weight = 60 +++ -Back in [The constructor]({{< relref "/experimental/worked-example/constructor" >}}), we +Back in [The constructor]({{< relref "constructor" >}}), we declared but did not implement a `.get()` function which returns a constexpr static instance of the domain. We implement this now: diff --git a/doc/src/content/experimental/worked-example/string_ref.md b/doc/src/content/experimental/worked-example-long/string_ref.md similarity index 100% rename from doc/src/content/experimental/worked-example/string_ref.md rename to doc/src/content/experimental/worked-example-long/string_ref.md diff --git a/doc/src/content/experimental/worked-example/value_type.md b/doc/src/content/experimental/worked-example-long/value_type.md similarity index 100% rename from doc/src/content/experimental/worked-example/value_type.md rename to doc/src/content/experimental/worked-example-long/value_type.md diff --git a/doc/src/content/experimental/worked-example/_index.md b/doc/src/content/experimental/worked-example/_index.md index ac7dd5e0bad..aebbe54ec3b 100644 --- a/doc/src/content/experimental/worked-example/_index.md +++ b/doc/src/content/experimental/worked-example/_index.md @@ -1,33 +1,30 @@ +++ -title = "Worked example: Custom domain" +title = "Worked example: Custom domain (the short way)" weight = 80 +++ -Here follows a worked example of use of Experimental Outcome. It presents -the same sample program I sent to the San Diego 2018 WG21 standards meeting -after I was asked by the committee to demonstrate how P1095 implements P0709 -in a working code example they could study and discuss. +The section after this one will take the long way through defining a custom domain +which sometimes is necessary if you have particularly bespoke requirements. +If however you just want to wrap a custom `enum` type of yours into its +own custom code domain, the boilerplate can be automated away +by filling in a few simple fields like this: -We will walk through this worked example, step by step, explaining how each -part works in detail. This will help you implement your own code based on -Experimental Outcome. +{{% snippet "quick_status_code_from_enum.cpp" "preamble" %}} -You may find it useful to open now in a separate browser tab the reference API -documentation for proposed `` at https://ned14.github.io/status-code/ -(scroll half way down). The references in the comments to P1028 are to -[P1028 *SG14 status_code and standard error object for P0709 Zero-overhead -deterministic exceptions*](http://wg21.link/P1028), which is the WG21 proposal -paper for potential ``. +Here we supply the bare minimum requirements for a status code domain: -### Goal of this section - -We are going to define a simple custom code domain which defines that -the status code's payload will consist of a POSIX error code, and the -`__FILE__` and `__LINE__` where the failure occurred. This custom status -code will have an implicit conversion to type erased `error` defined, which dynamically -allocates memory for the original status code, and outputs an `error` -which manages that dynamic allocation, indirecting all queries etc -to the erased custom status code type such that the `error` instance -quacks as if just like the original. This demonstrates that `error` could -just as equally convey a `std::exception_ptr`, for example, or indeed -manage the lifetime of any pointer. +1. The name in text, so it can be printed. +2. The unique UUID to identify when multiple copies of the domain are the same. +PLEASE use https://www.random.org/cgi-bin/randbyte?nbytes=16&format=h to generate +this, do not twiddle a few bits. +3. For each enum value, its printable text and a sequence of `errc::` enumerations +which you think it is semantically equivalent to i.e. its mapping onto `generic_code` +which is how status code defines the common mapping between status codes. If you later compare the +status code to one of those values (or to another status code which also provides +a mapping), if the generic codes are equivalent then the comparison will return true. +This means code like `if(sc == errc::no_such_file_or_directory) ...` would match +all custom error codes which mean 'something was not found'. +4. OPTIONAL: if you would like the custom status code type generated by this +to have additional member functions or additional payload, you can define a mixin +here to inject either data or functions or both. If you omit this, nothing gets +injected. \ No newline at end of file diff --git a/doc/src/content/experimental/worked-example/implicit-construction.md b/doc/src/content/experimental/worked-example/implicit-construction.md new file mode 100644 index 00000000000..ce089956305 --- /dev/null +++ b/doc/src/content/experimental/worked-example/implicit-construction.md @@ -0,0 +1,13 @@ ++++ +title = "Implicit construction" +weight = 10 ++++ + +The preceding code had the compiler stamp out a custom status code domain +for a user supplied `enum`. You now get the following types: + +{{% snippet "quick_status_code_from_enum.cpp" "implicit_construction" %}} + +As you can see, this is less work than [plugging your custom enum +into `std::error_code`]({{% relref "/motivation/plug_error_code" %}}). +It also has C compatibility, and generates better codegen. diff --git a/doc/src/snippets/c_api.c b/doc/src/snippets/c_api.c index 29f631c7653..de7fc2bd9ef 100644 --- a/doc/src/snippets/c_api.c +++ b/doc/src/snippets/c_api.c @@ -37,7 +37,7 @@ Distributed under the Boost Software License, Version 1.0. // // The first parameter is some unique identifier for this type which will be used // whenever we reference this type in the future. -CXX_DECLARE_RESULT_SYSTEM(to_string_rettype, size_t); +CXX_DECLARE_RESULT_SYSTEM(to_string_rettype, size_t) // Tell C about our extern C++ function `to_string()` extern CXX_RESULT_SYSTEM(to_string_rettype) _Z9to_stringPcmi(char *buffer, size_t bufferlen, int v); diff --git a/doc/src/snippets/c_api2.cpp b/doc/src/snippets/c_api2.cpp new file mode 100644 index 00000000000..9598ee60812 --- /dev/null +++ b/doc/src/snippets/c_api2.cpp @@ -0,0 +1,101 @@ +/* Example of Outcome used with C APIs +(C) 2024 Niall Douglas (4 commits) + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +// This header in Experimental Outcome is pure C, it provides a suite of C helper macros +#include "../../../include/outcome/experimental/result.h" + +#include +#include +#include +#include + +//! [preamble] +// Declare to C a Result with a happy value of intptr_t +CXX_DECLARE_RESULT_SYSTEM(result_int, intptr_t) + +// Save oneself typing out CXX_RESULT_SYSTEM(result_int) all the time +typedef CXX_RESULT_SYSTEM(result_int) result; + +// Our custom C enum +enum c_enum +{ + c_enum_not_found, + c_enum_bad_argument +}; + +// Make a custom status code domain for this C enum +CXX_DECLARE_RESULT_SYSTEM_FROM_ENUM(result_int, // The C Result type declared above + c_enum, // The C enum we wish to wrap + "{74ceb994-7622-3a21-07f0-b016aa705585}", // Unique UUID for this domain + // Mappings of C enum values to textual description and semantic equivalances to generic codes + {c_enum::c_enum_not_found, "item not found", {errc::no_such_file_or_directory}}, + {c_enum::c_enum_bad_argument, "invoked wrong", {errc::invalid_argument}}) + +// Make helper macros +#define SUCCESS(v) CXX_MAKE_RESULT_SYSTEM_SUCCESS(result_int, (v)) +#define FAILURE(v) CXX_MAKE_RESULT_SYSTEM_FROM_ENUM(result_int, c_enum, (v)) +//! [preamble] + +//! [using] +result positive_only(int x) +{ + if(x < 0) + { + return FAILURE(c_enum_bad_argument); + } + return SUCCESS(x); +} + +bool test(int x) +{ + result r = positive_only(x); + if(CXX_RESULT_HAS_ERROR(r)) + { + if(outcome_status_code_equal_generic(&r.error, EINVAL)) + { + fprintf(stderr, "Positive numbers only!\n"); + return false; + } + } + return true; +} +//! [using] + +//! [try] +result test2(int x) +{ + CXX_RESULT_SYSTEM_TRY(int v, // what to set to value if successful + fprintf(stderr, "Positive numbers only!\n"), // what cleanup to run if unsuccessful + positive_only(x)); + return SUCCESS(v + 1); +} +//! [try] + +int main(void) +{ + if(!test(0)) + abort(); + if(test(-1)) + abort(); + return 0; +} diff --git a/doc/src/snippets/intro_c_example.cpp b/doc/src/snippets/intro_c_example.cpp new file mode 100644 index 00000000000..d8d67429c18 --- /dev/null +++ b/doc/src/snippets/intro_c_example.cpp @@ -0,0 +1,72 @@ +/* Example of Outcome +(C) 2024 Niall Douglas + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +#include "../../../include/outcome/experimental/result.h" + +#include + +void use_string(const char *foo) +{ + (void) foo; +} + +//! [signature] +CXX_DECLARE_RESULT_SYSTEM(result_string, const char *) + +CXX_RESULT_SYSTEM(result_string) data_from_file(const char *path); +//! [signature] + +int main() +{ + //! [inspect] + CXX_RESULT_SYSTEM(result_string) rslt = data_from_file("config.cfg"); + if(CXX_RESULT_HAS_VALUE(rslt)) + use_string(rslt.value); // returns string + else + fprintf(stderr, "%s\n", outcome_status_code_message(&rslt.error)); + //! [inspect] +} + +//! [implementation] +CXX_DECLARE_RESULT_SYSTEM(result_int, int) + +CXX_RESULT_SYSTEM(result_int) process(const char *content); + +CXX_RESULT_SYSTEM(result_int) int_from_file(const char *path) +{ + CXX_RESULT_SYSTEM_TRY(const char *str, result_int, /* cleanup on fail */, data_from_file(path)); + // if control gets here data_from_file() has succeeded + return process(str); // decltype(str) == string +} +//! [implementation] + +CXX_RESULT_SYSTEM(result_int) process(const char *foo) +{ + (void) foo; + return outcome_make_result_system_result_int_success(1); +} + +CXX_RESULT_SYSTEM(result_string) data_from_file(const char *path) +{ + (void) path; + return outcome_make_result_system_result_string_success(""); +} diff --git a/doc/src/snippets/quick_status_code_from_enum.cpp b/doc/src/snippets/quick_status_code_from_enum.cpp new file mode 100644 index 00000000000..dc8bc812a6c --- /dev/null +++ b/doc/src/snippets/quick_status_code_from_enum.cpp @@ -0,0 +1,107 @@ +/* Example use of std::error implicit conversion +(C) 2024 Niall Douglas (5 commits) +File Created: Sept 2018 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +#include "../../../include/outcome/experimental/status-code/include/status-code/quick_status_code_from_enum.hpp" +#include "../../../include/outcome/experimental/status_result.hpp" +#include "outcome/experimental/status-code/include/status-code/config.hpp" + + +//! [preamble] +// My custom enum type +enum class custom_failure +{ + not_found, + bad_argument +}; + +// Tell `status_code` to stamp out a custom code domain for this enum type +SYSTEM_ERROR2_NAMESPACE_BEGIN +template <> struct quick_status_code_from_enum : quick_status_code_from_enum_defaults +{ + // Text name of the enum + static constexpr const auto domain_name = "My custom failure"; + // Unique UUID for the enum. PLEASE use https://www.random.org/cgi-bin/randbyte?nbytes=16&format=h + static constexpr const auto domain_uuid = "{be201f65-3962-dd0e-1266-a72e63776a42}"; + // Map of each enum value to its text string, and list of semantically equivalent errc's + static const std::initializer_list &value_mappings() + { + static const std::initializer_list v = { + // Format is: { enum value, "string representation", { list of errc mappings ... } } + {custom_failure::not_found, "item not found", {errc::no_such_file_or_directory}}, // + {custom_failure::bad_argument, "invoked wrong", {errc::invalid_argument}}, // + }; + return v; + } + // Completely optional definition of mixin for the status code synthesised from `Enum`. It can be omitted. + template struct mixin : Base + { + using Base::Base; + constexpr int custom_method() const { return 42; } + }; +}; +SYSTEM_ERROR2_NAMESPACE_END +//! [preamble] + +//! [implicit_construction] +// This is the status code generated for your custom enum type. It will implicitly construct from +// values of enum custom_failure. +using custom_failure_code = SYSTEM_ERROR2_NAMESPACE::quick_status_code_from_enum_code; + +namespace outcome_e = OUTCOME_V2_NAMESPACE::experimental; + +// You don't usually need to use the status code type explicitly, because this "just works": +outcome_e::status_result positive_only(int x) +{ + if(x < 0) + { + // Outcome's result sees that status_code will implicitly construct from this enum, + // and it returns an errored result + return custom_failure::bad_argument; + } + return x; +} + +// Semantic comparisons work +bool test(int x) +{ + if(auto r = positive_only(x); !r) + { + if(r.error() == outcome_e::errc::invalid_argument) + { + std::cerr << "Positive numbers only!" << std::endl; + return false; + } + } + return true; +} +//! [implicit_construction] + +int main(void) +{ + if(!test(0)) + abort(); + if(test(-1)) + abort(); + return 0; +} diff --git a/include/outcome/detail/revision.hpp b/include/outcome/detail/revision.hpp index 470f80e2d8f..e5fccf36199 100644 --- a/include/outcome/detail/revision.hpp +++ b/include/outcome/detail/revision.hpp @@ -22,6 +22,6 @@ Distributed under the Boost Software License, Version 1.0. */ // Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time -#define OUTCOME_PREVIOUS_COMMIT_REF f5a45b6909e732174fe98e59548914bcaa67d847 -#define OUTCOME_PREVIOUS_COMMIT_DATE "2024-06-21 11:40:22 +00:00" -#define OUTCOME_PREVIOUS_COMMIT_UNIQUE f5a45b69 +#define OUTCOME_PREVIOUS_COMMIT_REF 5bcd0f32f28659a0eab62f1d643d878c2220788d +#define OUTCOME_PREVIOUS_COMMIT_DATE "2024-07-15 17:46:44 +00:00" +#define OUTCOME_PREVIOUS_COMMIT_UNIQUE 5bcd0f32 diff --git a/include/outcome/detail/try.h b/include/outcome/detail/try.h new file mode 100644 index 00000000000..6960620c8f1 --- /dev/null +++ b/include/outcome/detail/try.h @@ -0,0 +1,98 @@ +/* Try operation macros +(C) 2017-2024 Niall Douglas (20 commits) +File Created: July 2017 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef OUTCOME_TRY_H +#define OUTCOME_TRY_H + +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wparentheses" +#endif + +#define OUTCOME_TRY_GLUE2(x, y) x##y +#define OUTCOME_TRY_GLUE(x, y) OUTCOME_TRY_GLUE2(x, y) +#define OUTCOME_TRY_UNIQUE_NAME OUTCOME_TRY_GLUE(_outcome_try_unique_name_temporary, __COUNTER__) + +#define OUTCOME_TRY_RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, count, ...) count +#define OUTCOME_TRY_EXPAND_ARGS(args) OUTCOME_TRY_RETURN_ARG_COUNT args +#define OUTCOME_TRY_COUNT_ARGS_MAX8(...) OUTCOME_TRY_EXPAND_ARGS((__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)) +#define OUTCOME_TRY_OVERLOAD_MACRO2(name, count) name##count +#define OUTCOME_TRY_OVERLOAD_MACRO1(name, count) OUTCOME_TRY_OVERLOAD_MACRO2(name, count) +#define OUTCOME_TRY_OVERLOAD_MACRO(name, count) OUTCOME_TRY_OVERLOAD_MACRO1(name, count) +#define OUTCOME_TRY_OVERLOAD_GLUE(x, y) x y +#define OUTCOME_TRY_CALL_OVERLOAD(name, ...) \ + OUTCOME_TRY_OVERLOAD_GLUE(OUTCOME_TRY_OVERLOAD_MACRO(name, OUTCOME_TRY_COUNT_ARGS_MAX8(__VA_ARGS__)), (__VA_ARGS__)) + +#define _OUTCOME_TRY_RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, count, ...) count +#define _OUTCOME_TRY_EXPAND_ARGS(args) _OUTCOME_TRY_RETURN_ARG_COUNT args +#define _OUTCOME_TRY_COUNT_ARGS_MAX8(...) _OUTCOME_TRY_EXPAND_ARGS((__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)) +#define _OUTCOME_TRY_OVERLOAD_MACRO2(name, count) name##count +#define _OUTCOME_TRY_OVERLOAD_MACRO1(name, count) _OUTCOME_TRY_OVERLOAD_MACRO2(name, count) +#define _OUTCOME_TRY_OVERLOAD_MACRO(name, count) _OUTCOME_TRY_OVERLOAD_MACRO1(name, count) +#define _OUTCOME_TRY_OVERLOAD_GLUE(x, y) x y +#define _OUTCOME_TRY_CALL_OVERLOAD(name, ...) \ + _OUTCOME_TRY_OVERLOAD_GLUE(_OUTCOME_TRY_OVERLOAD_MACRO(name, _OUTCOME_TRY_COUNT_ARGS_MAX8(__VA_ARGS__)), (__VA_ARGS__)) + +#if !defined(OUTCOME_TRY_LIKELY_IF) && defined(__has_cpp_attribute) +#if __has_cpp_attribute(likely) +#define OUTCOME_TRY_LIKELY_IF(...) if(__VA_ARGS__) [[likely]] +#endif +#endif +#ifndef OUTCOME_TRY_LIKELY_IF +#if defined(__clang__) || defined(__GNUC__) +#define OUTCOME_TRY_LIKELY_IF(...) if(__builtin_expect(!!(__VA_ARGS__), 1)) +#else +#define OUTCOME_TRY_LIKELY_IF(...) if(__VA_ARGS__) +#endif +#endif + +#ifdef __cplusplus +#define OUTCOME_TRYV2_UNIQUE_STORAGE_AUTO(...) auto +#else +#define OUTCOME_TRYV2_UNIQUE_STORAGE_AUTO(...) __typeof__(__VA_ARGS__) +#endif + +#define OUTCOME_TRYV2_UNIQUE_STORAGE_UNPACK(...) __VA_ARGS__ +#define OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE3(unique, ...) OUTCOME_TRYV2_UNIQUE_STORAGE_AUTO(__VA_ARGS__) unique = (__VA_ARGS__) +#define OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE2(x) x +#define OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE(unique, x, ...) OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE2(OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE3(unique, __VA_ARGS__)) +#define OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED3(unique, x, y, ...) x unique = (__VA_ARGS__) +#define OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED2(x) x +#define OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED(unique, ...) \ + OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED2(OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED3(unique, __VA_ARGS__)) +#define OUTCOME_TRYV2_UNIQUE_STORAGE1(...) OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE +#define OUTCOME_TRYV2_UNIQUE_STORAGE2(...) OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED +#define OUTCOME_TRYV2_UNIQUE_STORAGE(unique, spec, ...) \ + _OUTCOME_TRY_CALL_OVERLOAD(OUTCOME_TRYV2_UNIQUE_STORAGE, OUTCOME_TRYV2_UNIQUE_STORAGE_UNPACK spec) \ + (unique, OUTCOME_TRYV2_UNIQUE_STORAGE_UNPACK spec, __VA_ARGS__) + +#define OUTCOME_TRY2_VAR_SECOND2(x, var) var +#define OUTCOME_TRY2_VAR_SECOND3(x, y, ...) x y +#define OUTCOME_TRY2_VAR(spec) _OUTCOME_TRY_CALL_OVERLOAD(OUTCOME_TRY2_VAR_SECOND, OUTCOME_TRYV2_UNIQUE_STORAGE_UNPACK spec, spec) + +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic pop +#endif + +#endif diff --git a/include/outcome/experimental/result.h b/include/outcome/experimental/result.h index 5d826190ee6..fd45369dec8 100644 --- a/include/outcome/experimental/result.h +++ b/include/outcome/experimental/result.h @@ -25,8 +25,17 @@ Distributed under the Boost Software License, Version 1.0. #ifndef OUTCOME_EXPERIMENTAL_RESULT_H #define OUTCOME_EXPERIMENTAL_RESULT_H +#include // for size_t #include // for intptr_t +#include "../detail/try.h" + +#if __STDC_VERSION__ >= 199900L +#define OUTCOME_C_INLINE inline +#else +#define OUTCOME_C_INLINE +#endif + #include "../outcome_gdb.h" #ifdef __cplusplus @@ -54,7 +63,6 @@ extern "C" #define CXX_RESULT_ERROR_IS_ERRNO(r) (((r).flags & (1U << 4U)) == (1U << 4U)) - /***************************** support ******************************/ #define CXX_DECLARE_STATUS_CODE(ident, value_type) \ @@ -66,16 +74,91 @@ extern "C" #define CXX_STATUS_CODE(ident) struct cxx_status_code_##ident + extern void outcome_make_result_status_code_success(void *out, size_t bytes, size_t offset, const void *toset, size_t tosetbytes); + extern void outcome_make_result_status_code_failure_posix(void *out, size_t bytes, size_t offset, int errcode); + extern void outcome_make_result_status_code_failure_system(void *out, size_t bytes, size_t offset, intptr_t errcode); + extern int outcome_status_code_equal(const void *a, const void *b); + extern int outcome_status_code_equal_generic(const void *a, int errcode); + extern const char *outcome_status_code_message(const void *a); + + +#ifdef __cplusplus +#define CXX_DECLARE_RESULT_STATUS_CODE_CXX(ident, R, S) \ + static_assert(std::is_trivially_copyable::value || OUTCOME_V2_NAMESPACE::trait::is_move_bitcopying::value, \ + "R must be trivially copyable or move bitcopying to be used in a C Result"); \ + static_assert(std::is_trivially_copyable::value || OUTCOME_V2_NAMESPACE::trait::is_move_bitcopying::value, \ + "S must be trivially copyable or move bitcopying to be used in a C Result"); \ + inline OUTCOME_V2_NAMESPACE::experimental::status_result to_result(const cxx_result_status_code_##ident &v) \ + { \ + union type_punner_t \ + { \ + cxx_result_status_code_##ident c; \ + OUTCOME_V2_NAMESPACE::experimental::status_result cpp; \ + \ + type_punner_t() \ + : c{} \ + { \ + } \ + ~type_punner_t() {} \ + } pun; \ + \ + pun.c = v; \ + return std::move(pun.cpp); \ + } \ + inline cxx_result_status_code_##ident to_##ident(OUTCOME_V2_NAMESPACE::experimental::status_result v) \ + { \ + union type_punner_t \ + { \ + OUTCOME_V2_NAMESPACE::experimental::status_result cpp; \ + cxx_result_status_code_##ident c; \ + \ + type_punner_t(OUTCOME_V2_NAMESPACE::experimental::status_result v) \ + : cpp(std::move(v)) \ + { \ + } \ + ~type_punner_t() {} \ + } pun{std::move(v)}; \ + \ + return pun.c; \ + } +#else +#define CXX_DECLARE_RESULT_STATUS_CODE_CXX(ident, R, S) +#endif + #define CXX_DECLARE_RESULT_STATUS_CODE(ident, R, S) \ struct cxx_result_status_code_##ident \ { \ R value; \ unsigned flags; \ S error; \ - } + }; \ + static OUTCOME_C_INLINE struct cxx_result_status_code_##ident outcome_make_result_##ident##_success(R value) \ + { \ + struct cxx_result_status_code_##ident ret; \ + outcome_make_result_status_code_success((void *) &ret, sizeof(ret), offsetof(struct cxx_result_status_code_##ident, flags), (const void *) &value, \ + sizeof(value)); \ + return ret; \ + } \ + static OUTCOME_C_INLINE struct cxx_result_status_code_##ident outcome_make_result_##ident##_failure_posix(int errcode) \ + { \ + struct cxx_result_status_code_##ident ret; \ + outcome_make_result_status_code_failure_posix((void *) &ret, sizeof(ret), offsetof(struct cxx_result_status_code_##ident, flags), errcode); \ + return ret; \ + } \ + static OUTCOME_C_INLINE struct cxx_result_status_code_##ident outcome_make_result_##ident##_failure_system(intptr_t errcode) \ + { \ + struct cxx_result_status_code_##ident ret; \ + outcome_make_result_status_code_failure_system((void *) &ret, sizeof(ret), offsetof(struct cxx_result_status_code_##ident, flags), errcode); \ + return ret; \ + } \ + CXX_DECLARE_RESULT_STATUS_CODE_CXX(ident, R, S) #define CXX_RESULT_STATUS_CODE(ident) struct cxx_result_status_code_##ident +#define CXX_MAKE_RESULT_STATUS_CODE_SUCCESS(ident, ...) outcome_make_result_##ident##_success(__VA_ARGS__) +#define CXX_MAKE_RESULT_STATUS_CODE_FAILURE_POSIX(ident, ...) outcome_make_result_##ident##_failure_posix(__VA_ARGS__) +#define CXX_MAKE_RESULT_STATUS_CODE_FAILURE_SYSTEM(ident, ...) outcome_make_result_##ident##_failure_system(__VA_ARGS__) + struct cxx_status_code_posix { @@ -93,8 +176,235 @@ extern "C" #define CXX_DECLARE_RESULT_SYSTEM(ident, R) CXX_DECLARE_RESULT_STATUS_CODE(system_##ident, R, struct cxx_status_code_system) #define CXX_RESULT_SYSTEM(ident) CXX_RESULT_STATUS_CODE(system_##ident) -#ifdef __cplusplus +#define CXX_MAKE_RESULT_SYSTEM_SUCCESS(ident, ...) CXX_MAKE_RESULT_STATUS_CODE_SUCCESS(system_##ident, __VA_ARGS__) +#define CXX_MAKE_RESULT_SYSTEM_FAILURE_POSIX(ident, ...) CXX_MAKE_RESULT_STATUS_CODE_FAILURE_POSIX(system_##ident, __VA_ARGS__) +#define CXX_MAKE_RESULT_SYSTEM_FAILURE_SYSTEM(ident, ...) CXX_MAKE_RESULT_STATUS_CODE_FAILURE_SYSTEM(system_##ident, __VA_ARGS__) + +#define CXX_RESULT_SYSTEM_TRY_IMPLV(unique, retstmt, cleanup, spec, ...) \ + OUTCOME_TRYV2_UNIQUE_STORAGE(unique, spec, __VA_ARGS__); \ + OUTCOME_TRY_LIKELY_IF(CXX_RESULT_HAS_VALUE(unique)); \ + else \ + { \ + retstmt; \ + } +#define CXX_RESULT_SYSTEM_TRY_IMPLA(unique, retstmt, cleanup, var, ...) \ + CXX_RESULT_SYSTEM_TRY_IMPLV(unique, retstmt, cleanup, var, __VA_ARGS__) \ + OUTCOME_TRY2_VAR(var) = unique.value + +#define CXX_RESULT_SYSTEM_TRY_IMPL_RETURN(unique, ident) \ + CXX_RESULT_SYSTEM(ident) unique##_f; \ + unique##_f.flags = (unique).flags; \ + unique##_f.error = (unique).error; \ + return unique##_f + +#define CXX_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP_AND_NEW_RETURN_TYPE(unique, spec, ident, cleanup, ...) \ + CXX_RESULT_SYSTEM_TRY_IMPLA(unique, CXX_RESULT_SYSTEM_TRY_IMPL_RETURN(unique, ident), cleanup, spec, __VA_ARGS__) +#define CXX_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP(unique, spec, cleanup, ...) CXX_RESULT_SYSTEM_TRY_IMPLA(unique, return unique, cleanup, spec, __VA_ARGS__) +#define CXX_RESULT_SYSTEM_TRY_WITH_CLEANUP(unique, cleanup, ...) CXX_RESULT_SYSTEM_TRY_IMPLV(unique, return unique, cleanup, deduce, __VA_ARGS__) +#define CXX_RESULT_SYSTEM_TRY_SAME_RETURN_TYPE(unique, ...) CXX_RESULT_SYSTEM_TRY_IMPLV(unique, return unique, , deduce, __VA_ARGS__) + +#define CXX_RESULT_SYSTEM_TRY_INVOKE_TRY8(a, b, c, d, e, f, g, h) \ + CXX_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP_AND_NEW_RETURN_TYPE(OUTCOME_TRY_UNIQUE_NAME, a, b, c, d, e, f, g, h) +#define CXX_RESULT_SYSTEM_TRY_INVOKE_TRY7(a, b, c, d, e, f, g) \ + CXX_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP_AND_NEW_RETURN_TYPE(OUTCOME_TRY_UNIQUE_NAME, a, b, c, d, e, f, g) +#define CXX_RESULT_SYSTEM_TRY_INVOKE_TRY6(a, b, c, d, e, f) \ + CXX_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP_AND_NEW_RETURN_TYPE(OUTCOME_TRY_UNIQUE_NAME, a, b, c, d, e, f) +#define CXX_RESULT_SYSTEM_TRY_INVOKE_TRY5(a, b, c, d, e) CXX_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP_AND_NEW_RETURN_TYPE(OUTCOME_TRY_UNIQUE_NAME, a, b, c, d, e) +#define CXX_RESULT_SYSTEM_TRY_INVOKE_TRY4(a, b, c, d) CXX_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP_AND_NEW_RETURN_TYPE(OUTCOME_TRY_UNIQUE_NAME, a, b, c, d) +#define CXX_RESULT_SYSTEM_TRY_INVOKE_TRY3(a, b, c) CXX_RESULT_SYSTEM_TRY_WITH_SPEC_AND_CLEANUP(OUTCOME_TRY_UNIQUE_NAME, a, b, c) +#define CXX_RESULT_SYSTEM_TRY_INVOKE_TRY2(a, b) CXX_RESULT_SYSTEM_TRY_WITH_CLEANUP(OUTCOME_TRY_UNIQUE_NAME, a, b) +#define CXX_RESULT_SYSTEM_TRY_INVOKE_TRY1(expr) CXX_RESULT_SYSTEM_TRY_SAME_RETURN_TYPE(OUTCOME_TRY_UNIQUE_NAME, expr) + +#define CXX_RESULT_SYSTEM_TRY(...) OUTCOME_TRY_CALL_OVERLOAD(CXX_RESULT_SYSTEM_TRY_INVOKE_TRY, __VA_ARGS__) + +#define CXX_MAKE_RESULT_SYSTEM_FROM_ENUM(ident, enum_name, ...) outcome_make_result_##ident##_failure_system_enum_##enum_name(__VA_ARGS__) +#ifndef __cplusplus +// Declares the function in C, needs to occur at least once in a C++ source file to get implemented +#define CXX_DECLARE_RESULT_SYSTEM_FROM_ENUM(ident, enum_name, uuid, ...) \ + extern struct cxx_result_status_code_system_##ident outcome_make_result_##ident##_failure_system_enum_##enum_name(enum enum_name v); +#else } + +#include "../config.hpp" +#include "status-code/include/status-code/config.hpp" +#include "status-code/include/status-code/system_code.hpp" +#include "status_result.hpp" + + +#include "status-code/include/status-code/posix_code.hpp" +#ifdef _WIN32 +#include "status-code/include/status-code/win32_code.hpp" +#endif + +#include +#include + +// You need to include this C header in at least one C++ source file to have these C helper functions be implemented +extern "C" inline void outcome_make_result_status_code_success(void *out, size_t bytes, size_t offset, const void *toset, size_t tosetbytes) +{ + union type_punner_t + { + OUTCOME_V2_NAMESPACE::experimental::status_result cpp; + struct cxx_status_code + { + intptr_t value; + unsigned flags; + cxx_status_code_system error; + } c; + + type_punner_t() + : cpp(0) + { + } + ~type_punner_t() {} + } pun; + static_assert(sizeof(pun.cpp) == sizeof(pun.c), ""); + static constexpr size_t punoffset = offsetof(type_punner_t::cxx_status_code, flags); + assert(bytes - tosetbytes >= sizeof(pun.cpp) - punoffset); + const size_t tocopy = std::min(bytes - tosetbytes, sizeof(pun.c) - punoffset); + memcpy(out, toset, tosetbytes); + memcpy((void *) ((char *) out + offset), (const void *) ((const char *) &pun.c + punoffset), tocopy); +} + +extern "C" inline void outcome_make_result_status_code_failure_posix(void *out, size_t bytes, size_t offset, int errcode) +{ + using value_type = OUTCOME_V2_NAMESPACE::experimental::posix_code::value_type; + union type_punner_t + { + OUTCOME_V2_NAMESPACE::experimental::status_result cpp; + struct cxx_status_code + { + intptr_t value; + unsigned flags; + cxx_status_code_system error; + } c; + + explicit type_punner_t(OUTCOME_V2_NAMESPACE::experimental::status_result res) + : cpp(std::move(res)) + { + } + ~type_punner_t() {} + } pun{OUTCOME_V2_NAMESPACE::experimental::posix_code(errcode)}; + static_assert(sizeof(pun.cpp) == sizeof(pun.c), ""); + static constexpr size_t punoffset = offsetof(type_punner_t::cxx_status_code, flags); + assert(bytes - offset >= sizeof(pun.cpp) - punoffset); + const size_t tocopy = std::min(bytes - offset, sizeof(pun.cpp) - punoffset); + memcpy(out, (void *) &pun.c, sizeof(value_type)); + memcpy((void *) ((char *) out + offset), (const void *) ((const char *) &pun.c + punoffset), tocopy); +} + +extern "C" inline void outcome_make_result_status_code_failure_system(void *out, size_t bytes, size_t offset, intptr_t errcode) +{ + using value_type = OUTCOME_V2_NAMESPACE::experimental::system_code::value_type; + union type_punner_t + { + OUTCOME_V2_NAMESPACE::experimental::status_result cpp; + struct cxx_status_code + { + intptr_t value; + unsigned flags; + cxx_status_code_system error; + } c; + + explicit type_punner_t(OUTCOME_V2_NAMESPACE::experimental::status_result res) + : cpp(std::move(res)) + { + } + ~type_punner_t() {} + } pun{ +#ifdef _WIN32 + OUTCOME_V2_NAMESPACE::experimental::win32_code(errcode) +#else + OUTCOME_V2_NAMESPACE::experimental::posix_code(errcode) +#endif + }; + static_assert(sizeof(pun.cpp) == sizeof(pun.c), ""); + static constexpr size_t punoffset = offsetof(type_punner_t::cxx_status_code, flags); + assert(bytes - offset >= sizeof(pun.cpp) - punoffset); + const size_t tocopy = std::min(bytes - offset, sizeof(pun.cpp) - punoffset); + memcpy(out, (void *) &pun.c, sizeof(value_type)); + memcpy((void *) ((char *) out + offset), (const void *) ((const char *) &pun.c + punoffset), tocopy); +} + +extern "C" inline int outcome_status_code_equal(const void *_a, const void *_b) +{ + const auto *a = (const SYSTEM_ERROR2_NAMESPACE::system_code *) _a; + const auto *b = (const SYSTEM_ERROR2_NAMESPACE::system_code *) _b; + return *a == *b; +} + +extern "C" inline int outcome_status_code_equal_generic(const void *_a, int errcode) +{ + const auto *a = (const SYSTEM_ERROR2_NAMESPACE::system_code *) _a; + return *a == (SYSTEM_ERROR2_NAMESPACE::errc) errcode; +} + +extern "C" inline const char *outcome_status_code_message(const void *_a) +{ + static thread_local SYSTEM_ERROR2_NAMESPACE::system_code::string_ref msg((const char *) nullptr, 0); + const auto *a = (const SYSTEM_ERROR2_NAMESPACE::system_code *) _a; + msg = a->message(); + return msg.c_str(); +} + +OUTCOME_V2_NAMESPACE_BEGIN +namespace experimental +{ + namespace detail + { + template inline RetType outcome_make_result_failure_system_enum(EnumType v) + { + using value_type = OUTCOME_V2_NAMESPACE::experimental::system_code::value_type; + union type_punner_t + { + OUTCOME_V2_NAMESPACE::experimental::status_result cpp; + struct cxx_status_code + { + intptr_t value; + unsigned flags; + cxx_status_code_system error; + } c; + + explicit type_punner_t(OUTCOME_V2_NAMESPACE::experimental::status_result res) + : cpp(std::move(res)) + { + } + ~type_punner_t() {} + } pun{OUTCOME_V2_NAMESPACE::experimental::quick_status_code_from_enum_code(v)}; + static constexpr size_t bytes = sizeof(RetType); + static constexpr size_t offset = offsetof(RetType, flags); + static constexpr size_t punoffset = offsetof(typename type_punner_t::cxx_status_code, flags); + assert(bytes - offset >= sizeof(pun.cpp) - punoffset); + const size_t tocopy = std::min(bytes - offset, sizeof(pun.cpp) - punoffset); + RetType ret; + memcpy(&ret, (void *) &pun.c, sizeof(value_type)); + memcpy((void *) ((char *) &ret + offset), (const void *) ((const char *) &pun.c + punoffset), tocopy); + return ret; + } + } +} +OUTCOME_V2_NAMESPACE_END + +// Unique UUID for the enum PLEASE use https://www.random.org/cgi-bin/randbyte?nbytes=16&format=h +// .. is sequence of {enum_name::value, "text description", {errc::equivalent, ...}}, +#define CXX_DECLARE_RESULT_SYSTEM_FROM_ENUM(ident, enum_name, uuid, ...) \ + SYSTEM_ERROR2_NAMESPACE_BEGIN \ + template <> struct quick_status_code_from_enum : quick_status_code_from_enum_defaults \ + { \ + static constexpr const auto domain_name = #enum_name; \ + static constexpr const auto domain_uuid = uuid; \ + static const std::initializer_list &value_mappings() \ + { \ + static const std::initializer_list v = {__VA_ARGS__}; \ + return v; \ + } \ + }; \ + SYSTEM_ERROR2_NAMESPACE_END \ + extern "C" inline struct cxx_result_status_code_system_##ident outcome_make_result_##ident##_failure_system_enum_##enum_name(enum enum_name v) \ + { \ + return OUTCOME_V2_NAMESPACE::experimental::detail::outcome_make_result_failure_system_enum(v); \ + } + #endif #endif diff --git a/include/outcome/try.hpp b/include/outcome/try.hpp index dc58a1a42f9..1a4b0296010 100644 --- a/include/outcome/try.hpp +++ b/include/outcome/try.hpp @@ -25,6 +25,7 @@ Distributed under the Boost Software License, Version 1.0. #ifndef OUTCOME_TRY_HPP #define OUTCOME_TRY_HPP +#include "detail/try.h" #include "success_failure.hpp" OUTCOME_V2_NAMESPACE_BEGIN @@ -169,58 +170,6 @@ OUTCOME_V2_NAMESPACE_END #pragma GCC diagnostic ignored "-Wparentheses" #endif - -#define OUTCOME_TRY_GLUE2(x, y) x##y -#define OUTCOME_TRY_GLUE(x, y) OUTCOME_TRY_GLUE2(x, y) -#define OUTCOME_TRY_UNIQUE_NAME OUTCOME_TRY_GLUE(_outcome_try_unique_name_temporary, __COUNTER__) - -#define OUTCOME_TRY_RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, count, ...) count -#define OUTCOME_TRY_EXPAND_ARGS(args) OUTCOME_TRY_RETURN_ARG_COUNT args -#define OUTCOME_TRY_COUNT_ARGS_MAX8(...) OUTCOME_TRY_EXPAND_ARGS((__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)) -#define OUTCOME_TRY_OVERLOAD_MACRO2(name, count) name##count -#define OUTCOME_TRY_OVERLOAD_MACRO1(name, count) OUTCOME_TRY_OVERLOAD_MACRO2(name, count) -#define OUTCOME_TRY_OVERLOAD_MACRO(name, count) OUTCOME_TRY_OVERLOAD_MACRO1(name, count) -#define OUTCOME_TRY_OVERLOAD_GLUE(x, y) x y -#define OUTCOME_TRY_CALL_OVERLOAD(name, ...) \ - OUTCOME_TRY_OVERLOAD_GLUE(OUTCOME_TRY_OVERLOAD_MACRO(name, OUTCOME_TRY_COUNT_ARGS_MAX8(__VA_ARGS__)), (__VA_ARGS__)) - -#define _OUTCOME_TRY_RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, count, ...) count -#define _OUTCOME_TRY_EXPAND_ARGS(args) _OUTCOME_TRY_RETURN_ARG_COUNT args -#define _OUTCOME_TRY_COUNT_ARGS_MAX8(...) _OUTCOME_TRY_EXPAND_ARGS((__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)) -#define _OUTCOME_TRY_OVERLOAD_MACRO2(name, count) name##count -#define _OUTCOME_TRY_OVERLOAD_MACRO1(name, count) _OUTCOME_TRY_OVERLOAD_MACRO2(name, count) -#define _OUTCOME_TRY_OVERLOAD_MACRO(name, count) _OUTCOME_TRY_OVERLOAD_MACRO1(name, count) -#define _OUTCOME_TRY_OVERLOAD_GLUE(x, y) x y -#define _OUTCOME_TRY_CALL_OVERLOAD(name, ...) \ - _OUTCOME_TRY_OVERLOAD_GLUE(_OUTCOME_TRY_OVERLOAD_MACRO(name, _OUTCOME_TRY_COUNT_ARGS_MAX8(__VA_ARGS__)), (__VA_ARGS__)) - -#if !defined(OUTCOME_TRY_LIKELY_IF) && defined(__has_cpp_attribute) -#if __has_cpp_attribute(likely) -#define OUTCOME_TRY_LIKELY_IF(...) if(__VA_ARGS__) [[likely]] -#endif -#endif -#ifndef OUTCOME_TRY_LIKELY_IF -#if defined(__clang__) || defined(__GNUC__) -#define OUTCOME_TRY_LIKELY_IF(...) if(__builtin_expect(!!(__VA_ARGS__), true)) -#else -#define OUTCOME_TRY_LIKELY_IF(...) if(__VA_ARGS__) -#endif -#endif - -#define OUTCOME_TRYV2_UNIQUE_STORAGE_UNPACK(...) __VA_ARGS__ -#define OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE3(unique, ...) auto unique = (__VA_ARGS__) -#define OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE2(x) x -#define OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE(unique, x, ...) OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE2(OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE3(unique, __VA_ARGS__)) -#define OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED3(unique, x, y, ...) x unique = (__VA_ARGS__) -#define OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED2(x) x -#define OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED(unique, ...) \ - OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED2(OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED3(unique, __VA_ARGS__)) -#define OUTCOME_TRYV2_UNIQUE_STORAGE1(...) OUTCOME_TRYV2_UNIQUE_STORAGE_DEDUCE -#define OUTCOME_TRYV2_UNIQUE_STORAGE2(...) OUTCOME_TRYV2_UNIQUE_STORAGE_SPECIFIED -#define OUTCOME_TRYV2_UNIQUE_STORAGE(unique, spec, ...) \ - _OUTCOME_TRY_CALL_OVERLOAD(OUTCOME_TRYV2_UNIQUE_STORAGE, OUTCOME_TRYV2_UNIQUE_STORAGE_UNPACK spec) \ - (unique, OUTCOME_TRYV2_UNIQUE_STORAGE_UNPACK spec, __VA_ARGS__) - // Use if(!expr); else as some compilers assume else clauses are always unlikely #define OUTCOME_TRYV2_SUCCESS_LIKELY(unique, retstmt, spec, ...) \ OUTCOME_TRYV2_UNIQUE_STORAGE(unique, spec, __VA_ARGS__); \ @@ -238,9 +187,6 @@ OUTCOME_V2_NAMESPACE_END retstmt unique##_f; \ } -#define OUTCOME_TRY2_VAR_SECOND2(x, var) var -#define OUTCOME_TRY2_VAR_SECOND3(x, y, ...) x y -#define OUTCOME_TRY2_VAR(spec) _OUTCOME_TRY_CALL_OVERLOAD(OUTCOME_TRY2_VAR_SECOND, OUTCOME_TRYV2_UNIQUE_STORAGE_UNPACK spec, spec) #define OUTCOME_TRY2_SUCCESS_LIKELY(unique, retstmt, var, ...) \ OUTCOME_TRYV2_SUCCESS_LIKELY(unique, retstmt, var, __VA_ARGS__); \ OUTCOME_TRY2_VAR(var) = ::OUTCOME_V2_NAMESPACE::try_operation_extract_value(static_cast(unique)) diff --git a/single-header/outcome-basic.hpp b/single-header/outcome-basic.hpp index 199817ee910..a31f5475676 100644 --- a/single-header/outcome-basic.hpp +++ b/single-header/outcome-basic.hpp @@ -1019,9 +1019,9 @@ Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt) */ // Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time -#define OUTCOME_PREVIOUS_COMMIT_REF 571f9c930e672950e99d5d30f743603aaaf8014c -#define OUTCOME_PREVIOUS_COMMIT_DATE "2024-03-13 20:29:49 +00:00" -#define OUTCOME_PREVIOUS_COMMIT_UNIQUE 571f9c93 +#define OUTCOME_PREVIOUS_COMMIT_REF 5bcd0f32f28659a0eab62f1d643d878c2220788d +#define OUTCOME_PREVIOUS_COMMIT_DATE "2024-07-15 17:46:44 +00:00" +#define OUTCOME_PREVIOUS_COMMIT_UNIQUE 5bcd0f32 #define OUTCOME_V2 (QUICKCPPLIB_BIND_NAMESPACE_VERSION(outcome_v2)) #ifdef _DEBUG #define OUTCOME_V2_CXX_MODULE_NAME QUICKCPPLIB_BIND_NAMESPACE((QUICKCPPLIB_BIND_NAMESPACE_VERSION(outcome_v2d))) @@ -2138,15 +2138,11 @@ namespace detail // Void does nothing template <> struct move_assign_to_empty { - move_assign_to_empty(void *, void *) noexcept - { /* nothing to assign */ - } + move_assign_to_empty(void *, void *) noexcept { /* nothing to assign */ } }; template <> struct move_assign_to_empty { - move_assign_to_empty(const void *, const void *) noexcept - { /* nothing to assign */ - } + move_assign_to_empty(const void *, const void *) noexcept { /* nothing to assign */ } }; // Helpers for copy assigning to empty storage template ::value, @@ -2173,15 +2169,11 @@ namespace detail // Void does nothing template <> struct copy_assign_to_empty { - copy_assign_to_empty(void *, void *) noexcept - { /* nothing to assign */ - } + copy_assign_to_empty(void *, void *) noexcept { /* nothing to assign */ } }; template <> struct copy_assign_to_empty { - copy_assign_to_empty(const void *, const void *) noexcept - { /* nothing to assign */ - } + copy_assign_to_empty(const void *, const void *) noexcept { /* nothing to assign */ } }; template struct strong_swap_impl { @@ -2309,9 +2301,10 @@ namespace detail #ifdef _MSC_VER __declspec(noreturn) #elif defined(__GNUC__) || defined(__clang__) - __attribute__((noreturn)) + __attribute__((noreturn)) #endif - void make_ub(T && /*unused*/) + void + make_ub(T && /*unused*/) { OUTCOME_ASSERT(false); // NOLINT #if defined(__GNUC__) || defined(__clang__) @@ -2884,11 +2877,11 @@ namespace detail // value/value if(_status.have_value() && o._status.have_value()) { - struct _ + struct some_type { status_bitfield_type &a, &b; bool all_good{false}; - ~_() + ~some_type() { if(!this->all_good) { @@ -2897,19 +2890,19 @@ namespace detail this->b.set_have_lost_consistency(true); } } - } _{_status, o._status}; - strong_swap(_.all_good, _value, o._value); + } some_type_value{_status, o._status}; + strong_swap(some_type_value.all_good, _value, o._value); swap(_status, o._status); return; } // error/error if(_status.have_error() && o._status.have_error()) { - struct _ + struct some_type { status_bitfield_type &a, &b; bool all_good{false}; - ~_() + ~some_type() { if(!this->all_good) { @@ -2918,8 +2911,8 @@ namespace detail this->b.set_have_lost_consistency(true); } } - } _{_status, o._status}; - strong_swap(_.all_good, _error, o._error); + } some_type_value{_status, o._status}; + strong_swap(some_type_value.all_good, _error, o._error); swap(_status, o._status); return; } @@ -2969,13 +2962,13 @@ namespace detail return; } // It can now only be value/error, or error/value - struct _ + struct some_type { status_bitfield_type &a, &b; _value_type_ *value, *o_value; _error_type_ *error, *o_error; bool all_good{true}; - ~_() + ~some_type() { if(!this->all_good) { @@ -2984,21 +2977,21 @@ namespace detail this->b.set_have_lost_consistency(true); } } - } _{_status, o._status, OUTCOME_ADDRESS_OF(_value), OUTCOME_ADDRESS_OF(o._value), OUTCOME_ADDRESS_OF(_error), OUTCOME_ADDRESS_OF(o._error)}; + } some_type_value{_status, o._status, OUTCOME_ADDRESS_OF(_value), OUTCOME_ADDRESS_OF(o._value), OUTCOME_ADDRESS_OF(_error), OUTCOME_ADDRESS_OF(o._error)}; if(_status.have_value() && o._status.have_error()) { - strong_placement(_.all_good, _.o_value, _.value, [&_] { // - strong_placement(_.all_good, _.error, _.o_error, [&_] { // - swap(_.a, _.b); // + strong_placement(some_type_value.all_good, some_type_value.o_value, some_type_value.value, [&some_type_value] { // + strong_placement(some_type_value.all_good, some_type_value.error, some_type_value.o_error, [&some_type_value] { // + swap(some_type_value.a, some_type_value.b); // }); }); return; } if(_status.have_error() && o._status.have_value()) { - strong_placement(_.all_good, _.o_error, _.error, [&_] { // - strong_placement(_.all_good, _.value, _.o_value, [&_] { // - swap(_.a, _.b); // + strong_placement(some_type_value.all_good, some_type_value.o_error, some_type_value.error, [&some_type_value] { // + strong_placement(some_type_value.all_good, some_type_value.value, some_type_value.o_value, [&some_type_value] { // + swap(some_type_value.a, some_type_value.b); // }); }); return; @@ -3069,12 +3062,12 @@ namespace detail constexpr #endif value_storage_nontrivial_move_assignment & - operator=(value_storage_nontrivial_move_assignment &&o) noexcept( - std::is_nothrow_move_assignable::value && - std::is_nothrow_move_assignable::value && noexcept(move_assign_to_empty( - static_cast(nullptr), - static_cast(nullptr))) && noexcept(move_assign_to_empty(static_cast(nullptr), - static_cast(nullptr)))) // NOLINT + operator=(value_storage_nontrivial_move_assignment &&o) noexcept(std::is_nothrow_move_assignable::value && + std::is_nothrow_move_assignable::value && + noexcept(move_assign_to_empty(static_cast(nullptr), + static_cast(nullptr))) && + noexcept(move_assign_to_empty(static_cast(nullptr), + static_cast(nullptr)))) // NOLINT { using _value_type_ = typename Base::_value_type_; using _error_type_ = typename Base::_error_type_; @@ -3173,10 +3166,9 @@ namespace detail #endif value_storage_nontrivial_copy_assignment & operator=(const value_storage_nontrivial_copy_assignment &o) noexcept( - std::is_nothrow_copy_assignable::value && - std::is_nothrow_copy_assignable::value && noexcept(copy_assign_to_empty( - static_cast(nullptr), static_cast(nullptr))) && noexcept(copy_assign_to_empty(static_cast(nullptr), - static_cast(nullptr)))) + std::is_nothrow_copy_assignable::value && std::is_nothrow_copy_assignable::value && + noexcept(copy_assign_to_empty(static_cast(nullptr), static_cast(nullptr))) && + noexcept(copy_assign_to_empty(static_cast(nullptr), static_cast(nullptr)))) { using _value_type_ = typename Base::_value_type_; using _error_type_ = typename Base::_error_type_; @@ -6064,12 +6056,12 @@ SIGNATURE NOT RECOGNISED swap(this->_ptr, o._ptr); return; } - struct _ + struct some_type { basic_outcome &a, &b; bool exceptioned{false}; bool all_good{false}; - ~_() + ~some_type() { if(!this->all_good) { @@ -6110,11 +6102,11 @@ SIGNATURE NOT RECOGNISED check(&this->b); } } - } _{*this, o}; - strong_swap(_.all_good, this->_ptr, o._ptr); - _.exceptioned = true; + } some_type_value{*this, o}; + strong_swap(some_type_value.all_good, this->_ptr, o._ptr); + some_type_value.exceptioned = true; this->_state.swap(o._state); - _.exceptioned = false; + some_type_value.exceptioned = false; #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/single-header/outcome-experimental.hpp b/single-header/outcome-experimental.hpp index cd5ca0d1361..689b63b698f 100644 --- a/single-header/outcome-experimental.hpp +++ b/single-header/outcome-experimental.hpp @@ -1044,9 +1044,9 @@ Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt) */ // Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time -#define OUTCOME_PREVIOUS_COMMIT_REF 571f9c930e672950e99d5d30f743603aaaf8014c -#define OUTCOME_PREVIOUS_COMMIT_DATE "2024-03-13 20:29:49 +00:00" -#define OUTCOME_PREVIOUS_COMMIT_UNIQUE 571f9c93 +#define OUTCOME_PREVIOUS_COMMIT_REF 5bcd0f32f28659a0eab62f1d643d878c2220788d +#define OUTCOME_PREVIOUS_COMMIT_DATE "2024-07-15 17:46:44 +00:00" +#define OUTCOME_PREVIOUS_COMMIT_UNIQUE 5bcd0f32 #define OUTCOME_V2 (QUICKCPPLIB_BIND_NAMESPACE_VERSION(outcome_v2)) #ifdef _DEBUG #define OUTCOME_V2_CXX_MODULE_NAME QUICKCPPLIB_BIND_NAMESPACE((QUICKCPPLIB_BIND_NAMESPACE_VERSION(outcome_v2d))) @@ -2163,15 +2163,11 @@ namespace detail // Void does nothing template <> struct move_assign_to_empty { - move_assign_to_empty(void *, void *) noexcept - { /* nothing to assign */ - } + move_assign_to_empty(void *, void *) noexcept { /* nothing to assign */ } }; template <> struct move_assign_to_empty { - move_assign_to_empty(const void *, const void *) noexcept - { /* nothing to assign */ - } + move_assign_to_empty(const void *, const void *) noexcept { /* nothing to assign */ } }; // Helpers for copy assigning to empty storage template ::value, @@ -2198,15 +2194,11 @@ namespace detail // Void does nothing template <> struct copy_assign_to_empty { - copy_assign_to_empty(void *, void *) noexcept - { /* nothing to assign */ - } + copy_assign_to_empty(void *, void *) noexcept { /* nothing to assign */ } }; template <> struct copy_assign_to_empty { - copy_assign_to_empty(const void *, const void *) noexcept - { /* nothing to assign */ - } + copy_assign_to_empty(const void *, const void *) noexcept { /* nothing to assign */ } }; template struct strong_swap_impl { @@ -2334,9 +2326,10 @@ namespace detail #ifdef _MSC_VER __declspec(noreturn) #elif defined(__GNUC__) || defined(__clang__) - __attribute__((noreturn)) + __attribute__((noreturn)) #endif - void make_ub(T && /*unused*/) + void + make_ub(T && /*unused*/) { OUTCOME_ASSERT(false); // NOLINT #if defined(__GNUC__) || defined(__clang__) @@ -2909,11 +2902,11 @@ namespace detail // value/value if(_status.have_value() && o._status.have_value()) { - struct _ + struct some_type { status_bitfield_type &a, &b; bool all_good{false}; - ~_() + ~some_type() { if(!this->all_good) { @@ -2922,19 +2915,19 @@ namespace detail this->b.set_have_lost_consistency(true); } } - } _{_status, o._status}; - strong_swap(_.all_good, _value, o._value); + } some_type_value{_status, o._status}; + strong_swap(some_type_value.all_good, _value, o._value); swap(_status, o._status); return; } // error/error if(_status.have_error() && o._status.have_error()) { - struct _ + struct some_type { status_bitfield_type &a, &b; bool all_good{false}; - ~_() + ~some_type() { if(!this->all_good) { @@ -2943,8 +2936,8 @@ namespace detail this->b.set_have_lost_consistency(true); } } - } _{_status, o._status}; - strong_swap(_.all_good, _error, o._error); + } some_type_value{_status, o._status}; + strong_swap(some_type_value.all_good, _error, o._error); swap(_status, o._status); return; } @@ -2994,13 +2987,13 @@ namespace detail return; } // It can now only be value/error, or error/value - struct _ + struct some_type { status_bitfield_type &a, &b; _value_type_ *value, *o_value; _error_type_ *error, *o_error; bool all_good{true}; - ~_() + ~some_type() { if(!this->all_good) { @@ -3009,21 +3002,21 @@ namespace detail this->b.set_have_lost_consistency(true); } } - } _{_status, o._status, OUTCOME_ADDRESS_OF(_value), OUTCOME_ADDRESS_OF(o._value), OUTCOME_ADDRESS_OF(_error), OUTCOME_ADDRESS_OF(o._error)}; + } some_type_value{_status, o._status, OUTCOME_ADDRESS_OF(_value), OUTCOME_ADDRESS_OF(o._value), OUTCOME_ADDRESS_OF(_error), OUTCOME_ADDRESS_OF(o._error)}; if(_status.have_value() && o._status.have_error()) { - strong_placement(_.all_good, _.o_value, _.value, [&_] { // - strong_placement(_.all_good, _.error, _.o_error, [&_] { // - swap(_.a, _.b); // + strong_placement(some_type_value.all_good, some_type_value.o_value, some_type_value.value, [&some_type_value] { // + strong_placement(some_type_value.all_good, some_type_value.error, some_type_value.o_error, [&some_type_value] { // + swap(some_type_value.a, some_type_value.b); // }); }); return; } if(_status.have_error() && o._status.have_value()) { - strong_placement(_.all_good, _.o_error, _.error, [&_] { // - strong_placement(_.all_good, _.value, _.o_value, [&_] { // - swap(_.a, _.b); // + strong_placement(some_type_value.all_good, some_type_value.o_error, some_type_value.error, [&some_type_value] { // + strong_placement(some_type_value.all_good, some_type_value.value, some_type_value.o_value, [&some_type_value] { // + swap(some_type_value.a, some_type_value.b); // }); }); return; @@ -3094,12 +3087,12 @@ namespace detail constexpr #endif value_storage_nontrivial_move_assignment & - operator=(value_storage_nontrivial_move_assignment &&o) noexcept( - std::is_nothrow_move_assignable::value && - std::is_nothrow_move_assignable::value && noexcept(move_assign_to_empty( - static_cast(nullptr), - static_cast(nullptr))) && noexcept(move_assign_to_empty(static_cast(nullptr), - static_cast(nullptr)))) // NOLINT + operator=(value_storage_nontrivial_move_assignment &&o) noexcept(std::is_nothrow_move_assignable::value && + std::is_nothrow_move_assignable::value && + noexcept(move_assign_to_empty(static_cast(nullptr), + static_cast(nullptr))) && + noexcept(move_assign_to_empty(static_cast(nullptr), + static_cast(nullptr)))) // NOLINT { using _value_type_ = typename Base::_value_type_; using _error_type_ = typename Base::_error_type_; @@ -3198,10 +3191,9 @@ namespace detail #endif value_storage_nontrivial_copy_assignment & operator=(const value_storage_nontrivial_copy_assignment &o) noexcept( - std::is_nothrow_copy_assignable::value && - std::is_nothrow_copy_assignable::value && noexcept(copy_assign_to_empty( - static_cast(nullptr), static_cast(nullptr))) && noexcept(copy_assign_to_empty(static_cast(nullptr), - static_cast(nullptr)))) + std::is_nothrow_copy_assignable::value && std::is_nothrow_copy_assignable::value && + noexcept(copy_assign_to_empty(static_cast(nullptr), static_cast(nullptr))) && + noexcept(copy_assign_to_empty(static_cast(nullptr), static_cast(nullptr)))) { using _value_type_ = typename Base::_value_type_; using _error_type_ = typename Base::_error_type_; @@ -6089,12 +6081,12 @@ SIGNATURE NOT RECOGNISED swap(this->_ptr, o._ptr); return; } - struct _ + struct some_type { basic_outcome &a, &b; bool exceptioned{false}; bool all_good{false}; - ~_() + ~some_type() { if(!this->all_good) { @@ -6135,11 +6127,11 @@ SIGNATURE NOT RECOGNISED check(&this->b); } } - } _{*this, o}; - strong_swap(_.all_good, this->_ptr, o._ptr); - _.exceptioned = true; + } some_type_value{*this, o}; + strong_swap(some_type_value.all_good, this->_ptr, o._ptr); + some_type_value.exceptioned = true; this->_state.swap(o._state); - _.exceptioned = false; + some_type_value.exceptioned = false; #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/single-header/outcome.hpp b/single-header/outcome.hpp index 9dd4fc66347..8caece5e9ac 100644 --- a/single-header/outcome.hpp +++ b/single-header/outcome.hpp @@ -1043,9 +1043,9 @@ Distributed under the Boost Software License, Version 1.0. http://www.boost.org/LICENSE_1_0.txt) */ // Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time -#define OUTCOME_PREVIOUS_COMMIT_REF 571f9c930e672950e99d5d30f743603aaaf8014c -#define OUTCOME_PREVIOUS_COMMIT_DATE "2024-03-13 20:29:49 +00:00" -#define OUTCOME_PREVIOUS_COMMIT_UNIQUE 571f9c93 +#define OUTCOME_PREVIOUS_COMMIT_REF 5bcd0f32f28659a0eab62f1d643d878c2220788d +#define OUTCOME_PREVIOUS_COMMIT_DATE "2024-07-15 17:46:44 +00:00" +#define OUTCOME_PREVIOUS_COMMIT_UNIQUE 5bcd0f32 #define OUTCOME_V2 (QUICKCPPLIB_BIND_NAMESPACE_VERSION(outcome_v2)) #ifdef _DEBUG #define OUTCOME_V2_CXX_MODULE_NAME QUICKCPPLIB_BIND_NAMESPACE((QUICKCPPLIB_BIND_NAMESPACE_VERSION(outcome_v2d))) @@ -3075,15 +3075,11 @@ namespace detail // Void does nothing template <> struct move_assign_to_empty { - move_assign_to_empty(void *, void *) noexcept - { /* nothing to assign */ - } + move_assign_to_empty(void *, void *) noexcept { /* nothing to assign */ } }; template <> struct move_assign_to_empty { - move_assign_to_empty(const void *, const void *) noexcept - { /* nothing to assign */ - } + move_assign_to_empty(const void *, const void *) noexcept { /* nothing to assign */ } }; // Helpers for copy assigning to empty storage template ::value, @@ -3110,15 +3106,11 @@ namespace detail // Void does nothing template <> struct copy_assign_to_empty { - copy_assign_to_empty(void *, void *) noexcept - { /* nothing to assign */ - } + copy_assign_to_empty(void *, void *) noexcept { /* nothing to assign */ } }; template <> struct copy_assign_to_empty { - copy_assign_to_empty(const void *, const void *) noexcept - { /* nothing to assign */ - } + copy_assign_to_empty(const void *, const void *) noexcept { /* nothing to assign */ } }; template struct strong_swap_impl { @@ -3246,9 +3238,10 @@ namespace detail #ifdef _MSC_VER __declspec(noreturn) #elif defined(__GNUC__) || defined(__clang__) - __attribute__((noreturn)) + __attribute__((noreturn)) #endif - void make_ub(T && /*unused*/) + void + make_ub(T && /*unused*/) { OUTCOME_ASSERT(false); // NOLINT #if defined(__GNUC__) || defined(__clang__) @@ -3821,11 +3814,11 @@ namespace detail // value/value if(_status.have_value() && o._status.have_value()) { - struct _ + struct some_type { status_bitfield_type &a, &b; bool all_good{false}; - ~_() + ~some_type() { if(!this->all_good) { @@ -3834,19 +3827,19 @@ namespace detail this->b.set_have_lost_consistency(true); } } - } _{_status, o._status}; - strong_swap(_.all_good, _value, o._value); + } some_type_value{_status, o._status}; + strong_swap(some_type_value.all_good, _value, o._value); swap(_status, o._status); return; } // error/error if(_status.have_error() && o._status.have_error()) { - struct _ + struct some_type { status_bitfield_type &a, &b; bool all_good{false}; - ~_() + ~some_type() { if(!this->all_good) { @@ -3855,8 +3848,8 @@ namespace detail this->b.set_have_lost_consistency(true); } } - } _{_status, o._status}; - strong_swap(_.all_good, _error, o._error); + } some_type_value{_status, o._status}; + strong_swap(some_type_value.all_good, _error, o._error); swap(_status, o._status); return; } @@ -3906,13 +3899,13 @@ namespace detail return; } // It can now only be value/error, or error/value - struct _ + struct some_type { status_bitfield_type &a, &b; _value_type_ *value, *o_value; _error_type_ *error, *o_error; bool all_good{true}; - ~_() + ~some_type() { if(!this->all_good) { @@ -3921,21 +3914,21 @@ namespace detail this->b.set_have_lost_consistency(true); } } - } _{_status, o._status, OUTCOME_ADDRESS_OF(_value), OUTCOME_ADDRESS_OF(o._value), OUTCOME_ADDRESS_OF(_error), OUTCOME_ADDRESS_OF(o._error)}; + } some_type_value{_status, o._status, OUTCOME_ADDRESS_OF(_value), OUTCOME_ADDRESS_OF(o._value), OUTCOME_ADDRESS_OF(_error), OUTCOME_ADDRESS_OF(o._error)}; if(_status.have_value() && o._status.have_error()) { - strong_placement(_.all_good, _.o_value, _.value, [&_] { // - strong_placement(_.all_good, _.error, _.o_error, [&_] { // - swap(_.a, _.b); // + strong_placement(some_type_value.all_good, some_type_value.o_value, some_type_value.value, [&some_type_value] { // + strong_placement(some_type_value.all_good, some_type_value.error, some_type_value.o_error, [&some_type_value] { // + swap(some_type_value.a, some_type_value.b); // }); }); return; } if(_status.have_error() && o._status.have_value()) { - strong_placement(_.all_good, _.o_error, _.error, [&_] { // - strong_placement(_.all_good, _.value, _.o_value, [&_] { // - swap(_.a, _.b); // + strong_placement(some_type_value.all_good, some_type_value.o_error, some_type_value.error, [&some_type_value] { // + strong_placement(some_type_value.all_good, some_type_value.value, some_type_value.o_value, [&some_type_value] { // + swap(some_type_value.a, some_type_value.b); // }); }); return; @@ -4006,12 +3999,12 @@ namespace detail constexpr #endif value_storage_nontrivial_move_assignment & - operator=(value_storage_nontrivial_move_assignment &&o) noexcept( - std::is_nothrow_move_assignable::value && - std::is_nothrow_move_assignable::value && noexcept(move_assign_to_empty( - static_cast(nullptr), - static_cast(nullptr))) && noexcept(move_assign_to_empty(static_cast(nullptr), - static_cast(nullptr)))) // NOLINT + operator=(value_storage_nontrivial_move_assignment &&o) noexcept(std::is_nothrow_move_assignable::value && + std::is_nothrow_move_assignable::value && + noexcept(move_assign_to_empty(static_cast(nullptr), + static_cast(nullptr))) && + noexcept(move_assign_to_empty(static_cast(nullptr), + static_cast(nullptr)))) // NOLINT { using _value_type_ = typename Base::_value_type_; using _error_type_ = typename Base::_error_type_; @@ -4110,10 +4103,9 @@ namespace detail #endif value_storage_nontrivial_copy_assignment & operator=(const value_storage_nontrivial_copy_assignment &o) noexcept( - std::is_nothrow_copy_assignable::value && - std::is_nothrow_copy_assignable::value && noexcept(copy_assign_to_empty( - static_cast(nullptr), static_cast(nullptr))) && noexcept(copy_assign_to_empty(static_cast(nullptr), - static_cast(nullptr)))) + std::is_nothrow_copy_assignable::value && std::is_nothrow_copy_assignable::value && + noexcept(copy_assign_to_empty(static_cast(nullptr), static_cast(nullptr))) && + noexcept(copy_assign_to_empty(static_cast(nullptr), static_cast(nullptr)))) { using _value_type_ = typename Base::_value_type_; using _error_type_ = typename Base::_error_type_; @@ -7599,12 +7591,12 @@ SIGNATURE NOT RECOGNISED swap(this->_ptr, o._ptr); return; } - struct _ + struct some_type { basic_outcome &a, &b; bool exceptioned{false}; bool all_good{false}; - ~_() + ~some_type() { if(!this->all_good) { @@ -7645,11 +7637,11 @@ SIGNATURE NOT RECOGNISED check(&this->b); } } - } _{*this, o}; - strong_swap(_.all_good, this->_ptr, o._ptr); - _.exceptioned = true; + } some_type_value{*this, o}; + strong_swap(some_type_value.all_good, this->_ptr, o._ptr); + some_type_value.exceptioned = true; this->_state.swap(o._state); - _.exceptioned = false; + some_type_value.exceptioned = false; #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/test/tests/experimental-c-result.cpp b/test/tests/experimental-c-result.cpp new file mode 100644 index 00000000000..22b124cfd43 --- /dev/null +++ b/test/tests/experimental-c-result.cpp @@ -0,0 +1,197 @@ +/* Unit testing for outcomes +(C) 2013-2024 Niall Douglas (8 commits) + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_NOT_POSIX + +#include "../../include/outcome/experimental/result.h" + +struct custom_value_type +{ + int v[16]; + + bool operator==(const custom_value_type &o) const noexcept { return 0 == memcmp(v, o.v, sizeof(v)); } +}; + +enum c_enum +{ + c_enum_success, + c_enum_failure +}; + +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wunused-function" +#endif + +CXX_DECLARE_RESULT_SYSTEM(test, custom_value_type); +using result_custom = CXX_RESULT_SYSTEM(test); + +CXX_DECLARE_RESULT_SYSTEM_FROM_ENUM(test, c_enum, "{74ceb994-7622-3a21-07f0-b016aa705585}", {c_enum::c_enum_success, "success", {errc::success}}, + {c_enum::c_enum_failure, "failure", {errc::invalid_argument}}); + +template using result = OUTCOME_V2_NAMESPACE::experimental::status_result; + +#include "quickcpplib/boost/test/unit_test.hpp" + +BOOST_OUTCOME_AUTO_TEST_CASE(works / status_code / c_result, "Tests that the C result with status_code works as intended") +{ + using namespace SYSTEM_ERROR2_NAMESPACE; + const custom_value_type shouldbe{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + + { // errored with posix code + result_custom l(outcome_make_result_system_test_failure_posix((int) errc::bad_address)); + result m(to_result(l)); + BOOST_CHECK(!m); + BOOST_CHECK(!m.has_value()); + BOOST_CHECK(m.has_error()); + // BOOST_CHECK(!m.has_exception()); + BOOST_CHECK_THROW(m.value(), posix_error); + BOOST_CHECK_NO_THROW(m.error()); + result_custom l2(outcome_make_result_system_test_failure_posix((int) errc::bad_address)); + BOOST_CHECK(outcome_status_code_equal(&l.error, &l2.error)); + BOOST_CHECK(outcome_status_code_equal_generic(&l.error, EFAULT)); + std::cout << outcome_status_code_message(&l.error) << std::endl; + BOOST_CHECK(0 == strcmp(outcome_status_code_message(&l.error), "Bad address")); + } + { // valued + result_custom l(outcome_make_result_system_test_success(shouldbe)); + result m(to_result(l)); + BOOST_CHECK(m); + BOOST_CHECK(m.has_value()); + BOOST_CHECK(!m.has_error()); + // BOOST_CHECK(!m.has_exception()); + BOOST_CHECK(m.value() == shouldbe); + } + if(false) // correctly contract asserts + { // errored from custom C enum 1 + result_custom l(CXX_MAKE_RESULT_SYSTEM_FROM_ENUM(test, c_enum, c_enum::c_enum_success)); + result m(to_result(l)); + BOOST_CHECK(!m); + BOOST_CHECK(!m.has_value()); + BOOST_CHECK(m.has_error()); + // BOOST_CHECK(!m.has_exception()); + BOOST_CHECK_THROW(m.value(), status_error<_quick_status_code_from_enum_domain>); + BOOST_CHECK_NO_THROW(m.error()); + result_custom l2(outcome_make_result_system_test_failure_posix((int) errc::invalid_argument)); + BOOST_CHECK(!outcome_status_code_equal(&l.error, &l2.error)); + } + { // errored from custom C enum 2 + result_custom l(CXX_MAKE_RESULT_SYSTEM_FROM_ENUM(test, c_enum, c_enum::c_enum_failure)); + result m(to_result(l)); + BOOST_CHECK(!m); + BOOST_CHECK(!m.has_value()); + BOOST_CHECK(m.has_error()); + // BOOST_CHECK(!m.has_exception()); + BOOST_CHECK_THROW(m.value(), status_error<_quick_status_code_from_enum_domain>); + BOOST_CHECK_NO_THROW(m.error()); + result_custom l2(outcome_make_result_system_test_failure_posix((int) errc::invalid_argument)); + BOOST_CHECK(outcome_status_code_equal(&l.error, &l2.error)); + BOOST_CHECK(outcome_status_code_equal_generic(&l.error, EINVAL)); + std::cout << outcome_status_code_message(&l.error) << std::endl; + BOOST_CHECK(0 == strcmp(outcome_status_code_message(&l.error), "failure")); + } + auto d(outcome_make_result_system_test_failure_posix((int) errc::bad_address)); + static_assert(std::is_default_constructible::value, ""); + static_assert(std::is_nothrow_default_constructible::value, ""); + static_assert(std::is_copy_constructible::value, ""); + static_assert(std::is_trivially_copy_constructible::value, ""); + static_assert(std::is_nothrow_copy_constructible::value, ""); + static_assert(std::is_copy_assignable::value, ""); + static_assert(std::is_trivially_copy_assignable::value, ""); + static_assert(std::is_nothrow_copy_assignable::value, ""); + static_assert(std::is_move_assignable::value, ""); + static_assert(std::is_trivially_move_assignable::value, ""); + static_assert(std::is_nothrow_move_assignable::value, ""); + static_assert(std::is_trivially_destructible::value, ""); + static_assert(std::is_nothrow_destructible::value, ""); + + // Single parameter C try + { + auto will_succeed = [&]() -> result_custom { return outcome_make_result_system_test_success(shouldbe); }; + auto will_fail = [&]() -> result_custom { return outcome_make_result_system_test_failure_posix((int) errc::bad_address); }; + auto test = [&](result_custom r) -> result_custom + { + CXX_RESULT_SYSTEM_TRY(r); + return will_succeed(); + }; + auto r1 = test(will_succeed()); + BOOST_CHECK(CXX_RESULT_HAS_VALUE(r1)); + BOOST_CHECK(0 == memcmp(r1.value.v, shouldbe.v, sizeof(shouldbe.v))); + auto r2 = test(will_fail()); + BOOST_CHECK(CXX_RESULT_HAS_ERROR(r2)); + BOOST_CHECK(outcome_status_code_equal_generic(&r2.error, EFAULT)); + } + // C try with cleanup on failure + { + auto will_succeed = [&]() -> result_custom { return outcome_make_result_system_test_success(shouldbe); }; + auto will_fail = [&]() -> result_custom { return outcome_make_result_system_test_failure_posix((int) errc::bad_address); }; + auto test = [&](result_custom r) -> result_custom + { + CXX_RESULT_SYSTEM_TRY(printf("failed!\n"), r); + return will_succeed(); + }; + auto r1 = test(will_succeed()); + BOOST_CHECK(CXX_RESULT_HAS_VALUE(r1)); + BOOST_CHECK(0 == memcmp(r1.value.v, shouldbe.v, sizeof(shouldbe.v))); + auto r2 = test(will_fail()); + BOOST_CHECK(CXX_RESULT_HAS_ERROR(r2)); + BOOST_CHECK(outcome_status_code_equal_generic(&r2.error, EFAULT)); + } + // C try with cleanup and success output + { + auto will_succeed = [&]() -> result_custom { return outcome_make_result_system_test_success(shouldbe); }; + auto will_fail = [&]() -> result_custom { return outcome_make_result_system_test_failure_posix((int) errc::bad_address); }; + auto test = [&](result_custom r) -> result_custom + { + CXX_RESULT_SYSTEM_TRY(custom_value_type v, printf("failed!\n"), r); + return outcome_make_result_system_test_success(v); + }; + auto r1 = test(will_succeed()); + BOOST_CHECK(CXX_RESULT_HAS_VALUE(r1)); + BOOST_CHECK(0 == memcmp(r1.value.v, shouldbe.v, sizeof(shouldbe.v))); + auto r2 = test(will_fail()); + BOOST_CHECK(CXX_RESULT_HAS_ERROR(r2)); + BOOST_CHECK(outcome_status_code_equal_generic(&r2.error, EFAULT)); + } + // C try with cleanup and success output and different return type + { + auto will_succeed = [&]() -> result_custom { return outcome_make_result_system_test_success(shouldbe); }; + auto will_fail = [&]() -> result_custom { return outcome_make_result_system_test_failure_posix((int) errc::bad_address); }; + auto test = [&](result_custom r) -> result_custom + { + CXX_RESULT_SYSTEM_TRY(custom_value_type v, test, printf("failed!\n"), r); + return outcome_make_result_system_test_success(v); + }; + auto r1 = test(will_succeed()); + BOOST_CHECK(CXX_RESULT_HAS_VALUE(r1)); + BOOST_CHECK(0 == memcmp(r1.value.v, shouldbe.v, sizeof(shouldbe.v))); + auto r2 = test(will_fail()); + BOOST_CHECK(CXX_RESULT_HAS_ERROR(r2)); + BOOST_CHECK(outcome_status_code_equal_generic(&r2.error, EFAULT)); + } +} +#else +int main(void) +{ + return 0; +} +#endif