result<>
We will define a function that converts a 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;
Template alias result<T, E = varies, NoValuePolicy = policy::default_policy<T, E, void>>
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
in standalone Outcome, or to boost::system::error_code
in Boost.Outcome1. The third parameter (NoValuePolicy
) 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.
Triviality, complexity and constexpr-ness of each operation (construction, copy construction, assignment,
destruction etc) is always the intersection of the corresponding operation in T
and EC
,
so for example if both T
and EC
are literal types, so will be result<T, EC, NVP>
.
Additionally, if both T
and EC
have standard layout, result<T, EC, NVP>
has standard layout;
thus if both T
and EC
are C-compatible, so will result<T, EC, NVP>
be C-compatible.
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
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());
}
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};
Or use helper functions:
outcome::result<int> r = outcome::failure(ConversionErrc::EmptyString);
outcome::result<int> s = outcome::success(1);
The functions auto failure(T &&, …)
and auto success(T &&, …)
return special
types implicitly convertible to failed or successful result
(and outcome
).
- You can mandate a choice using
std_result<T>
orboost_result<T>
. [return]