Project: ArangoDB (App Definition and Development / Database)
File: foreign-try.md

+++ title = "Extending `BOOSTOUTCOMETRY`" description = "How to informing `BOOSTOUTCOMETRY` about foreign Result types." tags = [ "TRY" ] +++ Outcome's {{% api "BOOSTOUTCOMETRY(var, expr)" %}} operation is fully extensible to accept as input any foreign types. It already recognises types matching the {{% api "concepts::valueorerror<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 function. In order of preference, a public `.asfailure()`/`.assumeerror()`/`.error()` member function. This should automatically handle inputs of `std::expected<T, E>`, and many others, including intermixing Boost.Outcome and standalone Outcome within the same translation unit. `BOOSTOUTCOMETRY` has the following free function customisation points: <dl> <dt><code>BOOSTOUTCOMEV2NAMESPACE::</code>{{% api "tryoperationhasvalue(X)" %}} <dd>Returns a `bool` which is true if the input to TRY has a value. <dt><code>BOOSTOUTCOMEV2NAMESPACE::</code>{{% api "tryoperationreturnas(X)" %}} <dd>Returns a suitable {{% api "failure_type<EC, EP = void>" %}} which is returned immediately to cause stack unwind. Ought to preserve rvalue semantics (i.e. if passed an rvalue, move the error state into the failure type). <dt><code>BOOSTOUTCOMEV2NAMESPACE::</code>{{% api "tryoperationextractvalue(X)" %}} <dd>Extracts a value type from the input for the `TRY` to set its variable. Ought to preserve rvalue semantics (i.e. if passed an rvalue, move the value). </dl> New overloads of these to support additional input types must be injected into the `BOOSTOUTCOMEV2_NAMESPACE` namespace before the compiler parses the relevant `BOOSTOUTCOMETRY` in order to be found. This is called 'early binding' in the two phase name lookup model in C++. This was chosen over 'late binding', where an `BOOSTOUTCOMETRY` in a templated piece of code could look up overloads introduced after parsing the template containing the `BOOSTOUTCOMETRY`, because it has much lower impact on build times, as binding is done once at the point of parse, instead of on every occasion at the point of instantiation. If you are careful to ensure that you inject the overloads which you need early in the parse of the translation unit, all will be well. Let us work through an applied example. This is a paraphrase of a poorly written pseudo-Expected type which I once encountered in the production codebase of a large multinational. Lots of the code was already using it, and it was weird enough that it couldn't be swapped out for something better easily. {{% snippet "foreigntry.cpp" "foreigntype" %}} What we would like is for new code to be written using Outcome, but be able to transparently call old code, like this: {{% snippet "foreign_try.cpp" "functions" %}} Telling Outcome about this weird foreign Expected is straightforward: {{% snippet "foreigntry.cpp" "telloutcome" %}} And now `BOOSTOUTCOMETRY` works exactly as expected: {{% snippet "foreign_try.cpp" "example" %}} ... which outputs: ``` new_code(5) returns successful 5 new_code(0) returns failure argument out of domain ```