Skip to content

Commit

Permalink
Ported the tutorial to Outcome v2.2.
Browse files Browse the repository at this point in the history
  • Loading branch information
ned14 committed Dec 15, 2020
1 parent e0efc83 commit 68bf2d6
Show file tree
Hide file tree
Showing 26 changed files with 166 additions and 92 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/make_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ jobs:
shell: bash
run: |
if [ -n "$BOOSTORG_OUTCOME_DEPLOY_KEY" ] && [ "${{ steps.waitforstatuschecks.outputs.status }}" = "success" ]; then
mkdir -p ~/.ssh;
printf "%s" "$BOOSTORG_OUTCOME_DEPLOY_KEY" > "~/.ssh/id_boostorg_outcome";
chmod -R og-rwx ~/.ssh;
mkdir -p $HOME/.ssh;
printf "%s" "$BOOSTORG_OUTCOME_DEPLOY_KEY" > "$HOME/.ssh/id_boostorg_outcome";
chmod -R og-rwx $HOME/.ssh;
unset HAS_DEPLOY_KEY;
HAS_DEPLOY_KEY='true';
fi
Expand Down
4 changes: 2 additions & 2 deletions doc/src/content/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ At the end of December 2020, Outcome v2.2 replaced v2.1 in develop branch. This
change and all Outcome v2.1 code will need to be upgraded using [the v2.1 => v2.2 upgrade guide]({{% relref "/changelog/upgrade_v21_v22" %}}). See also
[the list of v2.2 major changes]({{% relref "/changelog/v22" %}}).
<br><br>
This library's tutorial is currently being converted from v2.1 to v2.2, so be aware some code
examples may not compile correctly yet. Once the documentation is fully upgraded, develop branch
This library's documentation is currently being converted from v2.1 to v2.2, so be aware some places
may be out of date. Once the documentation is fully upgraded, develop branch
shall be merged into master branch.
{{% /notice %}}

Expand Down
49 changes: 36 additions & 13 deletions doc/src/content/experimental/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,34 @@ weight = 15

In `<outcome/experimental>`, there ships an Outcome-based simulation of
the proposed [P1095 *Zero overhead deterministic failure*](https://wg21.link/P1095)
specific implementation of [P0709 *Zero overhead exceptions: Throwing values*](http://wg21.link/P0709), aka "Herbceptions". This library-only implementation lets you use a close simulacrum
specific implementation of [P0709 *Zero overhead exceptions: Throwing values*](http://wg21.link/P0709).
This library-only implementation lets you use a close simulacrum
of potential future C++ lightweight exceptions today in [any C++ 14 compiler
which Outcome supports]({{< relref "/requirements" >}}).

{{% notice warning %}}
<b>It is stressed, in the strongest possible terms, that any item inside
`<outcome/experimental>` is subject to unannounced breaking change based
on WG21 standards committee feedback</b>. That said, the chances are high
that most of those breaking changes will be to naming rather than to
fundamental semantics, so you can upgrade with a bit of find and replace.
There are quite a few large code bases out there
already using this experimental support in anger, we know it works well
at scale and it's a good bit superior to `std::error_code` et al on every
measure.
{{% /notice %}}
This Experimental Outcome implementation has been in production use for some
years now. It has shipped on at least one billion devices, as part of a
games suite very popular on Microsoft Windows, Apple iOS and Google Android
devices. It powers big iron enterprise applications as well, indeed all
trades including futures in the United States are captured live into a database
for the SEC by an Experimental Outcome based codebase.

P1095's support library has a reference implementation at https://ned14.github.io/status-code/.
Experimental Outcome uses the [same proposed `std::error` object as P1095 would do
for its `E` type](https://wg21.link/P1028) by bundling internally a copy of
https://ned14.github.io/status-code/, the reference implementation library
for proposed `std::error`. P1028 `status_code` is a large enhancement and
backwards compatible superset of `std::error_code` which is also capable of
transporting any move-only type such as `std::exception_ptr`. Status codes
are [P1029 move bitcopying](https://wg21.link/P1029) whereby moved-from
objects do not need to be destroyed. Outcome emulates move
bitcopying semantics for types declaring themselves move bitcopying via the
trait {{% api "is_move_bitcopying<T>" %}}, and status codes opt into
this. This greatly improves codegen to be no worse than with `std::error_code`
(a trivially copyable type), as https://godbolt.org/z/GEdEGc
demonstrates, despite that proposed `std::error` is a move-only type with
a non-trivial destructor.

P1095's reference implementation can be found at https://ned14.github.io/status-code/.
You will find terse documentation there, and an API reference.
This library is wholly incorporated into Outcome in the `<outcome/experimental/status-code>`
directory, with bindings into Outcome provided in the following headers:
Expand All @@ -44,3 +55,15 @@ For non-Windows non-POSIX platforms such as some embedded systems, standalone
Experimental Outcome can be used with the `SYSTEM_ERROR2_NOT_POSIX` macro
defined. This does not include POSIX headers, and makes available a high fidelity,
fully deterministic, alternative to C++ exceptions on such platforms.

Finally, there is a single include edition of Experimental Outcome, which
can be found at https://github.com/ned14/outcome/blob/develop/single-header/outcome-experimental.hpp

{{% notice warning %}}
<b>It is stressed, in the strongest possible terms, that any item inside
`<outcome/experimental>` is subject to unannounced breaking change based
on WG21 standards committee feedback</b>. That said, the chances are high
that most of those breaking changes will be to naming rather than to
fundamental semantics, so you can upgrade with a bit of find and replace.
{{% /notice %}}

2 changes: 1 addition & 1 deletion doc/src/content/recipes/foreign-try.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ tags = [ "TRY" ]
Outcome's {{% api "OUTCOME_TRY(var, expr)" %}} operation is fully extensible
to accept as input any foreign types.
It already recognises types matching the
{{% api "ValueOrError<T, E>" %}} concept, which is to say all types which have:
{{% api "concepts::value_or_error<T, E>" %}} concept, which is to say all types which have:

- A public `.has_value()` member function which returns a `bool`.
- In order of preference, a public `.assume_value()`/`.value()` member
Expand Down
2 changes: 1 addition & 1 deletion doc/src/content/reference/concepts/value_or_none.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ If on C++ 20 or the Concepts TS is enabled, a boolean concept matching types wit

If without Concepts, a static constexpr bool which is true for types matching the same requirements, using a SFINAE based emulation.

This concept matches optional-like types such as {{% api "std::optional<T>" %}}. Note it also matches {{% api "std::expected<T, E>" %}}, which also has an optional-like interface. You may thus wish to preferentially match {{% api "ValueOrError<T, E>" %}} for any given `T`.
This concept matches optional-like types such as {{% api "std::optional<T>" %}}. Note it also matches {{% api "std::expected<T, E>" %}}, which also has an optional-like interface. You may thus wish to preferentially match {{% api "concepts::value_or_error<T, E>" %}} for any given `T`.

*Namespace*: `OUTCOME_V2_NAMESPACE::concepts`

Expand Down
8 changes: 4 additions & 4 deletions doc/src/content/reference/converters/value_or_error.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
+++
title = "`value_or_error<T, U>`"
description = "A customisable converter of `ValueOrError<T, E>` concept matching types."
description = "A customisable converter of `value_or_error<T, E>` concept matching types."
+++

A customisable converter of {{% api "ValueOrError<T, E>" %}} concept matching types. It must have the following form:
A customisable converter of {{% api "concepts::value_or_error<T, E>" %}} concept matching types. It must have the following form:

```c++
// `T` will be the destination basic_result or basic_outcome.
// `U` will be the decayed form of the `ValueOrError<T, E>` concept matching input type.
// `U` will be the decayed form of the `value_or_error<T, E>` concept matching input type.
template <class T> struct value_or_error<T, U>
{
// False to indicate that this converter wants `basic_result`/`basic_outcome` to reject all other `basic_result`
Expand All @@ -22,7 +22,7 @@ template <class T> struct value_or_error<T, U>
*Overridable*: By template specialisation into the `convert` namespace.
*Default*: If decayed `X` is same as `U`, concept `ValueOrError<U>` matches, `X::value_type` is `void` or is explicitly constructible to `T::value_type`, and `X::error_type` is `void` or is explicitly constructible to `T::error_type`, then `operator()(X &&)` is made available.
*Default*: If decayed `X` is same as `U`, concept `value_or_error<U>` matches, `X::value_type` is `void` or is explicitly constructible to `T::value_type`, and `X::error_type` is `void` or is explicitly constructible to `T::error_type`, then `operator()(X &&)` is made available.
`operator()(X &&v)` tests if `v.has_value()` is true, if so then a `T` with successful value is returned, else a `T` with unsuccessful value. If the input type was `void`, a default constructed value is used for either, else a move/copy construction from the source is performed.
Expand Down
38 changes: 38 additions & 0 deletions doc/src/content/reference/traits/is_move_bitcopying.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
+++
title = "`is_move_bitcopying<T>`"
description = "A customisable integral constant type true for `T` types which are move bitcopying safe."
+++

A customisable integral constant type true for `T` types which are move bitcopying
safe. As per [P1029 move bitcopying](https://wg21.link/P1029), these are types for
which:

1. There is an inline, constexpr-available, default constructor.
2. The move constructor has side effects equivalent to `memcpy` of source to destination,
followed by a `memcpy` of a default constructed instance to source.
3. That the destruction of a default constructed instance has no visible
side effects.

This implies that if you move from a bit copying type, you need not call its
destructor, even if that is a virtual destructor.

If you opt your types into this trait, Outcome will track moved-from state and
not call the destructor for your type on moved-from instances. Obviously enough
this is, in current C++ standards, undefined behaviour. However it very
significantly improves the quality of codegen during inlining.

*Overridable*: By template specialisation into the `trait` namespace.

*Default*: False. Default specialisations exist for:

- `<outcome/experimental/status_result.hpp>`
- True for `SYSTEM_ERROR2_NAMESPACE::status_code<DomainType>` if trait
`SYSTEM_ERROR2_NAMESPACE::traits::is_move_bitcopying<SYSTEM_ERROR2_NAMESPACE::status_code<DomainType>>::value`
is true.
- True for `SYSTEM_ERROR2_NAMESPACE::errored_status_code<DomainType>` if trait
`SYSTEM_ERROR2_NAMESPACE::traits::is_move_bitcopying<SYSTEM_ERROR2_NAMESPACE::errored_status_code<DomainType>>::value`
is true.

*Namespace*: `OUTCOME_V2_NAMESPACE::trait`

*Header*: `<outcome/trait.hpp>`
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
+++
title = "`explicit basic_outcome(ValueOrError<T, E> &&)`"
description = "Explicit converting constructor from `ValueOrError<T, E>` concept matching types. Available if `convert::value_or_error<>` permits it. Constexpr, triviality and noexcept propagating."
title = "`explicit basic_outcome(concepts::value_or_error<T, E> &&)`"
description = "Explicit converting constructor from `concepts::value_or_error<T, E>` concept matching types. Available if `convert::value_or_error<>` permits it. Constexpr, triviality and noexcept propagating."
categories = ["constructors", "explicit-constructors", "converting-constructors"]
weight = 300
+++

Explicit converting constructor from {{% api "ValueOrError<T, E>" %}} concept matching types. Delegates to the `basic_result` move constructor {{% api "explicit basic_outcome(basic_result<A, B, C> &&)" %}}.
Explicit converting constructor from {{% api "concepts::value_or_error<T, E>" %}} concept matching types. Delegates to the `basic_result` move constructor {{% api "explicit basic_outcome(basic_result<A, B, C> &&)" %}}.

*Requires*: `convert::`{{% api "value_or_error<T, U>" %}} has an available call operator, and if the input is a `basic_result` or `basic_outcome`, then `convert::value_or_error<>` has enabled those inputs for that `convert::value_or_error<>` specialisation.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
+++
title = "`explicit basic_result(ValueOrError<T, E> &&)`"
description = "Explicit converting constructor from `ValueOrError<T, E>` concept matching types. Available if `convert::value_or_error<>` permits it. Constexpr, triviality and noexcept propagating."
title = "`explicit basic_result(concepts::value_or_error<T, E> &&)`"
description = "Explicit converting constructor from `concepts::value_or_error<T, E>` concept matching types. Available if `convert::value_or_error<>` permits it. Constexpr, triviality and noexcept propagating."
categories = ["constructors", "explicit-constructors", "converting-constructors"]
weight = 300
+++

Explicit converting constructor from {{% api "ValueOrError<T, E>" %}} concept matching types. Delegates to the `basic_result` move constructor.
Explicit converting constructor from {{% api "concepts::value_or_error<T, E>" %}} concept matching types. Delegates to the `basic_result` move constructor.

*Requires*: `convert::`{{% api "value_or_error<T, U>" %}} has an available call operator, and if the input is a `basic_result` or `basic_outcome`, then `convert::value_or_error<>` has enabled those inputs for that `convert::value_or_error<>` specialisation.

Expand Down
5 changes: 0 additions & 5 deletions doc/src/content/tutorial/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,4 @@ title = "Tutorial"
weight = 10
+++

{{% notice note %}}
This tutorial is currently being upgraded to refer to Outcome v2.2. It may
be in a partially upgraded state with sections still referring to Outcome v2.1.
{{% /notice %}}

{{% children description="true" depth="2" %}}
6 changes: 4 additions & 2 deletions doc/src/content/tutorial/advanced/hooks/adl_bridging.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ tags = [ "adl-bridging"]
+++

{{% notice note %}}
In Outcome v2.2 the ADL-based event hooks will be replaced with policy-based event hooks.
In Outcome v2.2 the ADL-based event hooks were replaced with policy-based event hooks (next page).
The code in this section is still valid in v2.2 onwards, it's just that ADL is no longer used
to find the hooks.
{{% /notice %}}

In a previous section, we used the `failure_info` type to create
the ADL bridge into the namespace where the ADL discovered [`outcome_throw_as_system_error_with_payload()`]({{< relref "/reference/functions/policy" >}}) function was to be found.

Here we do the same, but more directly by creating a thin clone of `std::error_code`
into the local namespace. This ensures that this namespace will be searched by the
compiler when discovering the event hooks.
compiler when discovering the event hooks (Outcome v2.1 and earlier only).

{{% snippet "error_code_extended.cpp" "error_code_extended2" %}}

Expand Down
13 changes: 7 additions & 6 deletions doc/src/content/tutorial/advanced/hooks/hook_outcome.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ description = ""
weight = 50
+++

{{% notice note %}}
In Outcome v2.2 the ADL-based event hooks will be replaced with policy-based event hooks.
{{% /notice %}}

The final step is to add ADL discovered event hooks for the very specific case of
The final step is to add event hooks for the very specific case of
when our localised `outcome` is copy or move constructed from our localised `result`.

You ought to be very careful that the `noexcept`-ness of these matches the `noexcept`-ness
Expand All @@ -19,7 +15,12 @@ copy and/or move constructor. Thus if `poke_exception()` throws, instant program
termination would occur, which is bad.

We avoid that problem in this case by wrapping `poke_exception()` in a `try...catch`
which throws away any exceptions thrown.
which throws away any exceptions thrown. For Outcome before v2.2, these specially
named free functions must be placed into a namespace which is ADL searched:

{{% snippet "error_code_extended.cpp" "error_code_extended5" %}}

For Outcome v2.2 and later, these functions must be placed into a custom no value
policy with the names `on_outcome_copy_construction()` and `on_outcome_move_construction()`
respectively. As with before, the implementation of the functions is identical, just
the name and location has changed.
11 changes: 7 additions & 4 deletions doc/src/content/tutorial/advanced/hooks/hook_result.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ description = ""
weight = 30
+++

{{% notice note %}}
In Outcome v2.2 the ADL-based event hooks will be replaced with policy-based event hooks.
{{% /notice %}}

We now tell Outcome that for every instance of our localised `result<T>`, that
on failure construction only, we want custom code to be run which increments the current
slot in TLS storage and writes the current stack backtrace into it.

For Outcome before v2.2, we must do this by inserting a specially named free function into
a namespace searched by ADL:

{{% snippet "error_code_extended.cpp" "error_code_extended3" %}}

For Outcome v2.2 and later, we must do this by using a custom no value policy which contains
a function named `on_result_construction()`. The function implementation is identical between
both mechanisms, just the name and placement of the function declaration differs.

The only non-obvious part above is the call to {{< api "void set_spare_storage(basic_result|basic_outcome *, uint16_t) noexcept" >}}.

Both `result` and `outcome` keep their internal state metadata in a `uint32_t`,
Expand Down
4 changes: 0 additions & 4 deletions doc/src/content/tutorial/advanced/hooks/poke_exception.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ description = ""
weight = 40
+++

{{% notice note %}}
In Outcome v2.2 the ADL-based event hooks will be replaced with policy-based event hooks.
{{% /notice %}}

If you merely want `result` to capture stack backtraces without calling a memory allocator
and retaining any triviality of copy which is important for optimisation,
you already have everything you need.
Expand Down
2 changes: 1 addition & 1 deletion doc/src/content/tutorial/advanced/interop/app-go.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This is how you might now write application code using these three libraries:

The curiosity will be surely the `ext()` markup function, which needs
explaining. It was felt
important during Outcome's design that `ValueOrError` conversions never
important during Outcome's design that `value_or_error` conversions never
be implicit, as they almost always represent a transition across an
ABI or semantic boundary. They are also usually non-trivial to implement
and compile, and it was felt important that the programmer ought to
Expand Down
10 changes: 5 additions & 5 deletions doc/src/content/tutorial/advanced/interop/app-map-httplib2.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ title = "Mapping the HTTP library into the Application `2/2`"
weight = 35
+++

If you remember the tutorial section on the [`ValueOrError` Concept](../value-or-error),
this is an example of how to implement a custom `ValueOrError` Concept converter
If you remember the tutorial section on the [`value_or_error` Concept](../value-or-error),
this is an example of how to implement a custom `value_or_error` Concept converter
in Outcome:

{{% snippet "finale.cpp" "app_map_httplib2" %}}

The first thing that you should note is that these custom converters must be injected
directly into the `OUTCOME_V2_NAMESPACE::convert` namespace, and they must partially
or completely specialise {{% api "value_or_error<T, U>" %}}. Here we specialise the
converter for `ValueOrError` conversions from `httplib::result<U>` to `app::outcome<T>`
converter for `value_or_error` conversions from `httplib::result<U>` to `app::outcome<T>`
i.e. from our third party HTTP library's error type into our application's `outcome`
type (which is unique to our application, as we hard code an `app`-local error type).

Expand All @@ -22,10 +22,10 @@ converter[^1]. In this converter, we really do wish to convert other `result` an
`outcome` inputs, so we mark these booleans as `true`.

The third thing to note is the requirements on `operator()`. If the requirements are
not met, the `ValueOrError` converting constructor in `basic_result` and `basic_outcome`
not met, the `value_or_error` converting constructor in `basic_result` and `basic_outcome`
disables. Note the requirement that the decayed `operator()` input `X` matches
`httplib::result<U>`, and that `T` is constructible from `U`. This means that the
{{% api "explicit basic_result(ValueOrError<T, E> &&)" %}} and {{% api "explicit basic_outcome(ValueOrError<T, E> &&)" %}}
{{% api "explicit basic_result(concepts::value_or_error<T, E> &&)" %}} and {{% api "explicit basic_outcome(concepts::value_or_error<T, E> &&)" %}}
constructors are available if, and only if, the input type is a `httplib::result<U>`,
and the result's value type is constructible from the input's value type.

Expand Down
2 changes: 1 addition & 1 deletion doc/src/content/tutorial/advanced/interop/conclusion.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ If you control your application's `E` type, then that is probably a
better, and certainly simpler, approach.

However there are occasions when you don't have control over the
implementation of the destination `E` type e.g. in callbacks. Outcome's `ValueOrError`
implementation of the destination `E` type e.g. in callbacks. Outcome's `value_or_error`
infrastructure lets you inject custom interop code for any pair
of incommensurate third party `E` types, without needing to modify either's
source code.
Expand Down
Loading

0 comments on commit 68bf2d6

Please sign in to comment.