|
JSON Voorhees
Killer JSON for C++
|
A type that represents a value which might be a success (is_ok) or a failure (is_error).
More...
#include <jsonv/result.hpp>
Inheritance diagram for jsonv::result< TValue, TError >:
Collaboration diagram for jsonv::result< TValue, TError >:Public Types | |
| using | value_type = TValue |
The type this result holds when it is result_state::ok. It is allowed to be void. | |
| using | error_type = TError |
The type this result holds when it is result_state::error. It is allowed to be void. | |
Public Types inherited from jsonv::result_value_operations< TSelf, TValue > | |
| using | value_type = TValue |
| See result::value_type. | |
Public Types inherited from jsonv::result_error_operations< TSelf, TError > | |
| using | error_type = TError |
| See result::error_type. | |
Public Member Functions | |
| constexpr | result () noexcept |
| Construct an empty result. | |
| template<typename... TArgs> | |
| constexpr | result (in_place_ok_t, TArgs &&... args) noexcept(std::is_nothrow_constructible_v< value_type, TArgs... >) |
Construct a result with result_state::ok from the provided args. | |
| template<typename TOkValue > | |
| constexpr | result (TOkValue &&value, std::enable_if_t< detail::is_template_of_v< ok, std::decay_t< TOkValue > > &&std::is_convertible_v< typename TOkValue::value_type, value_type > > *=nullptr) |
Construct a result with result_state::ok from the contents of value. | |
| template<typename TOkValue > | |
| constexpr | result (TOkValue &&value, std::enable_if_t< std::is_same_v< ok< void >, std::decay_t< TOkValue > > &&std::is_void_v< value_type > > *=nullptr) noexcept |
Construct a result with result_state::ok from a ok<void>. | |
| template<typename UValue > | |
| constexpr | result (UValue &&value, std::enable_if_t< std::is_convertible_v< UValue, value_type > &&!std::is_same_v< value_type, error_type > > *=nullptr) noexcept(std::is_nothrow_constructible_v< value_type, UValue >) |
Construct a result with result_state::ok from the provided value. | |
| template<typename... TArgs> | |
| constexpr | result (in_place_error_t, TArgs &&... args) noexcept(std::is_nothrow_constructible_v< error_type, TArgs... >) |
Construct a result with result_state::error from the provided args. | |
| template<typename TErrorValue > | |
| constexpr | result (TErrorValue &&error_value, std::enable_if_t< detail::is_template_of_v< jsonv::error, TErrorValue > &&std::is_convertible_v< typename TErrorValue::error_type, error_type > > *=nullptr) |
Construct a result with result_state::error from the contents of error_value. | |
| template<typename TErrorValue > | |
| constexpr | result (TErrorValue &&, std::enable_if_t< std::is_same_v< jsonv::error< void >, std::decay_t< TErrorValue > > &&std::is_void_v< error_type > > *=nullptr) noexcept |
Construct a result with result_state::error from an error<void>. | |
| constexpr | result (const result &)=default |
Result instances are copy-constructible iff value_type and error_type are. | |
| constexpr | result (result &&) noexcept=default |
Result instances are move-constructible iff value_type and error_type are. | |
| constexpr result & | operator= (const result &)=default |
Result instances are copy-assignable iff value_type and error_type are. | |
| constexpr result & | operator= (result &&) noexcept=default |
Result instances are move-assignable iff value_type and error_type are. | |
| template<typename UValue , typename UError , typename = std::enable_if_t< std::is_convertible_v<UValue, value_type> && std::is_convertible_v<UError, error_type> >> | |
| result (const result< UValue, UError > &src) | |
Converting constructor from a result with a convertible value_type and error_type. | |
| template<typename UValue , typename UError , typename = std::enable_if_t< std::is_convertible_v<UValue, value_type> && std::is_convertible_v<UError, error_type> >> | |
| result (result< UValue, UError > &&src) noexcept | |
Converting constructor from a result with a convertible value_type and error_type. | |
| constexpr result_state | state () const noexcept |
| Get the state of this object. | |
| constexpr bool | is_ok () const noexcept |
Check that this result has state of result_state::ok. | |
| constexpr | operator bool () const noexcept |
Check that this result is_ok. | |
| constexpr bool | is_error () const noexcept |
Check that this result has state of result_state::error. | |
| void | reset () noexcept(std::is_nothrow_destructible_v< value_type > &&std::is_nothrow_destructible_v< error_type >) |
Reset the contents of this instance to result_state::empty. | |
| template<typename FUnary , typename ResultInfo = detail::result_map_result_info<const self_type&, FUnary>> | |
| ResultInfo::type | map (FUnary &&transform) const &noexcept(ResultInfo::is_noexcept) |
| template<typename FUnary , typename ResultInfo = detail::result_map_result_info<self_type&&, FUnary>> | |
| ResultInfo::type | map (FUnary &&transform) &&noexcept(ResultInfo::is_noexcept) |
Apply transform to a result_state::ok value, returning the result of the transformation in another result. | |
| template<typename FUnary , typename ResultInfo = detail::result_map_flat_result_info<const self_type&, FUnary>> | |
| ResultInfo::type | map_flat (FUnary &&transform) const &noexcept(ResultInfo::is_noexcept) |
| template<typename FUnary , typename ResultInfo = detail::result_map_flat_result_info<self_type&&, FUnary>> | |
| ResultInfo::type | map_flat (FUnary &&transform) &&noexcept(ResultInfo::is_noexcept) |
Apply transform to a result_state::ok value, returning the result of the transformation, which itself is a result. | |
| template<typename FUnary , typename ResultInfo = detail::result_map_error_result_info<const self_type&, FUnary>> | |
| ResultInfo::type | map_error (FUnary &&transform) const &noexcept(ResultInfo::is_noexcept) |
| template<typename FUnary , typename ResultInfo = detail::result_map_error_result_info<self_type&&, FUnary>> | |
| ResultInfo::type | map_error (FUnary &&transform) &&noexcept(ResultInfo::is_noexcept) |
Apply transform to a result_state::error value, returning the result of the transformation in another result. | |
| template<typename FUnary , typename ResultInfo = detail::result_recover_result_info<const self_type&, FUnary>> | |
| ResultInfo::type | recover (FUnary &&recovery) const &noexcept(ResultInfo::is_noexcept) |
| template<typename FUnary , typename ResultInfo = detail::result_recover_flat_result_info<const self_type&, FUnary>> | |
| ResultInfo::type | recover_flat (FUnary &&recovery) const &noexcept(ResultInfo::is_noexcept) |
| template<typename FUnary , typename ResultInfo = detail::result_recover_flat_result_info<self_type&&, FUnary>> | |
| ResultInfo::type | recover_flat (FUnary &&recovery) &&noexcept(ResultInfo::is_noexcept) |
Apply recovery to a result_state::error value, returning the result of the transformation, which itself is a result. | |
Public Member Functions inherited from jsonv::result_value_operations< TSelf, TValue > | |
| const value_type & | value () const & |
| value_type & | value () & |
| value_type && | value () && |
| template<typename UValue > | |
| constexpr value_type | value_or (UValue &&recovery_value) const &noexcept(std::is_nothrow_copy_constructible_v< value_type > &&std::is_nothrow_constructible_v< value_type, UValue >) |
| template<typename UValue > | |
| constexpr value_type | value_or (UValue &&recovery_value) &&noexcept(std::is_nothrow_move_constructible_v< value_type > &&std::is_nothrow_constructible_v< value_type, UValue >) |
| const value_type & | operator* () const & |
| value_type & | operator* () & |
| value_type && | operator* () && |
| const value_type * | operator-> () const |
| value_type * | operator-> () |
Public Member Functions inherited from jsonv::result_error_operations< TSelf, TError > | |
| const error_type & | error () const & |
| error_type & | error () & |
| error_type && | error () && |
Friends | |
| template<typename , typename > | |
| class | result_value_operations |
| template<typename , typename > | |
| class | result_error_operations |
A type that represents a value which might be a success (is_ok) or a failure (is_error).
It is similar to std::variant, but additional meaning is attached to the values, even if TValue and TError are the same type. Returning a result should be seen as an alternative to exception-throwing or the use of errno. While it is legal to throw from code which uses result, it is discouraged (see Comparison to Other Error Handling Techniques for more information).
Instances are transformed through member functions:
ok result into another ok with a different type.ok result into either an ok with a different type or an error with the same type.error result into another error with a different type.error result into an ok result with the same type.error result into either an ok of the same type or an error of a different type.A result instance is created with the ok and error helper types from some function performing an operation which might fail.
Program output:
The sqrt_checked function will give us \( \sqrt{x} \) when \( x \ge 0 \) and a math_error::domain error when \( x < 0 \). What if we wanted to make a sqrt_div_2_checked function to compute \( \frac{\sqrt{x}}{2} \) using the result of sqrt_checked? While we could use is_ok to check if we should apply a /2 operation, this is easier to do with map.
Program output:
The function provided to map will not fail, as division by 2 works on all possible double values. But what if we try to compute \( \arcsin\sqrt{x} \)? The std::asin function requires input with range \( \left[ -1, 1 \right] \), so let's write a checked version of it:
Program output:
Use of result for communication of operations which might fail is one of many error-handling techniques available in C++. It is used in cases where there is no reasonable fallback value to return (in contrast to jsonv::value::find).
Exceptions are a common mechanism to represent error cases, but are not always the ideal choice. While code relying on exceptions is fast in the normal case, throwing an exception is much slower. As such, exception-throwing works best in situations where errors are relatively rare. In cases where errors occur at a high or unknown rate (such as parsing or extraction), techniques that do not pay a massive penalty to communicate errors are preferable.
One technique is returning a value (such as a bool or a dedicated error code) to indicate operation error. The use of the C++ attribute [[nodiscard]] helps prevent the common issue of forgetting to check the result.
Unfortunately, this technique only works when output_type has a default constructor so calling code can create an initial value for output. Furthermore, output_type must be mutable or assignable-to so implementing code can populate output. There is also a question as to what should happen if output is passed as a non-empty value (such as a std::vector which already has values). Should that be an error, should output be concatenated, or should it be overwritten?
To address all these questions, we could have foo return either an optional or variant.
This solves a lot of the issues with the code-returning function. Aside from forcing callers to use the slightly awkward std::get to access the result, this is close to ideal. Problems arise when either output_type or error_type is void, as a variant can not store a void. In these cases, we could rely on optional to represent the value or error, with nullopt indicating failure when error_type is void or success when output_type is void. If they are both void, use bool.
std::variant<output_type, error_type> in cases where both output_type and error_type are not void std::optional<output_type> in cases where output_type is not void, but error_type isstd::optional<error_type> in cases where output_type is void, but error_type is notbool in case where both output_type and error_type are void This all works fine if we are writing a non-generic foo, but what if the output_type or error_type of foo is dependent on some other generic function? The type signatures for doing this can get a bit unwieldy:
Programmers now have a harder time figuring out what foo actually returns. Code relying this also needs to handle this strange return type. Their usage is quite different to, as std::variant is accessed through the std::get free function while std::optional has a member get and operator bool overload. The potential for accidental misuse is quite high.
A result is meant to address all of the representational problems with error codes.
The signature is quite a bit simpler.
JSON Voorhees uses a mixture of error-handling techniques depending on the situation and the nature of the error. For cases where errors happen with unknown frequency, a result is used.
Definition at line 1088 of file result.hpp.
| using jsonv::result< TValue, TError >::error_type = TError |
The type this result holds when it is result_state::error. It is allowed to be void.
Definition at line 1108 of file result.hpp.
| using jsonv::result< TValue, TError >::value_type = TValue |
The type this result holds when it is result_state::ok. It is allowed to be void.
Definition at line 1105 of file result.hpp.
|
inlineconstexprnoexcept |
Construct an empty result.
Definition at line 1112 of file result.hpp.
|
inlineexplicitconstexprnoexcept |
Construct a result with result_state::ok from the provided args.
Definition at line 1118 of file result.hpp.
|
inlineconstexpr |
Construct a result with result_state::ok from the contents of value.
This constructor is only enabled when TOkValue is an ok with a value_type convertible to this result's value_type.
Definition at line 1126 of file result.hpp.
|
inlineconstexprnoexcept |
Construct a result with result_state::ok from a ok<void>.
This constructor is only enabled when TOkValue is an ok<void> and this result has value_type of void.
Definition at line 1137 of file result.hpp.
|
inlineconstexprnoexcept |
Construct a result with result_state::ok from the provided value.
This constructor is only enabled when UValue is convertible to value_type and value_type is distinct from error_type.
Definition at line 1149 of file result.hpp.
|
inlineexplicitconstexprnoexcept |
Construct a result with result_state::error from the provided args.
Definition at line 1160 of file result.hpp.
|
inlineconstexpr |
Construct a result with result_state::error from the contents of error_value.
This constructor is only enabled when TErrorValue is an error with a error_type convertible to this result's error_type.
Definition at line 1168 of file result.hpp.
|
inlineconstexprnoexcept |
Construct a result with result_state::error from an error<void>.
This constructor is only enabled when TErrorValue is an error<void> and this result has error_type of void.
Definition at line 1179 of file result.hpp.
|
inline |
Converting constructor from a result with a convertible value_type and error_type.
Definition at line 1207 of file result.hpp.
|
inlinenoexcept |
Converting constructor from a result with a convertible value_type and error_type.
Definition at line 1244 of file result.hpp.
|
inlineconstexprnoexcept |
Check that this result has state of result_state::error.
Definition at line 1299 of file result.hpp.
|
inlineconstexprnoexcept |
Check that this result has state of result_state::ok.
Definition at line 1287 of file result.hpp.
|
inlinenoexcept |
Apply transform to a result_state::ok value, returning the result of the transformation in another result.
If the instance is result_state::error, the error is moved into the returned value. If the instance is result_state::empty, an empty instance will be returned.
This function is noexcept when calling transform is noexcept, the result is noexcept move-constructible, and the error_type is noexcept move-constructible. It is highly encouraged to not throw from your transform function.
| FUnary | R (*)(value_type&&) if value_type is not void or R (*)() if it is. The inferred type R will be the value_type of the returned result (which can be void). |
Definition at line 1425 of file result.hpp.
|
inlinenoexcept |
Apply transform to a result_state::ok value, returning the result of the transformation in another result. If the instance is result_state::error, the error is copied into the returned value. If the instance is result_state::empty, an empty instance will be returned.
This function is noexcept when calling transform is noexcept, the result is noexcept move-constructible, and the error_type is noexcept copy-constructible. It is highly encouraged to not throw from your transform function.
| FUnary | R (*)(const value_type&) if value_type is not void or R (*)() if it is. The inferred type R will be the value_type of the returned result (which can be void). |
Definition at line 1348 of file result.hpp.
|
inlinenoexcept |
Apply transform to a result_state::error value, returning the result of the transformation in another result.
If the instance is result_state::ok, the value is moved into the returned value. If the instance is result_state::empty, an empty instance will be returned.
This function is noexcept when calling transform is noexcept, the result is noexcept move-constructible, and the value_type is noexcept move-constructible. It is highly encouraged to not throw from your transform function.
| FUnary | E (*)(error_type&&) if error_type is not void or E (*)() if it is. The inferred type E will be the error_type of the returned result (which can be void). |
Definition at line 1710 of file result.hpp.
|
inlinenoexcept |
Apply transform to a result_state::error value, returning the result of the transformation in another result. If the instance is result_state::ok, the value is copied into the returned value. If the instance is result_state::empty, an empty instance will be returned.
This function is noexcept when calling transform is noexcept, the result is noexcept move-constructible, and the value_type is noexcept copy-constructible. It is highly encouraged to not throw from your transform function.
| FUnary | E (*)(const error_type&) if error_type is not void or E (*)() if it is. The inferred type E will be the error_type of the returned result (which can be void). |
Definition at line 1632 of file result.hpp.
|
inlinenoexcept |
Apply transform to a result_state::ok value, returning the result of the transformation, which itself is a result.
If the instance is result_state::error, the error is moved into the returned value. If the instance is result_state::empty, an empty instance will be returned.
This function is noexcept when calling transform is noexcept, the result is noexcept move-constructible, and the error_type of the returned type is noexcept constructible from this instance's error_type. It is highly encouraged to not throw from your transform function.
| FUnary | result<R, E> (*)(value_type&&) if value_type is not void or result<R, E> (*)() if it is. Type E in the result must have common type with error_type. |
Definition at line 1569 of file result.hpp.
|
inlinenoexcept |
Apply transform to a result_state::ok value, returning the result of the transformation, which itself is a result. If the instance is result_state::error, the error is copied into the returned value. If the instance is result_state::empty, an empty instance will be returned.
This function is noexcept when calling transform is noexcept, the result is noexcept move-constructible, and the error_type of the returned type is noexcept constructible from this instance's error_type. It is highly encouraged to not throw from your transform function.
| FUnary | result<R, E> (*)(const value_type&) if value_type is not void or result<R, E> (*)() if it is. Type E in the result must have common type with error_type. |
Definition at line 1508 of file result.hpp.
|
inlineexplicitconstexprnoexcept |
Check that this result is_ok.
Definition at line 1293 of file result.hpp.
|
inlinenoexcept |
Apply recovery to a result_state::error value, returning the result of the transformation if it returns an optional instance which has a value. If recovery returns nullopt, this instance's error is copied to the returned value. If this instance is result_state::ok, recovery is not called and the current value is copied to the result. If the instance is result_state::empty, an empty instance will be returned.
This function is noexcept when calling transform is noexcept, the error_type is noexcept copy constructible, and the common type of value_type and the recovery function's value_type is noexcept copy constructible from value_type and noexcept move constructible from the recovery function's value_type. It is highly recommended to not throw from your recovery function.
| recovery | The function to call in an attempt to recover from an error. This function returns a valued optional instance to indicate the error has been recovered from with the value to recover to. Returning a nullopt indicates the error was not recovered from. |
| FUnary | optional<R> (*)(const error_type&) if error_type is not void or optional<R> (*)() if it is. The type R in the result must have common type with value_type if value_type is not void and must be ok<void> if it is. |
&& qualified overload of recover. In result_state::error cases, the error_type instance would be moved into the recovery function, which should be passed back in cases where the error could not be recovered from. This would require the signature for FUnary to be something which could return either a value or an error, which is covered by recover_flat. Definition at line 1805 of file result.hpp.
|
inlinenoexcept |
Apply recovery to a result_state::error value, returning the result of the transformation, which itself is a result.
If the instance is result_state::ok, the value is copied into the returned value. If the instance is result_state::empty, an empty instance will be returned.
This function is noexcept when calling recovery is noexcept, the result is noexcept move-constructible, and the value_type of the returned type is noexcept constructible from this instance's value_type. It is highly encouraged to not throw from your recovery function.
| FUnary | result<R, E> (*)(error_type&&) if error_type is not void or result<R, E> (*)() if it is. Type R in the result must have common type with value_type. |
Definition at line 1960 of file result.hpp.
|
inlinenoexcept |
Apply recovery to a result_state::error value, returning the result of the transformation, which itself is a result. If the instance is result_state::ok, the value is copied into the returned value. If the instance is result_state::empty, an empty instance will be returned.
This function is noexcept when calling recovery is noexcept, the result is noexcept move-constructible, and the value_type of the returned type is noexcept constructible from this instance's value_type. It is highly encouraged to not throw from your recovery function.
| FUnary | result<R, E> (*)(const error_type&) if error_type is not void or result<R, E> (*)() if it is. Type R in the result must have common type with value_type. |
Definition at line 1892 of file result.hpp.
|
inlinenoexcept |
Reset the contents of this instance to result_state::empty.
Definition at line 1305 of file result.hpp.
|
inlineconstexprnoexcept |
Get the state of this object.
Definition at line 1276 of file result.hpp.
Definition at line 1095 of file result.hpp.
|
friend |
Definition at line 1101 of file result.hpp.
|
friend |
Definition at line 1098 of file result.hpp.