C/C++ API Reference
Loading...
Searching...
No Matches
pw::Result< T > Class Template Reference

Overview

template<typename T>
class pw::Result< T >

Error propagation primitive: value-or error

The pw::Result<T> class template is a union of a pw::Status object and an object of type T. Result<t> models an object that is either a usable object, or an error (of type pw::Status) explaining why such an object is not present. It is typically the return value of a function which may fail.

Checking for success

Result<T> can never hold an OK status; instead, the presence of an object of type T indicates success. Instead of checking for a kOk value, use the Result<T>::ok() member function. (It is for this reason, and code readability, that using the ok() function is preferred for pw::Status as well.)

Example:

Result<Foo> result = DoBigCalculationThatCouldFail();
if (result.ok()) {
result->DoSomethingCool();
} else {
PW_LOG_ERROR("Calculation failed: %s", result.status().str());
}
Definition: result.h:143
constexpr bool ok() const
Definition: result.h:447
constexpr Status status() const
Definition: result.h:803
const char * str() const
Definition: status.h:433

Accessing objects

Accessing the object held by a Result<T> should be performed via operator* or operator->, after a call to ok() confirms that the Result<T> holds an object of type T:

Example:

Result<int> i = GetCount();
if (i.ok()) {
updated_total += *i
}

Using Result<T>::value() when no valid value is present will trigger a PW_ASSERT.

Example:

Result<Foo> result = DoBigCalculationThatCouldFail();
const Foo& foo = result.value(); // Crash/exception if no value present
foo.DoSomethingCool();
constexpr const T & value() const &PW_ATTRIBUTE_LIFETIME_BOUND
Definition: result.h:808

Constructing result objects

A Result<T*> can be constructed from a null pointer like any other pointer value, and the result will be that ok() returns true and value() returns nullptr. Checking the value of pointer in a Result<T> generally requires a bit more care, to ensure both that a value is present and that value is not null:

Result<Foo*> result = LookUpTheFoo(arg);
if (!result.ok()) {
PW_LOG_ERROR("Unable to look up the Foo: %s", result.status().str());
} else if (*result == nullptr) {
PW_LOG_ERROR("Unexpected null pointer");
} else {
(*result)->DoSomethingCool();
}

Example factory implementation returning Result<T>:

Result<Foo> FooFactory::MakeFoo(int arg) {
if (arg <= 0) {
}
return Foo(arg);
}
static constexpr Status InvalidArgument()
Definition: status.h:164
Inheritance diagram for pw::Result< T >:

Public Types

typedef T value_type
 

Public Member Functions

constexpr Result ()
 
constexpr Result (const Result &)=default
 Result<T> is copy constructible if T is copy constructible.
 
constexpr Resultoperator= (const Result &)=default
 
constexpr Result (Result &&)=default
 Result<T> is move constructible if T is move constructible.
 
constexpr Resultoperator= (Result &&)=default
 
template<typename U , std::enable_if_t< std::conjunction< std::negation< std::is_same< T, U > >, std::is_constructible< T, const U & >, std::is_convertible< const U &, T >, std::negation< internal_result::IsConstructibleOrConvertibleFromResult< T, U > > >::value, int > = 0>
constexpr Result (const Result< U > &other)
 
template<typename U , std::enable_if_t< std::conjunction< std::negation< std::is_same< T, U > >, std::is_constructible< T, const U & >, std::negation< std::is_convertible< const U &, T > >, std::negation< internal_result::IsConstructibleOrConvertibleFromResult< T, U > > >::value, int > = 0>
constexpr Result (const Result< U > &other)
 
template<typename U , std::enable_if_t< std::conjunction< std::negation< std::is_same< T, U > >, std::is_constructible< T, U && >, std::is_convertible< U &&, T >, std::negation< internal_result::IsConstructibleOrConvertibleFromResult< T, U > > >::value, int > = 0>
constexpr Result (Result< U > &&other)
 
template<typename U , std::enable_if_t< std::conjunction< std::negation< std::is_same< T, U > >, std::is_constructible< T, U && >, std::negation< std::is_convertible< U &&, T > >, std::negation< internal_result::IsConstructibleOrConvertibleFromResult< T, U > > >::value, int > = 0>
constexpr Result (Result< U > &&other)
 
template<typename U , std::enable_if_t< std::conjunction< std::negation< std::is_same< T, U > >, std::is_constructible< T, const U & >, std::is_assignable< T, const U & >, std::negation< internal_result::IsConstructibleOrConvertibleOrAssignableFromResult< T, U > > >::value, int > = 0>
constexpr Resultoperator= (const Result< U > &other)
 
template<typename U , std::enable_if_t< std::conjunction< std::negation< std::is_same< T, U > >, std::is_constructible< T, U && >, std::is_assignable< T, U && >, std::negation< internal_result::IsConstructibleOrConvertibleOrAssignableFromResult< T, U > > >::value, int > = 0>
constexpr Resultoperator= (Result< U > &&other)
 
template<typename U = Status, std::enable_if_t< std::conjunction< std::is_convertible< U &&, Status >, std::is_constructible< Status, U && >, std::negation< std::is_same< std::decay_t< U >, Result< T > > >, std::negation< std::is_same< std::decay_t< U >, T > >, std::negation< std::is_same< std::decay_t< U >, std::in_place_t > >, std::negation< internal_result::HasConversionOperatorToResult< T, U && > > >::value, int > = 0>
constexpr Result (U &&v)
 
template<typename U = Status, std::enable_if_t< std::conjunction< std::negation< std::is_convertible< U &&, Status > >, std::is_constructible< Status, U && >, std::negation< std::is_same< std::decay_t< U >, Result< T > > >, std::negation< std::is_same< std::decay_t< U >, T > >, std::negation< std::is_same< std::decay_t< U >, std::in_place_t > >, std::negation< internal_result::HasConversionOperatorToResult< T, U && > > >::value, int > = 0>
constexpr Result (U &&v)
 
template<typename U = Status, std::enable_if_t< std::conjunction< std::is_convertible< U &&, Status >, std::is_constructible< Status, U && >, std::negation< std::is_same< std::decay_t< U >, Result< T > > >, std::negation< std::is_same< std::decay_t< U >, T > >, std::negation< std::is_same< std::decay_t< U >, std::in_place_t > >, std::negation< internal_result::HasConversionOperatorToResult< T, U && > > >::value, int > = 0>
constexpr Resultoperator= (U &&v)
 
template<typename U = T, typename = typename std::enable_if<std::conjunction< std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>, std::disjunction< std::is_same<std::remove_cv_t<std::remove_reference_t<U>>, T>, std::conjunction< std::negation<std::is_convertible<U&&, Status>>, std::negation< internal_result::HasConversionOperatorToResult<T, U&&>>>>, internal_result::IsForwardingAssignmentValid<T, U&&>>::value>::type>
constexpr Resultoperator= (U &&v)
 
template<typename... Args>
constexpr Result (std::in_place_t, Args &&... args)
 
template<typename U , typename... Args>
constexpr Result (std::in_place_t, std::initializer_list< U > ilist, Args &&... args)
 
template<typename U = T, std::enable_if_t< std::conjunction< internal_result::IsDirectInitializationValid< T, U && >, std::is_constructible< T, U && >, std::is_convertible< U &&, T >, std::disjunction< std::is_same< std::remove_cv_t< std::remove_reference_t< U > >, T >, std::conjunction< std::negation< std::is_convertible< U &&, Status > >, std::negation< internal_result::HasConversionOperatorToResult< T, U && > > > > >::value, int > = 0>
constexpr Result (U &&u)
 
template<typename U = T, std::enable_if_t< std::conjunction< internal_result::IsDirectInitializationValid< T, U && >, std::disjunction< std::is_same< std::remove_cv_t< std::remove_reference_t< U > >, T >, std::conjunction< std::negation< std::is_constructible< Status, U && > >, std::negation< internal_result::HasConversionOperatorToResult< T, U && > > > >, std::is_constructible< T, U && >, std::negation< std::is_convertible< U &&, T > > >::value, int > = 0>
constexpr Result (U &&u)
 
constexpr bool ok () const
 
constexpr Status status () const
 
constexpr const T & value () const &PW_ATTRIBUTE_LIFETIME_BOUND
 
constexpr T & value () &PW_ATTRIBUTE_LIFETIME_BOUND
 
constexpr const T && value () const &&PW_ATTRIBUTE_LIFETIME_BOUND
 
constexpr T && value () &&PW_ATTRIBUTE_LIFETIME_BOUND
 
constexpr const T & operator* () const &PW_ATTRIBUTE_LIFETIME_BOUND
 
constexpr T & operator* () &PW_ATTRIBUTE_LIFETIME_BOUND
 
constexpr const T && operator* () const &&PW_ATTRIBUTE_LIFETIME_BOUND
 
constexpr T && operator* () &&PW_ATTRIBUTE_LIFETIME_BOUND
 
constexpr const T * operator-> () const PW_ATTRIBUTE_LIFETIME_BOUND
 
constexpr T * operator-> () PW_ATTRIBUTE_LIFETIME_BOUND
 
template<typename U >
constexpr T value_or (U &&default_value) const &
 
template<typename U >
constexpr T value_or (U &&default_value) &&
 
constexpr void IgnoreError () const
 
template<typename... Args>
T & emplace (Args &&... args)
 
template<typename U , typename... Args, std::enable_if_t< std::is_constructible< T, std::initializer_list< U > &, Args &&... >::value, int > = 0>
T & emplace (std::initializer_list< U > ilist, Args &&... args)
 
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, T&>, std::enable_if_t< std::is_copy_constructible_v< Ret >, int > = 0>
constexpr Ret and_then (Fn &&function) &
 
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, T&&>, std::enable_if_t< std::is_move_constructible_v< Ret >, int > = 0>
constexpr auto and_then (Fn &&function) &&
 
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, const T&>, std::enable_if_t< std::is_copy_constructible_v< Ret >, int > = 0>
constexpr auto and_then (Fn &&function) const &
 
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, const T&&>, std::enable_if_t< std::is_move_constructible_v< Ret >, int > = 0>
constexpr auto and_then (Fn &&function) const &&
 
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, const Status&>, std::enable_if_t<!std::is_void_v< Ret >, int > = 0>
constexpr Result< T > or_else (Fn &&function) const &
 
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, const Status&>, std::enable_if_t< std::is_void_v< Ret >, int > = 0>
constexpr Result< T > or_else (Fn &&function) const &
 
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, Status&&>, std::enable_if_t<!std::is_void_v< Ret >, int > = 0>
constexpr Result< T > or_else (Fn &&function) &&
 
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, Status&&>, std::enable_if_t< std::is_void_v< Ret >, int > = 0>
constexpr Result< T > or_else (Fn &&function) &&
 
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, T&>, std::enable_if_t< std::is_copy_constructible_v< Ret >, int > = 0>
constexpr Result< Ret > transform (Fn &&function) &
 
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, T&&>, std::enable_if_t< std::is_move_constructible_v< Ret >, int > = 0>
constexpr Result< Ret > transform (Fn &&function) &&
 
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, T&>, std::enable_if_t< std::is_copy_constructible_v< Ret >, int > = 0>
constexpr Result< Ret > transform (Fn &&function) const &
 
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, T&&>, std::enable_if_t< std::is_move_constructible_v< Ret >, int > = 0>
constexpr Result< Ret > transform (Fn &&function) const &&
 
template<typename U >
constexpr T value_or (U &&default_value) const &
 
template<typename U >
constexpr T value_or (U &&default_value) &&
 

Member Typedef Documentation

◆ value_type

template<typename T >
typedef T pw::Result< T >::value_type

This instance data provides a generic value_type member for use within generic programming. This usage is analogous to that of std::optional::value_type.

Constructor & Destructor Documentation

◆ Result() [1/5]

template<typename T >
constexpr pw::Result< T >::Result
explicitconstexpr

Constructs a new Result<T> with an pw::Status::Unknown() status.

This constructor is marked explicit to prevent usages in return values (e.g. return {}) under the misconception that Result<std::vector<int>> (for example) will be initialized with an empty vector.

◆ Result() [2/5]

template<typename T >
template<typename U , std::enable_if_t< std::conjunction< std::negation< std::is_same< T, U > >, std::is_constructible< T, const U & >, std::is_convertible< const U &, T >, std::negation< internal_result::IsConstructibleOrConvertibleFromResult< T, U > > >::value, int > = 0>
constexpr pw::Result< T >::Result ( const Result< U > &  other)
inlineconstexpr

Constructs a new Result<T> from an pw::Result<U>, when T is constructible from U.

To avoid ambiguity, these constructors are disabled if T is also constructible from Result<U>. This constructor is explicit if and only if the corresponding construction of T from U is explicit. (This constructor inherits its explicitness from the underlying constructor.)

◆ Result() [3/5]

template<typename T >
template<typename U = Status, std::enable_if_t< std::conjunction< std::is_convertible< U &&, Status >, std::is_constructible< Status, U && >, std::negation< std::is_same< std::decay_t< U >, Result< T > > >, std::negation< std::is_same< std::decay_t< U >, T > >, std::negation< std::is_same< std::decay_t< U >, std::in_place_t > >, std::negation< internal_result::HasConversionOperatorToResult< T, U && > > >::value, int > = 0>
constexpr pw::Result< T >::Result ( U &&  v)
inlineconstexpr

Constructs a new Result<T> with a non-OK status.

After calling this constructor, this->ok() will be false and calls to value() will crash, or produce an exception if exceptions are enabled.

The constructor also takes any type U that is convertible to Status. This constructor is explicit if an only if U is not of type Status and the conversion from U to Status is explicit.

Precondition
!Status(std::forward<U>(v)).ok(). This requirement is DCHECKed. In optimized builds, passing OK here will have the effect of passing INTERNAL as a fallback.

◆ Result() [4/5]

template<typename T >
template<typename... Args>
constexpr pw::Result< T >::Result ( std::in_place_t  ,
Args &&...  args 
)
explicitconstexpr

Constructs the inner value T in-place using the provided args, using the T(args...) constructor.

◆ Result() [5/5]

template<typename T >
template<typename U = T, std::enable_if_t< std::conjunction< internal_result::IsDirectInitializationValid< T, U && >, std::is_constructible< T, U && >, std::is_convertible< U &&, T >, std::disjunction< std::is_same< std::remove_cv_t< std::remove_reference_t< U > >, T >, std::conjunction< std::negation< std::is_convertible< U &&, Status > >, std::negation< internal_result::HasConversionOperatorToResult< T, U && > > > > >::value, int > = 0>
constexpr pw::Result< T >::Result ( U &&  u)
inlineconstexpr

Constructs the inner value T in-place using the provided args, using the T(U) (direct-initialization) constructor.

Precondition
This constructor is only valid if T can be constructed from a U. Can accept move or copy constructors.

This constructor is explicit if U is not convertible to T. To avoid ambiguity, this constructor is disabled if U is a Result<J>, where J is convertible to T.

Member Function Documentation

◆ and_then()

template<typename T >
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, T&>, std::enable_if_t< std::is_copy_constructible_v< Ret >, int > = 0>
constexpr Ret pw::Result< T >::and_then ( Fn &&  function) &
inlineconstexpr
Returns
The Result<T> from the invocation of the function on the contained value if it exists. Otherwise, returns the contained status in the Result<T>.

General template logic:

template <typename U>
constexpr Ret and_then(Fn &&function) &
Definition: result.h:585
fit::function_impl< function_internal::config::kInlineCallableSize, !function_internal::config::kEnableDynamicAllocation, FunctionType, PW_FUNCTION_DEFAULT_ALLOCATOR_TYPE > Function
Definition: function.h:73

Example:

Result<Foo> CreateFoo();
Result<Bar> CreateBarFromFoo(const Foo& foo);
Result<Bar> bar = CreateFoo().and_then(CreateBarFromFoo);

◆ emplace()

template<typename T >
template<typename... Args>
T & pw::Result< T >::emplace ( Args &&...  args)
inline

Reconstructs the inner value T in-place using the provided args, using the T(args...) constructor.

Returns
A reference to the reconstructed T.

◆ IgnoreError()

template<typename T >
constexpr void pw::Result< T >::IgnoreError
constexpr

Ignores any errors. This method does nothing except potentially suppress complaints from any tools that are checking that errors are not dropped.

◆ ok()

template<typename T >
constexpr bool pw::Result< T >::ok ( ) const
inlineconstexpr
Returns
Whether or not this Result<T> holds a T value. This member function is analogous to OK and should be used similarly to check the status of return values.

Example:

Result<Foo> result = DoBigCalculationThatCouldFail();
if (result.ok()) {
// Handle result
else {
// Handle error
}

◆ operator*()

template<typename T >
constexpr const T & pw::Result< T >::operator* ( ) const &
constexpr
Returns
A reference to the current value.
Precondition
this->ok() == true, otherwise the behavior is undefined.

Use this->ok() to verify that there is a current value within the Result<T>. Alternatively, see the value() member function for a similar API that guarantees crashing or throwing an exception if there is no current value.

◆ operator->()

template<typename T >
constexpr const T * pw::Result< T >::operator->
constexpr
Returns
A pointer to the current value.
Precondition
this->ok() == true, otherwise the behavior is undefined.

Use this->ok() to verify that there is a current value.

◆ operator=() [1/4]

template<typename T >
constexpr Result & pw::Result< T >::operator= ( const Result< T > &  )
constexprdefault

Result<T> is copy assignable if T is copy constructible and copy assignable.

◆ operator=() [2/4]

template<typename T >
template<typename U , std::enable_if_t< std::conjunction< std::negation< std::is_same< T, U > >, std::is_constructible< T, const U & >, std::is_assignable< T, const U & >, std::negation< internal_result::IsConstructibleOrConvertibleOrAssignableFromResult< T, U > > >::value, int > = 0>
constexpr Result & pw::Result< T >::operator= ( const Result< U > &  other)
inlineconstexpr

Creates a Result<T> through assignment from a Result<U>.

Precondition
These overloads only apply if Result<T> is constructible and assignable from Result<U> and Result<T> cannot be directly assigned from Result<U>.
Both Result<T> and pw::Result<U> are OK by assigning U to T directly.
Result<T> is OK and pw::Result<U> contains an error code by destroying the value of Result<T> and assigning from Result<U>.
Result<T> contains an error code and pw::Result<U> is OK by directly initializing T from U.
Both Result<T> and pw::Result<U> contain an error code by assigning the Status in Result<U> to Result<T>.

◆ operator=() [3/4]

template<typename T >
constexpr Result & pw::Result< T >::operator= ( Result< T > &&  )
constexprdefault

Result<T> is move assignable if T is move constructible and move assignable.

◆ operator=() [4/4]

template<typename T >
template<typename U = T, typename = typename std::enable_if<std::conjunction< std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>, std::disjunction< std::is_same<std::remove_cv_t<std::remove_reference_t<U>>, T>, std::conjunction< std::negation<std::is_convertible<U&&, Status>>, std::negation< internal_result::HasConversionOperatorToResult<T, U&&>>>>, internal_result::IsForwardingAssignmentValid<T, U&&>>::value>::type>
constexpr Result & pw::Result< T >::operator= ( U &&  v)
inlineconstexpr

Perfect-forwarding value assignment operator

If *this contains a T value before the call, the contained value is assigned from std::forward<U>(v). Otherwise, it is directly initialized from std::forward<U>(v).

Precondition
std::is_constructible_v<T, U> is true.
std::is_assignable_v<T&, U> is true.
std::is_same_v<Result<T>, std::remove_cvref_t<U>> is false.
Assigning U to T is not ambiguous. If U is Result<V> and T is constructible and assignable from both Result<V> and V, the assignment is considered bug-prone and ambiguous and thus will fail to compile. For example:
Result<bool> s1 = true; // s1.ok() && *s1 == true
Result<bool> s2 = false; // s2.ok() && *s2 == false
s1 = s2; // ambiguous, `s1 = *s2` or `s1 = bool(s2)`?

◆ or_else()

template<typename T >
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, const Status&>, std::enable_if_t<!std::is_void_v< Ret >, int > = 0>
constexpr Result< T > pw::Result< T >::or_else ( Fn &&  function) const &
inlineconstexpr
Returns
A Result<T> if it has a value, otherwise it invokes the given function. The function must return a type convertible to a Result<T> or a void.

General template logic:

template <typename U>
requires std::is_convertible_v<U, Result<T>>
constexpr Result< T > or_else(Fn &&function) const &
Definition: result.h:647
Definition: status.h:120

Example:

Result<Foo> CreateFoo();
Result<Foo> foo = CreateFoo().or_else(
[](Status s) { PW_LOG_ERROR("Status: %d", s.code()); });
constexpr Code code() const
Definition: status.h:341

◆ status()

template<typename T >
constexpr Status pw::Result< T >::status
constexpr
Returns
The current pw::Status code contained within the Result<T>. If pw::Result<T> contains a T, then this function returns OK.

◆ transform()

template<typename T >
template<typename Fn , typename Ret = internal_result::InvokeResultType<Fn, T&>, std::enable_if_t< std::is_copy_constructible_v< Ret >, int > = 0>
constexpr Result< Ret > pw::Result< T >::transform ( Fn &&  function) &
inlineconstexpr
Returns
A Result<U> which contains the result of the invocation of the given function if *this contains a value. Otherwise, returns a Result<U> with the same Status as *this.

General template logic:

template <typename U>
constexpr Result< Ret > transform(Fn &&function) &
Definition: result.h:698

◆ value()

template<typename T >
constexpr const T & pw::Result< T >::value ( ) const &
constexpr
Returns
A reference to the held value if this->ok(). Otherwise, terminates the process.

If you have already checked the status using this->ok(), you probably want to use operator*() or operator->() to access the value instead of value.

For value types that are cheap to copy, prefer simple code:

T value = result.value();

Otherwise, if the value type is expensive to copy, but can be left in the Result<T>, simply assign to a reference:

T& value = result.value(); // or `const T&`

Otherwise, if the value type supports an efficient move, it can be used as follows:

T value = std::move(result).value();

The std::move on result instead of on the whole expression enables warnings about possible uses of the result object after the move.

◆ value_or()

template<typename T >
template<typename U >
constexpr T pw::Result< T >::value_or ( U &&  default_value) const &
constexpr
Returns
The current value if this->ok() == true. Otherwise constructs a value using the provided default_value.

Unlike value, this function returns by value, copying the current value if necessary. If the value type supports an efficient move, it can be used as follows:

T value = std::move(result).value_or(def);

Unlike with value, calling std::move() on the result of value_or will still trigger a copy.


The documentation for this class was generated from the following files: