expected lite: expected objects for C++11 and later
expected lite is a single-file header-only library for objects that either represent a valid value or an error that you can pass by value. It is intended for use with C++11 and later. The library is based on the std::expected proposal [1] .
Contents
- Example usage
- In a nutshell
- License
- Dependencies
- Installation
- Synopsis
- Comparison with like types
- Reported to work with
- Implementation notes
- Other implementations of expected
- Notes and references
- Appendix
Example usage
#include "nonstd/expected.hpp"
#include <cstdlib>
#include <iostream>
#include <string>
using namespace nonstd;
using namespace std::literals;
auto to_int( char const * const text ) -> expected<int, std::string>
{
char * pos = nullptr;
auto value = strtol( text, &pos, 0 );
if ( pos != text ) return value;
else return make_unexpected( "'"s + text + "' isn't a number" );
}
int main( int argc, char * argv[] )
{
auto text = argc > 1 ? argv[1] : "42";
auto ei = to_int( text );
if ( ei ) std::cout << "'" << text << "' is " << *ei << ", ";
else std::cout << "Error: " << ei.error();
}
Compile and run
prompt> g++ -std=c++14 -Wall -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe 123 && 01-basic.exe abc
'123' is 123, Error: 'abc' isn't a number
In a nutshell
expected lite is a single-file header-only library to represent value objects that either contain a valid value or an error. The library is a partly implementation of the proposal for std::expected [1,2,3] for use with C++11 and later.
Some Features and properties of expected lite are ease of installation (single header), default and explicit construction of an expected, construction and assignment from a value that is convertible to the underlying type, copy- and move-construction and copy- and move-assignment from another expected of the same type, testing for the presence of a value, operators for unchecked access to the value or the error (pointer or reference), value() and value_or() for checked access to the value, relational operators, swap() and various factory functions.
expected lite shares the approach to in-place tags with any-lite, optional-lite and with variant-lite and these libraries can be used together.
Not provided are reference-type expecteds. expected lite doesn't honour triviality of value and error types. expected lite doesn't handle overloaded address of operators.
For more examples, see [1].
License
expected lite is distributed under the Boost Software License.
Dependencies
expected lite has no other dependencies than the C++ standard library.
Installation
expected lite is a single-file header-only library. Put expected.hpp
directly into the project source tree or somewhere reachable from your project.
Synopsis
Contents
- Configuration
- Types in namespace nonstd
- Interface of expected
- Algorithms for expected
- Interface of unexpected_type
- Algorithms for unexpected_type
Configuration
Tweak header
If the compiler supports __has_include()
, expected lite supports the tweak header mechanism. Provide your tweak header as nonstd/expected.tweak.hpp
in a folder in the include-search-path. In the tweak header, provide definitions as documented below, like #define expected_CPLUSPLUS 201103L
.
Standard selection macro
-Dnsel_CPLUSPLUS=199711L
Define this macro to override the auto-detection of the supported C++ standard, or if your compiler does not set the __cplusplus
macro correctly.
std::expected
or nonstd::expected
Select At default, expected lite uses std::expected
if it is available and lets you use it via namespace nonstd
. You can however override this default and explicitly request to use std::expected
or expected lite's nonstd::expected
as nonstd::expected
via the following macros.
-Dnsel_CONFIG_SELECT_EXPECTED=nsel_EXPECTED_DEFAULT
Define this to nsel_EXPECTED_STD
to select std::expected
as nonstd::expected
. Define this to nsel_EXPECTED_NONSTD
to select nonstd::expected
as nonstd::expected
. Default is undefined, which has the same effect as defining to nsel_EXPECTED_DEFAULT
.
-Dnsel_P0323R=7 (default)
Define this to the proposal revision number to control the presence and behavior of features (see tables). Default is 7 for the latest revision.
Disable C++ exceptions
-Dnsel_CONFIG_NO_EXCEPTIONS=0
Define this to 1 if you want to compile without exceptions. If not defined, the header tries and detect if exceptions have been disabled (e.g. via -fno-exceptions
or /kernel
). Default determined in header.
Enable SEH exceptions
-Dnsel_CONFIG_NO_EXCEPTIONS_SEH=0
Define this to 1 or 0 to control the use of SEH when C++ exceptions are disabled (see above). If not defined, the header tries and detect if SEH is available if C++ exceptions have been disabled (e.g. via -fno-exceptions
or /kernel
). Default determined in header.
Enable compilation errors
-Dnsel_CONFIG_CONFIRMS_COMPILATION_ERRORS=0
Define this macro to 1 to experience the by-design compile-time errors of the library in the test suite. Default is 0.
Types in namespace nonstd
Purpose | Type | Note / Object |
---|---|---|
Expected | template<typename T, typename E = std::exception_ptr> class expected; |
nsel_P0323 <= 2 |
Expected | template<typename T, typename E> class expected; |
nsel_P0323 > 2 |
Error type | template<typename E> class unexpected_type; |
ย |
Error type | template<> class unexpected_type<std::exception_ptr>; |
nsel_P0323 <= 2 |
Error type | template<typename E> class unexpected; |
>= C++17 |
Traits | template<typename E> struct is_unexpected; |
nsel_P0323 <= 3 |
In-place value construction | struct in_place_t; | in_place_t in_place{}; |
In-place error construction | struct in_place_unexpected_t; | in_place_unexpected_t unexpect{}; |
In-place error construction | struct in_place_unexpected_t; | in_place_unexpected_t in_place_unexpected{}; |
Error reporting | class bad_expected_access; | ย |
Interface of expected
Kind | Method | Result |
---|---|---|
Construction | [constexpr] expected() noexcept(...) | an object with default value |
ย | [constexpr] expected( expected const & other ) | initialize to contents of other |
ย | [constexpr] expected( expected && other ) | move contents from other |
ย | [constexpr] expected( value_type const & value ) | initialize to value |
ย | [constexpr] expected( value_type && value ) noexcept(...) | move from value |
ย | [constexpr] explicit expected( in_place_t, Args&&... args ) | construct value in-place from args |
ย | [constexpr] explicit expected( in_place_t, โstd::initializer_list<U> il, Args&&... args ) |
construct value in-place from args |
ย | [constexpr] expected( unexpected_type const & error ) | initialize to error |
ย | [constexpr] expected( unexpected_type && error ) | move from error |
ย | [constexpr] explicit expected( in_place_unexpected_t, โArgs&&... args ) |
construct error in-place from args |
ย | [constexpr] explicit expected( in_place_unexpected_t, โstd::initializer_list<U> il, Args&&... args ) |
construct error in-place from args |
Destruction | ~expected() | destruct current content |
Assignment | expected operator=( expected const & other ) | assign contents of other; destruct current content, if any |
ย | expected & operator=( expected && other ) noexcept(...) | move contents of other |
ย | expected & operator=( U && v ) | move value from v |
ย | expected & operator=( unexpected_type const & u ) | initialize to unexpected |
ย | expected & operator=( unexpected_type && u ) | move from unexpected |
ย | template<typename... Args> void emplace( Args &&... args ) |
emplace from args |
ย | template<typename U, typename... Args> void emplace( std::initializer_list<U> il, Args &&... args ) |
emplace from args |
Swap | void swap( expected & other ) noexcept | swap with other |
Observers | constexpr value_type const * operator->() const | pointer to current content (const); must contain value |
ย | value_type * operator->() | pointer to current content (non-const); must contain value |
ย | constexpr value_type const & operator *() const & | the current content (const ref); must contain value |
ย | constexpr value_type && operator *() && | the current content (non-const ref); must contain value |
ย | constexpr explicit operator bool() const noexcept | true if contains value |
ย | constexpr has_value() const noexcept | true if contains value |
ย | constexpr value_type const & value() const & | current content (const ref); see note 1 |
ย | value_type & value() & | current content (non-const ref); see note 1 |
ย | constexpr value_type && value() && | move from current content; see note 1 |
ย | constexpr error_type const & error() const & | current error (const ref); must contain error |
ย | error_type & error() & | current error (non-const ref); must contain error |
ย | constexpr error_type && error() && | move from current error; must contain error |
ย | constexpr unexpected_type get_unexpected() const | the error as unexpected<>; must contain error |
ย | template<typename Ex> bool has_exception() const |
true of contains exception (as base) |
ย | value_type value_or( U && v ) const & | value or move from v |
ย | value_type value_or( U && v ) && | move from value or move from v |
ย | ... | ย |
Note 1: checked access: if no content, for std::exception_ptr rethrows error(), otherwise throws bad_expected_access(error()).
Algorithms for expected
Kind | Function |
---|---|
Comparison with expected | ย |
==โ!= | template<typename T1, typename E1, typename T2, typename E2> constexpr bool operator op( โexpected<T1,E1> const & x, โexpected<T2,E2> const & y ) |
Comparison with expected | nsel_P0323R <= 2 |
<โ>โ<=โ>= | template<typename T, typename E> constexpr bool operator op( โexpected<T,E> const & x, โexpected<T,E> const & y ) |
Comparison with unexpected_type | ย |
==โ!= | template<typename T1, typename E1, typename E2> constexpr bool operator op( โexpected<T1,E1> const & x, โunexpected_type<E2> const & u ) |
ย | template<typename T1, typename E1, typename E2> constexpr bool operator op( โunexpected_type<E2> const & u, โexpected<T1,E1> const & x ) |
Comparison with unexpected_type | nsel_P0323R <= 2 |
<โ>โ<=โ>= | template<typename T, typename E> constexpr bool operator op( โexpected<T,E> const & x, โunexpected_type<E> const & u ) |
ย | template<typename T, typename E> constexpr bool operator op( โunexpected_type<E> const & u, โexpected<T,E> const & x ) |
Comparison with T | ย |
==โ!= | template<typename T, typename E> constexpr bool operator op( โexpected<T,E> const & x, โT const & v ) |
ย | template<typename T, typename E> constexpr bool operator op( โT const & v, โexpected<T,E> const & x ) |
Comparison with T | nsel_P0323R <= 2 |
<โ>โ<=โ>= | template<typename T, typename E> constexpr bool operator op( โexpected<T,E> const & x, โT const & v ) |
ย | template<typename T, typename E> constexpr bool operator op( โT const & v, โexpected<T,E> const & x ) |
Specialized algorithms | ย |
Swap | template<typename T, typename E> void swap( โexpected<T,E> & x, โexpected<T,E> & y )โnoexcept( noexcept( x.swap(y) ) ) |
Make expected from | nsel_P0323R <= 3 |
โValue | template<typename T> constexpr auto make_expected( T && v ) -> โexpected< typename std::decay<T>::type> |
โNothing | auto make_expected() -> expected<void> |
โCurrent exception | template<typename T> constexpr auto make_expected_from_current_exception() -> expected<T> |
โException | template<typename T> auto make_expected_from_exception( std::exception_ptr v ) -> expected<T> |
โError | template<typename T, typename E> constexpr auto make_expected_from_error( E e ) -> โexpected<T, typename std::decay<E>::type> |
โCall | template<typename F> auto make_expected_from_call( F f ) -> โexpected< typename std::result_of<F()>::type> |
โCall, void specialization | template<typename F> auto make_expected_from_call( F f ) -> expected<void> |
Interface of unexpected_type
Kind | Method | Result |
---|---|---|
Construction | unexpected_type() = delete; | no default construction |
ย | constexpr explicit unexpected_type( E const & error ) | copy-constructed from an E |
ย | constexpr explicit unexpected_type( E && error ) | move-constructed from an E |
Observers | constexpr error_type const & value() const | can observe contained error |
ย | error_type & value() | can modify contained error |
Algorithms for unexpected_type
Kind | Function |
---|---|
Comparison with unexpected | ย |
==โ!= | template<typename E> constexpr bool operator op( โunexpected_type<E> const & x, โunexpected_type<E> const & y ) |
Comparison with unexpected | nsel_P0323R <= 2 |
<โ>โ<=โ>= | template<typename E> constexpr bool operator op( โunexpected_type<E> const & x, โunexpected_type<E> const & y ) |
Comparison with exception_ptr | ย |
==โ!= | constexpr bool operator op( โunexpected_type<std::exception_ptr> const & x, โunexpected_type<std::exception_ptr> const & y ) |
Comparison with exception_ptr | nsel_P0323R <= 2 |
<โ>โ<=โ>= | constexpr bool operator op( โunexpected_type<std::exception_ptr> const & x, โunexpected_type<std::exception_ptr> const & y ) |
Specialized algorithms | ย |
Make unexpected from | ย |
โError | template<typename E> [constexpr] auto make_unexpected( E && v) -> โunexpected_type< typename std::decay<E>::type> |
Make unexpected from | nsel_P0323R <= 3 |
โCurrent exception | [constexpr] auto make_unexpected_from_current_exception() -> โunexpected_type< std::exception_ptr> |
Comparison with like types
Feature | std::pair |
std:: optional | std:: expected | nonstd:: expected | Boost. Expected | Nonco expected | Andrei Expected | Hagan required |
---|---|---|---|---|---|---|---|---|
More information | see [14] | see [5] | see [1] | this work | see [4] | see [7] | see [8] | see [13] |
C++03 | yes | no | no | no/not yet | no (union) | no | no | yes |
C++11 | yes | no | no | yes | yes | yes | yes | yes |
C++14 | yes | no | no | yes | yes | yes | yes | yes |
C++17 | yes | yes | no | yes | yes | yes | yes | yes |
DefaultConstructible | T param | yes | yes | yes | yes | no | no | no |
In-place construction | no | yes | yes | yes | yes | yes | no | no |
Literal type | yes | yes | yes | yes | yes | no | no | no |
Disengaged information | possible | no | yes | yes | yes | yes | yes | no |
Vary disengaged type | yes | no | yes | yes | yes | no | no | no |
Engaged nonuse throws | no | no | no | no | error_traits | no | no | yes |
Disengaged use throws | no | yes, value() | yes, value() | yes, value() | yes, value() |
yes, get() |
yes, get() |
n/a |
Proxy (rel.ops) | no | yes | yes | yes | yes | no | no | no |
References | no | yes | no/not yet | no/not yet | no/not yet | yes | no | no |
Chained visitor(s) | no | no | yes | maybe | yes | no | no | no |
Note 1: std::experimental::expected
Note 2: sources for Nonco expected, Andrei Expected and Hagan required can befound in the spike-expected repository.
Reported to work with
TBD
Implementation notes
TBD
Other implementations of expected
- Simon Brand. C++11/14/17 std::expected with functional-style extensions. Single-header.
- Isabella Muerte. MNMLSTC Core (C++11).
- Vicente J. Botet Escriba. stdmake's expected (C++17).
- Facebook. Folly's Expected.h (C++14).
Notes and references
[1] Vicente J. Botet Escriba. p0323 - A proposal to add a utility class to represent expected object (latest) (HTML). (r12, r11, r10, r9, r8, r7, r6, r5, r4, r3, r2, r1, r0, draft).
[2] Vicente J. Botet Escriba. JASEL: Just a simple experimental library for C++. Reference implementation of expected.
[3] Vicente J. Botet Escriba. Expected - An exception-friendly Error Monad. C++Now 2014. 24 September 2014.
[4] Pierre Talbot. Boost.Expected. Unofficial Boost candidate. 5 May 2013. GitHub, GSoC 2013 Proposal, [email protected].
[5] Fernando Cacciola and Andrzej Krzemieลski. A proposal to add a utility class to represent optional objects (Revision 4). ISO/IEC JTC1 SC22 WG21 N3672 2013-04-19.
[6] Andrzej Krzemieลski, Optional library implementation in C++11.
[7] Anto Nonco. Extending expected to deal with references. 27 May 2013.
[8] Andrei Alexandrescu. Systematic Error Handling in C++. Prepared for The C++and Beyond Seminar 2012. Video. Slides.
[9] Andrei Alexandrescu. Choose your Poison: Exceptions or Error Codes? (PDF). ACCU Conference 2007.
[10] Andrei Alexandrescu. The Power of None (PPT). Northwest C++ Users' Group. May 17th, 2006.
[11] Jon Jagger. A Return Type That Doesn't Like Being Ignored. Overload issue 53, February 2003.
[12] Andrei Alexandrescu. Error Handling in C++: Are we inching towards a total solution?. ACCU Conference 2002.
[13] Ken Hagan et al. Exploding return codes. comp.lang.c++.moderated. 11 February 2000.
[14] std::pair. cppreference.com
[15] Niall Douglas. Outcome. Very lightweight outcome<T> and result<T> (non-Boost edition).
[16] Niall Douglas. p0762 - Concerns about expected<T, E> from the Boost.Outcome peer review. 15 October 2017.
Appendix
A.1 Compile-time information
The version of expected lite is available via tag [.version]
. The following tags are available for information on the compiler and on the C++ standard library used: [.compiler]
, [.stdc++]
, [.stdlanguage]
and [.stdlibrary]
.
A.2 Expected lite test specification
click to expand
unexpected_type: Disallows default construction
unexpected_type: Allows to copy-construct from unexpected_type, default
unexpected_type: Allows to move-construct from unexpected_type, default
unexpected_type: Allows to in-place-construct
unexpected_type: Allows to in-place-construct from initializer_list
unexpected_type: Allows to copy-construct from error_type
unexpected_type: Allows to move-construct from error_type
unexpected_type: Allows to copy-construct from unexpected_type, explicit converting
unexpected_type: Allows to copy-construct from unexpected_type, non-explicit converting
unexpected_type: Allows to move-construct from unexpected_type, explicit converting
unexpected_type: Allows to move-construct from unexpected_type, non-explicit converting
unexpected_type: Allows to copy-assign from unexpected_type, default
unexpected_type: Allows to move-assign from unexpected_type, default
unexpected_type: Allows to copy-assign from unexpected_type, converting
unexpected_type: Allows to move-assign from unexpected, converting
unexpected_type: Allows to observe its value via a l-value reference
unexpected_type: Allows to observe its value via a r-value reference
unexpected_type: Allows to modify its value via a l-value reference
unexpected_type: Allows to be swapped
unexpected_type<std::exception_ptr>: Disallows default construction
unexpected_type<std::exception_ptr>: Allows to copy-construct from error_type
unexpected_type<std::exception_ptr>: Allows to move-construct from error_type
unexpected_type<std::exception_ptr>: Allows to copy-construct from an exception
unexpected_type<std::exception_ptr>: Allows to observe its value
unexpected_type<std::exception_ptr>: Allows to modify its value
unexpected_type: Provides relational operators
unexpected_type: Provides relational operators, std::exception_ptr specialization
make_unexpected(): Allows to create an unexpected_type<E> from an E
unexpected: C++17 and later provide unexpected_type as unexpected
bad_expected_access: Disallows default construction
bad_expected_access: Allows construction from error_type
bad_expected_access: Allows to observe its error
bad_expected_access: Allows to change its error
bad_expected_access: Provides non-empty what()
expected: Allows to default construct
expected: Allows to copy-construct from expected: value
expected: Allows to copy-construct from expected: error
expected: Allows to move-construct from expected: value
expected: Allows to move-construct from expected: error
expected: Allows to copy-construct from expected; value, explicit converting
expected: Allows to copy-construct from expected; error, explicit converting
expected: Allows to copy-construct from expected; value, non-explicit converting
expected: Allows to copy-construct from expected; error, non-explicit converting
expected: Allows to move-construct from expected; value, explicit converting
expected: Allows to move-construct from expected; error, explicit converting
expected: Allows to move-construct from expected; value, non-explicit converting
expected: Allows to move-construct from expected; error, non-explicit converting
expected: Allows to forward-construct from value, explicit converting
expected: Allows to forward-construct from value, non-explicit converting
expected: Allows to in-place-construct value
expected: Allows to in-place-construct value from initializer_list
expected: Allows to copy-construct from unexpected, explicit converting
expected: Allows to copy-construct from unexpected, non-explicit converting
expected: Allows to move-construct from unexpected, explicit converting
expected: Allows to move-construct from unexpected, non-explicit converting
expected: Allows to in-place-construct error
expected: Allows to in-place-construct error from initializer_list
expected: Allows to copy-assign from expected, value
expected: Allows to copy-assign from expected, error
expected: Allows to move-assign from expected, value
expected: Allows to move-assign from expected, error
expected: Allows to forward-assign from value
expected: Allows to copy-assign from unexpected
expected: Allows to move-assign from unexpected
expected: Allows to move-assign from move-only unexpected
expected: Allows to emplace value
expected: Allows to emplace value from initializer_list
expected: Allows to be swapped
expected: Allows to observe its value via a pointer
expected: Allows to observe its value via a pointer to constant
expected: Allows to modify its value via a pointer
expected: Allows to observe its value via a l-value reference
expected: Allows to observe its value via a r-value reference
expected: Allows to modify its value via a l-value reference
expected: Allows to modify its value via a r-value reference
expected: Allows to observe if it contains a value (or error)
expected: Allows to observe its value
expected: Allows to modify its value
expected: Allows to move its value
expected: Allows to observe its error
expected: Allows to modify its error
expected: Allows to move its error
expected: Allows to observe its error as unexpected
expected: Allows to query if it contains an exception of a specific base type
expected: Allows to observe its value if available, or obtain a specified value otherwise
expected: Allows to move its value if available, or obtain a specified value otherwise
expected: Throws bad_expected_access on value access when disengaged
expected<void>: Allows to default-construct
expected<void>: Allows to copy-construct from expected<void>: value
expected<void>: Allows to copy-construct from expected<void>: error
expected<void>: Allows to move-construct from expected<void>: value
expected<void>: Allows to move-construct from expected<void>: error
expected<void>: Allows to in-place-construct
expected<void>: Allows to copy-construct from unexpected, explicit converting
expected<void>: Allows to copy-construct from unexpected, non-explicit converting
expected<void>: Allows to move-construct from unexpected, explicit converting
expected<void>: Allows to move-construct from unexpected, non-explicit converting
expected<void>: Allows to in-place-construct unexpected_type
expected<void>: Allows to in-place-construct error from initializer_list
expected<void>: Allows to copy-assign from expected, value
expected<void>: Allows to copy-assign from expected, error
expected<void>: Allows to move-assign from expected, value
expected<void>: Allows to move-assign from expected, error
expected<void>: Allows to emplace value
expected<void>: Allows to be swapped
expected<void>: Allows to observe if it contains a value (or error)
expected<void>: Allows to observe its value
expected<void>: Allows to observe its error
expected<void>: Allows to modify its error
expected<void>: Allows to move its error
expected<void>: Allows to observe its error as unexpected
expected<void>: Allows to query if it contains an exception of a specific base type
expected<void>: Throws bad_expected_access on value access when disengaged
operators: Provides expected relational operators
operators: Provides expected relational operators (void)
swap: Allows expected to be swapped
std::hash: Allows to compute hash value for expected
tweak header: reads tweak header if supported [tweak]