Outcome 2.0 namespace

It is recommended that you refer to entities from this Outcome 2.0 via the following namespace alias:

namespace outcome = OUTCOME_V2_NAMESPACE;
View this code on Github

As patches and modifications are applied to this library, namespaces get permuted in order not to break any backward compatibility. At some point namespace outcome::v2 will be defined, and this will be the prefered namespace. Until then OUTCOME_V2_NAMESPACE denotes the most recently updated version, getting closer to outcome::v2.

Creating result<>

We will define a function that converts an std::string to an int. This function can fail for a number of reasons; if it does we want to communicate the failure reason.

outcome::result<int> convert(const std::string& str) noexcept;
View this code on Github

Class template result<T, EC, NVP> has three template parameters, but the last two have default values. The first (T) represents the type of the object returned from the function upon success. The second (EC) is the type of object containing information about the reason for failure when the function fails. A result object stores either a T or an EC at any given moment, and is therefore conceptually similar to variant<T, EC>. EC is defaulted to std::error_code. The third parameter (NVP) is called a no-value policy. We will cover it later.

If both T and EC are trivially copyable, result<T, EC, NVP> is also trivially copyable.

Now, we will define an enumeration describing different failure situations during conversion.

enum class ConversionErrc
  Success     = 0, // 0 should not represent an error
  EmptyString = 1, // (for rationale, see tutorial on error codes)
  IllegalChar = 2,
  TooLong     = 3,

// all boilerplate necessary to plug ConversionErrc
// into std::error_code framework
View this code on Github

Assume we have plugged it into std::error_code framework, as described in this section.

One notable effect of such plugging is that ConversionErrc is now convertible to std::error_code. Now we can implement function convert as follows:

outcome::result<int> convert(const std::string& str) noexcept
  if (str.empty())
    return ConversionErrc::EmptyString;

  if (!std::all_of(str.begin(), str.end(), ::isdigit))
    return ConversionErrc::IllegalChar;

  if (str.length() > 9)
    return ConversionErrc::TooLong;

  return atoi(str.c_str());
View this code on Github

result<T, EC> is convertible from any T2 convertible to T as well as any EC2 convertible to EC, provided that there is no constructability possible in either direction between T and EC. If there is, all implicit conversion is disabled, and you will need to use one of the tagged constructors:

outcome::result<int> r {outcome::in_place_type<std::error_code>, ConversionErrc::EmptyString};
outcome::result<int> s {outcome::in_place_type<int>, 1};
View this code on Github

Or use helper factory functions:

outcome::result<int> r = outcome::failure(ConversionErrc::EmptyString);
outcome::result<int> s = outcome::success(1);
View this code on Github