Skip to content

Commit

Permalink
- Outcome.Experimental has had C representation support since the beg…
Browse files Browse the repository at this point in the history
…inning, 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.
  • Loading branch information
ned14 committed Jul 16, 2024
1 parent 5bcd0f3 commit d99338e
Show file tree
Hide file tree
Showing 37 changed files with 1,322 additions and 345 deletions.
3 changes: 3 additions & 0 deletions cmake/headers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ 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"
"include/outcome/experimental/result.h"
"include/outcome/experimental/status-code/example/outcome-experimental.hpp"
"include/outcome/experimental/status-code/include/status-code/boost_error_code.hpp"
"include/outcome/experimental/status-code/include/status-code/com_code.hpp"
"include/outcome/experimental/status-code/include/status-code/config.hpp"
Expand All @@ -49,6 +51,7 @@ set(outcome_HEADERS
"include/outcome/experimental/status-code/include/status-code/system_code_from_exception.hpp"
"include/outcome/experimental/status-code/include/status-code/system_error2.hpp"
"include/outcome/experimental/status-code/include/status-code/win32_code.hpp"
"include/outcome/experimental/status-code/single-header/system_error2-nowindows.hpp"
"include/outcome/experimental/status-code/single-header/system_error2.hpp"
"include/outcome/experimental/status_outcome.hpp"
"include/outcome/experimental/status_result.hpp"
Expand Down
1 change: 1 addition & 0 deletions cmake/tests.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
20 changes: 19 additions & 1 deletion doc/src/content/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -44,7 +46,7 @@ Fully deterministic all-`noexcept` C++ Coroutine support in Outcome is particula
supply Outcome-optimising {{< api "eager<T, Executor = void>/atomic_eager<T, Executor = void>" >}}, {{< api "lazy<T, Executor = void>/atomic_lazy<T, Executor = void>" >}}
and {{<api "generator<T, Executor = void>" >}} awaitables which work for any user type.

## Sample usage
## Sample usage (C++)

The main workhorse in the Outcome library is `result<T>`: 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:

Expand All @@ -61,6 +63,22 @@ Or, if this function is called in another function that also returns `result<T>`
`OUTCOME_TRY` is a control statement. If the returned `result<T>` object contains an error information, the enclosing function is immediately returned with `result<U>` 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 %}}
Expand Down
24 changes: 21 additions & 3 deletions doc/src/content/changelog/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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)

Expand Down
3 changes: 3 additions & 0 deletions doc/src/content/experimental/advantages.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<outcome/experimental>`.

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
Expand Down
50 changes: 50 additions & 0 deletions doc/src/content/experimental/c-api/from-c/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
+++
title = "C Results"
description = "Outcome's C Result support"
weight = 30
+++

The C macro API header `<outcome/experimental/result.h>` has some macros for working with any kind of Result:

<dl>
<dt><code>CXX_DECLARE_RESULT(ident, T, E)</code>
<dd>Declares to C a <code>basic_result<T, E></code> type uniquely
identified by <code>ident</code>. <code>T</code> is available at the
member variable <code>.value</code>, and <code>E</code> is available
at the member variable <code>.error</code>.

<dt><code>CXX_RESULT(ident)</code>
<dd>A reference to a previously declared <code>result</code> type with
unique <code>ident</code>.

<dt><code>CXX_RESULT_HAS_VALUE(r)</code>
<dd>Evaluates to 1 (true) if the input <code>result</code> has a value.

<dt><code>CXX_RESULT_HAS_ERROR(r)</code>
<dd>Evaluates to 1 (true) if the input <code>result</code> has an error.

<dt><code>CXX_RESULT_ERROR_IS_ERRNO(r)</code>
<dd>Evaluates to 1 (true) if the input <code>result</code>'s error value
is a code in the POSIX <code>errno</code> domain.
</dl>

The above let you work, somewhat awkwardly, with any C-compatible
`basic_result<T, E>`. `basic_result<T, E>` 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.


11 changes: 11 additions & 0 deletions doc/src/content/experimental/c-api/from-c/declare.md
Original file line number Diff line number Diff line change
@@ -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.
86 changes: 86 additions & 0 deletions doc/src/content/experimental/c-api/from-c/system_code.md
Original file line number Diff line number Diff line change
@@ -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 `<outcome/experimental/result.h>`.

The subset supported are those `result<T, E>` which are [a `status_result<T>`]({{% 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:

<dl>
<dt><code>CXX_DECLARE_RESULT_SYSTEM(ident, T)</code>
<dd>Declares to C a <code>status_result<T></code> type uniquely
identified by <code>ident</code>. <code>T</code> is available at the
member variable <code>.value</code>, and <code>struct cxx_status_code_system</code>
is available at the member variable <code>.error</code>. If in C++,
implements C extern functions for making successful and failure results
of this type.

<dt><code>CXX_RESULT_SYSTEM(ident)</code>
<dd>A reference to a previously declared <code>status_result</code> type with
unique <code>ident</code>.

<dt><code>CXX_MAKE_RESULT_SYSTEM_SUCCESS(ident, expr)</code> (needs C++ counterpart linked into final binary)
<dd>This invokes the aforementioned extern function which creates a <code>status_result</code>
with a successful value of type <code>T</code>.
<dt><code>CXX_MAKE_RESULT_SYSTEM_FAILURE_POSIX(ident, expr)</code> (needs C++ counterpart linked into final binary)
<dd>This invokes the aforementioned extern function which creates a <code>status_result</code>
with a failure of type <code>posix_code</code> representing a POSIX <code>errno</code>.
<dt><code>CXX_MAKE_RESULT_SYSTEM_FAILURE_SYSTEM(ident, expr)</code> (needs C++ counterpart linked into final binary)
<dd>This invokes the aforementioned extern function which creates a <code>status_result</code>
with a failure of type <code>posix_code</code> representing a POSIX <code>errno</code>
if on POSIX; if on Windows then a failure of type <code>win32_code</code>
representing a Win32 error code from a Windows API.

<br><br>
<dt><code>CXX_RESULT_HAS_VALUE(r)</code>
<dd>Evaluates to 1 (true) if the input <code>result</code> has a value.

<dt><code>CXX_RESULT_HAS_ERROR(r)</code>
<dd>Evaluates to 1 (true) if the input <code>result</code> has an error.

<dt><code>CXX_RESULT_ERROR_IS_ERRNO(r)</code>
<dd>Evaluates to 1 (true) if the input <code>result</code>'s error value
is a code in the POSIX <code>errno</code> domain.
<br><br>
<dt><code>CXX_RESULT_SYSTEM_TRY(expr)</code>
<dd>If the <code>status_result</code> returned by <code>expr</code> is
errored, exit the current function returning the result. This obviously
requires that the return type of the current function matches that of <code>
expr</code>.

<dt><code>CXX_RESULT_SYSTEM_TRY(cleanup, expr)</code>
<dd>Same as the above, but execute <code>cleanup</code> just before exiting the function
if returning failure.

<dt><code>CXX_RESULT_SYSTEM_TRY(var, cleanup, expr)</code>
<dd>Same as the above, but set <code>var</code> equal to the result's <code>.value</code> on success.

<dt><code>CXX_RESULT_SYSTEM_TRY(var, ident, cleanup, expr)</code>
<dd>Same as the above, but use <code>ident</code> as the return type instead. This allows
the return type of the calling function to differ from that of <code>expr</code>.
<br><br>
<dt><code>CXX_DECLARE_RESULT_SYSTEM_FROM_ENUM(ident, enum_name, uuid, {enum mapping-sequence, ...})</code>
<dd>This declares to C an extern function which creates a <code>status_result</code>
from a C enum. If in C++, it implements a <code>quick_status_code_from_enum</code> for
the C enum and the associated extern function, and you will need to supply <code>uuid</code>
and the appropriate enum value mapping sequence <a href="{{% relref "../../worked-example" %}}">
as per the <code>quick_status_code_from_enum</code> documentation</a>.
<dt><code>CXX_MAKE_RESULT_SYSTEM_FROM_ENUM(ident, enum_name, expr)</code> (needs C++ counterpart linked into final binary)
<dd>This invokes the aforementioned extern function which creates a <code>status_result</code>
from a C enum.
</dl>

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.
18 changes: 18 additions & 0 deletions doc/src/content/experimental/c-api/from-c/try.md
Original file line number Diff line number Diff line change
@@ -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.
14 changes: 14 additions & 0 deletions doc/src/content/experimental/c-api/from-c/use.md
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading

0 comments on commit d99338e

Please sign in to comment.