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