Hook result

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.

namespace error_code_extended
{
  // Specialise the result construction hook for our localised result
  // We hook any non-copy, non-move, non-inplace construction, capturing a stack backtrace
  // if the result is errored.
  template <class T, class U> inline void hook_result_construction(result<T> *res, U && /*unused*/) noexcept
  {
    if(res->has_error())
    {
      // Grab the next extended info slot in the TLS
      extended_error_info &eei = mythreadlocaldata().next();

      // Write the index just grabbed into the spare uint16_t
      OUTCOME_V2_NAMESPACE::hooks::set_spare_storage(res, mythreadlocaldata().current - 1);

      // Capture a backtrace into my claimed extended info slot in the TLS
      eei.items = ::backtrace(eei.backtrace.data(), eei.backtrace.size());
    }
  }
}
View this code on Github

The only non-obvious part above is the call to void set_spare_storage(basic_result|basic_outcome *, uint16_t) noexcept .

Both result and outcome keep their internal state metadata in a uint32_t, half of which is not used by Outcome. As it can be very useful to keep a small unique number attached to any particular result or outcome instance, we permit user code to set those sixteen bits to anything they feel like. The corresponding function to retrieve those sixteen bits is uint16_t spare_storage(const basic_result|basic_outcome *) noexcept .

The state of the sixteen bits of spare storage are ignored during comparison operations.

The sixteen bits of spare storage propagate during the following operations:

  1. Copy and move construction between result’s.
  2. Copy and move construction between outcome’s.
  3. Copy and move construction from a result to an outcome.
  4. Converting copy and move constructions for all the above.
  5. Assignment for all of the above.

They are NOT propagated in these operations:

  1. Any conversion or translation which goes through a failure_type or success_type.
  2. Any conversion or translation which goes through a ValueOrError concept match.
  3. Any unpacking or repacking of value/error/exception e.g. a manual repack of an outcome into a result.