• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1+++
2title = "Extending `BOOST_OUTCOME_TRY`"
3description = "How to informing `BOOST_OUTCOME_TRY` about foreign Result types."
4tags = [ "TRY" ]
5+++
6
7Outcome's {{% api "BOOST_OUTCOME_TRY(var, expr)" %}} operation is fully extensible
8to accept as input any foreign types.
9It already recognises types matching the
10{{% api "ValueOrError<T, E>" %}} concept, which is to say all types which have:
11
12- A public `.has_value()` member function which returns a `bool`.
13- In order of preference, a public `.assume_value()`/`.value()` member
14function.
15- In order of preference, a public `.as_failure()`/`.assume_error()`/`.error()`
16member function.
17
18This should automatically handle inputs of `std::expected<T, E>`, and many others,
19including intermixing Boost.Outcome and standalone Outcome within the same
20translation unit.
21
22`BOOST_OUTCOME_TRY` has the following free function customisation points:
23
24<dl>
25<dt><code>BOOST_OUTCOME_V2_NAMESPACE::</code>{{% api "try_operation_has_value(X)" %}}
26<dd>Returns a `bool` which is true if the input to TRY has a value.
27<dt><code>BOOST_OUTCOME_V2_NAMESPACE::</code>{{% api "try_operation_return_as(X)" %}}
28<dd>Returns a suitable {{% api "failure_type<EC, EP = void>" %}} which
29is returned immediately to cause stack unwind. Ought to preserve rvalue
30semantics (i.e. if passed an rvalue, move the error state into the failure
31type).
32<dt><code>BOOST_OUTCOME_V2_NAMESPACE::</code>{{% api "try_operation_extract_value(X)" %}}
33<dd>Extracts a value type from the input for the `TRY` to set its variable.
34Ought to preserve rvalue semantics (i.e. if passed an rvalue, move the value).
35</dl>
36
37New overloads of these to support additional input types must be injected into
38the `BOOST_OUTCOME_V2_NAMESPACE` namespace before the compiler parses the relevant
39`BOOST_OUTCOME_TRY` in order to be found. This is called 'early binding' in the two
40phase name lookup model in C++. This was chosen over 'late binding', where an
41`BOOST_OUTCOME_TRY` in a templated piece of code could look up overloads introduced after
42parsing the template containing the `BOOST_OUTCOME_TRY`, because it has much lower
43impact on build times, as binding is done once at the point of parse, instead
44of on every occasion at the point of instantiation. If you are careful to ensure
45that you inject the overloads which you need early in the parse of the
46translation unit, all will be well.
47
48Let us work through an applied example.
49
50---
51## A very foreign pseudo-Expected type
52
53This is a paraphrase of a poorly written pseudo-Expected type which I once
54encountered in the production codebase of a large multinational. Lots
55of the code was already using it, and it was weird enough that it couldn't
56be swapped out for something better easily.
57
58{{% snippet "foreign_try.cpp" "foreign_type" %}}
59
60What we would like is for new code to be written using Outcome, but be able
61to transparently call old code, like this:
62
63{{% snippet "foreign_try.cpp" "functions" %}}
64
65Telling Outcome about this weird foreign Expected is straightforward:
66
67{{% snippet "foreign_try.cpp" "tell_outcome" %}}
68
69And now `BOOST_OUTCOME_TRY` works exactly as expected:
70
71{{% snippet "foreign_try.cpp" "example" %}}
72
73... which outputs:
74
75```
76new_code(5) returns successful 5
77
78new_code(0) returns failure argument out of domain
79```
80