C++11/14/17 std::expected with functional-style extensions

expected

Single header implementation of std::expected with functional-style extensions.

Documentation Status Clang + GCC: Linux Build Status MSVC: Windows Build Status

Available on Vcpkg and Conan.

std::expected is proposed as the preferred way to represent object which will either have an expected value, or an unexpected value giving information about why something failed. Unfortunately, chaining together many computations which may fail can be verbose, as error-checking code will be mixed in with the actual programming logic. This implementation provides a number of utilities to make coding with expected cleaner.

For example, instead of writing this code:

std::expected<image,fail_reason> get_cute_cat (const image& img) {
    auto cropped = crop_to_cat(img);
    if (!cropped) {
      return cropped;
    }

    auto with_tie = add_bow_tie(*cropped);
    if (!with_tie) {
      return with_tie;
    }

    auto with_sparkles = make_eyes_sparkle(*with_tie);
    if (!with_sparkles) {
       return with_sparkles;
    }

    return add_rainbow(make_smaller(*with_sparkles));
}

You can do this:

tl::expected<image,fail_reason> get_cute_cat (const image& img) {
    return crop_to_cat(img)
           .and_then(add_bow_tie)
           .and_then(make_eyes_sparkle)
           .map(make_smaller)
           .map(add_rainbow);
}

The interface is the same as std::expected as proposed in p0323r3, but the following member functions are also defined. Explicit types are for clarity.

  • map: carries out some operation on the stored object if there is one.
    • tl::expected<std::size_t,std::error_code> s = exp_string.map(&std::string::size);
  • map_error: carries out some operation on the unexpected object if there is one.
    • my_error_code translate_error (std::error_code);
    • tl::expected<int,my_error_code> s = exp_int.map_error(translate_error);
  • and_then: like map, but for operations which return a tl::expected.
    • tl::expected<ast, fail_reason> parse (const std::string& s);
    • tl::expected<ast, fail_reason> exp_ast = exp_string.and_then(parse);
  • or_else: calls some function if there is no value stored.
    • exp.or_else([] { throw std::runtime_error{"oh no"}; });

Compiler support

Tested on:

  • Linux
    • clang 6.0.1
    • clang 5.0.2
    • clang 4.0.1
    • clang 3.9
    • clang 3.8
    • clang 3.7
    • clang 3.6
    • clang 3.5
    • g++ 8.0.1
    • g++ 7.3
    • g++ 6.4
    • g++ 5.5
    • g++ 4.9
    • g++ 4.8
  • Windows
    • MSVC 2015
    • MSVC 2017

Acknowledgements

Thanks to Kévin Alexandre Boissonneault and Björn Fahller for various bug fixes.


CC0

To the extent possible under law, Sy Brand has waived all copyright and related or neighboring rights to the expected library. This work is published from: United Kingdom.

Comments
  • expected and polymorphism

    expected and polymorphism

    auto return_derived_type() -> expected<std::unique_ptr<derived_type>, std::error_code>
    {
        ....
    }
    auto test() -> expected<std::unique_ptr<basetype>, std::error_code>
    {
        // NO VIABLE OVERLOAD '='
        expected<std::unique_ptr<basetype>, std::error_code> res = do_something();
        ...
        return res;
    }
    

    Isn't there a way to convert an expected taking a derived-type unique_ptr to the basetype version?

  • Moving expected doesn't work for nested types

    Moving expected doesn't work for nested types

    It seems like this implementation of expected is unable to select the move constructor on GCC >= 5 for types that are more complex. While this compiles without issue:

    #include <expected.hpp>
    
    struct Foo {
        Foo() {}
        Foo(Foo&) = delete;
        Foo(Foo&&) {}
    };
    
    tl::expected<Foo, std::exception_ptr> bar() {
        Foo foo;
        return { std::move(foo) };
    }
    

    adding std::vector (which has move constructors as well) into the mix will make it fail on GCC >= 5. It compiles fine on Clang, and GCC 4.9.

    #include <expected.hpp>
    #include <vector>
    
    struct Foo {
        Foo(Foo&) = delete;
        Foo(Foo&&) {}
    };
    
    tl::expected<std::vector<Foo>, std::exception_ptr> bar() {
        std::vector<Foo> foos;
        return { std::move(foos) };
    }
    
  • and_then error when use void expected

    and_then error when use void expected

    tl::expected< void, std::string > voidtest() {
    	return {};
    }
    
    int main(int argc, char *argv[])
    {
    	voidtest()
    		.and_then( []() {} );
    }
    

    it's compile error ocurr ( win10, mingw 5.3 )

    src\main.cpp:28: error: no matching function for call to 'tl::expected<void, std::__cxx11::basic_string<char> >::and_then(main(int, char**)::<lambda()>)'
        .and_then( []() {} );
                           ^
    
  • GCC 5.5 doesn't work

    GCC 5.5 doesn't work

    GCC 5.5 seems to have a compiler bug which results in errors like this:

    <source>:2037:26: error: call of overloaded 'map(main()::<lambda(int)>&)' is ambiguous
    
         auto ret = e.map(mul2);
    
                              ^
    
    <source>:1163:52: note: candidate: constexpr auto tl::expected<T, E>::map(F&&) & [with F = main()::<lambda(int)>&; T = int; E = int]
    
       template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & {
    
                                                        ^
    
    <source>:1169:52: note: candidate: constexpr auto tl::expected<T, E>::map(F&&) && [with F = main()::<lambda(int)>&; T = int; E = int]
    
       template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && {
    
                                                        ^
    
    <source>:1175:37: note: candidate: constexpr auto tl::expected<T, E>::map(F&&) const & [with F = main()::<lambda(int)>&; T = int; E = int]
    
       template <class F> constexpr auto map(F &&f) const & {
    
                                         ^
    
    <source>:1181:37: note: candidate: constexpr auto tl::expected<T, E>::map(F&&) const && [with F = main()::<lambda(int)>&; T = int; E = int]
    
       template <class F> constexpr auto map(F &&f) const && {
    

    All other GCC 5 versions seem to work. I might need to not use ref-qualifiers for GCC5.5 or something.

  • Make exceptions optional

    Make exceptions optional

    It would be nice to have either a preprocessor option or a separate set of functions (unsafe_value()?) to disable throws in expected and invoke undefined behavior instead because this fails to compile with gcc and the -fno-exceptions flag. This makes it impossible to use in constrained environments like microcontrollers and other freestanding implementations where exceptions are either too expensive or simply not implemented.

    This can be implemented in standard C++ with the noreturn attribute which gcc and clang can take advantage of:

    template<typename E>
    [[noreturn]] constexpr void throw_exception(E &&e) {
    #ifndef DISABLE_EXCEPTIONS
        throw std::forward<E>(e);
    #endif
    }
    

    throw statements are legal in [[noreturn]] functions.

  • Failure to compile on glibc 2.34 and later

    Failure to compile on glibc 2.34 and later

    This project fails to compile on my machine, with this error:

    $ cmake -B build && cmake --build build --parallel 18
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /tmp/tmp.Qwf5IxQGCK/expected/build
    Consolidate compiler generated dependencies of target tl-expected-tests
    [  7%] Building CXX object CMakeFiles/tl-expected-tests.dir/tests/main.cpp.o
    In file included from /usr/include/signal.h:328,
                     from /tmp/tmp.Qwf5IxQGCK/expected/build/_deps/catch2-src/single_include/catch2/catch.hpp:7712,
                     from /tmp/tmp.Qwf5IxQGCK/expected/tests/main.cpp:2:
    /tmp/tmp.Qwf5IxQGCK/expected/build/_deps/catch2-src/single_include/catch2/catch.hpp:10453:58: error: call to non-‘constexpr’ function ‘long int sysconf(int)’
    10453 |     static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
          |                                                          ^~~~~~~~~~~
    In file included from /usr/include/bits/sigstksz.h:24:
    /usr/include/unistd.h:640:17: note: ‘long int sysconf(int)’ declared here
      640 | extern long int sysconf (int __name) __THROW;
          |                 ^~~~~~~
    /tmp/tmp.Qwf5IxQGCK/expected/build/_deps/catch2-src/single_include/catch2/catch.hpp:10512:45: error: size of array ‘altStackMem’ is not an integral constant-expression
    10512 |     char FatalConditionHandler::altStackMem[sigStackSize] = {};
          |                                             ^~~~~~~~~~~~
    gmake[2]: *** [CMakeFiles/tl-expected-tests.dir/build.make:174: CMakeFiles/tl-expected-tests.dir/tests/main.cpp.o] Error 1
    gmake[1]: *** [CMakeFiles/Makefile2:856: CMakeFiles/tl-expected-tests.dir/all] Error 2
    gmake: *** [Makefile:166: all] Error 2
    

    This appears to be caused by the fact that the version of Catch2 used in this repo, 2.9.2, expects to be able to use MINSIGSTKSZ as a constant expression, which breaks starting with glibc 2.34. According to Catch2's release notes, this is fixed in Catch2 2.13.5, so it would be nice if the dependency on Catch2 was updated to at least that version.

  • Compilation errors with GCC10

    Compilation errors with GCC10

    I'm trying to return a tl::expected<nlohmann::json, std::string> from a method in my class, but all that happens is compilation errors.

    I've tested each library in minimal test cases with zero issue, but when put together, I get errors.

    Here's a minimal example with the two together on godbolt

    And the long, long compilation errors

    In file included from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/move.h:57,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/nested_exception.h:40,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/exception:148,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/ios:39,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/ostream:38,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/iostream:39,
    
                     from <source>:1:
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits: In instantiation of 'struct std::is_constructible<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>':
    
    /opt/compiler-explorer/libs/nlohmann_json/v3.6.0/single_include/nlohmann/json.hpp:9384:79:   required by substitution of 'template<class ... Args, typename std::enable_if<std::is_constructible<nlohmann::basic_json<>, Args ...>::value, int>::type <anonymous> > nlohmann::detail::json_ref<nlohmann::basic_json<> >::json_ref(Args&& ...) [with Args = {const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&}; typename std::enable_if<std::is_constructible<nlohmann::basic_json<>, Args ...>::value, int>::type <anonymous> = <missing>]'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:901:30:   required from 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:930:12:   required from 'struct std::__is_copy_constructible_impl<nlohmann::basic_json<>, true>'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:936:12:   required from 'struct std::is_copy_constructible<nlohmann::basic_json<> >'
    
    https://raw.githubusercontent.com/TartanLlama/expected/master/include/tl/expected.hpp:1041:70:   required from 'class tl::expected<nlohmann::basic_json<>, std::__cxx11::basic_string<char> >'
    
    <source>:6:57:   required from here
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:906:12: error: invalid use of incomplete type 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
    
      906 |     struct is_constructible
    
          |            ^~~~~~~~~~~~~~~~
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:900:12: note: declaration of 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
    
      900 |     struct __is_constructible_impl
    
          |            ^~~~~~~~~~~~~~~~~~~~~~~
    
    In file included from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/vector:66,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/functional:62,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/pstl/glue_algorithm_defs.h:13,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/algorithm:74,
    
                     from /opt/compiler-explorer/libs/nlohmann_json/v3.6.0/single_include/nlohmann/json.hpp:37,
    
                     from <source>:3:
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h: In instantiation of '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >; _ForwardIterator = nlohmann::basic_json<>*]':
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h:325:37:   required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >; _ForwardIterator = nlohmann::basic_json<>*; _Tp = nlohmann::basic_json<>]'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_vector.h:558:31:   required from 'std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = nlohmann::basic_json<>; _Alloc = std::allocator<nlohmann::basic_json<> >]'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/ext/new_allocator.h:150:4:   required from 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; _Args = {const std::vector<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>, std::allocator<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer> > >&}; _Tp = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >]'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/alloc_traits.h:512:17:   required from 'static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; _Args = {const std::vector<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>, std::allocator<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer> > >&}; _Tp = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >]'
    
    /opt/compiler-explorer/libs/nlohmann_json/v3.6.0/single_include/nlohmann/json.hpp:13602:35:   required from 'static T* nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::create(Args&& ...) [with T = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; Args = {const std::vector<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>, std::allocator<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer> > >&}; ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer]'
    
    /opt/compiler-explorer/libs/nlohmann_json/v3.6.0/single_include/nlohmann/json.hpp:13754:36:   required from 'nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::json_value::json_value(const array_t&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::array_t = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >]'
    
    /opt/compiler-explorer/libs/nlohmann_json/v3.6.0/single_include/nlohmann/json.hpp:14539:25:   required from 'nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::basic_json(const nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer]'
    
    <source>:34:43:   required from here
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h:137:72: error: 'value' is not a member of 'std::is_constructible<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
    
      137 |       static_assert(is_constructible<_ValueType2, decltype(*__first)>::value,
    
          |                                                                        ^~~~~
    
    ASM generation compiler returned: 1
    
    In file included from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/move.h:57,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/nested_exception.h:40,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/exception:148,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/ios:39,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/ostream:38,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/iostream:39,
    
                     from <source>:1:
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits: In instantiation of 'struct std::is_constructible<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>':
    
    /opt/compiler-explorer/libs/nlohmann_json/v3.6.0/single_include/nlohmann/json.hpp:9384:79:   required by substitution of 'template<class ... Args, typename std::enable_if<std::is_constructible<nlohmann::basic_json<>, Args ...>::value, int>::type <anonymous> > nlohmann::detail::json_ref<nlohmann::basic_json<> >::json_ref(Args&& ...) [with Args = {const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&}; typename std::enable_if<std::is_constructible<nlohmann::basic_json<>, Args ...>::value, int>::type <anonymous> = <missing>]'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:901:30:   required from 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:930:12:   required from 'struct std::__is_copy_constructible_impl<nlohmann::basic_json<>, true>'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:936:12:   required from 'struct std::is_copy_constructible<nlohmann::basic_json<> >'
    
    https://raw.githubusercontent.com/TartanLlama/expected/master/include/tl/expected.hpp:1041:70:   required from 'class tl::expected<nlohmann::basic_json<>, std::__cxx11::basic_string<char> >'
    
    <source>:6:57:   required from here
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:906:12: error: invalid use of incomplete type 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
    
      906 |     struct is_constructible
    
          |            ^~~~~~~~~~~~~~~~
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/type_traits:900:12: note: declaration of 'struct std::__is_constructible_impl<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
    
      900 |     struct __is_constructible_impl
    
          |            ^~~~~~~~~~~~~~~~~~~~~~~
    
    In file included from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/vector:66,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/functional:62,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/pstl/glue_algorithm_defs.h:13,
    
                     from /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/algorithm:74,
    
                     from /opt/compiler-explorer/libs/nlohmann_json/v3.6.0/single_include/nlohmann/json.hpp:37,
    
                     from <source>:3:
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h: In instantiation of '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >; _ForwardIterator = nlohmann::basic_json<>*]':
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h:325:37:   required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >; _ForwardIterator = nlohmann::basic_json<>*; _Tp = nlohmann::basic_json<>]'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_vector.h:558:31:   required from 'std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = nlohmann::basic_json<>; _Alloc = std::allocator<nlohmann::basic_json<> >]'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/ext/new_allocator.h:150:4:   required from 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; _Args = {const std::vector<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>, std::allocator<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer> > >&}; _Tp = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >]'
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/alloc_traits.h:512:17:   required from 'static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; _Args = {const std::vector<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>, std::allocator<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer> > >&}; _Tp = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >]'
    
    /opt/compiler-explorer/libs/nlohmann_json/v3.6.0/single_include/nlohmann/json.hpp:13602:35:   required from 'static T* nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::create(Args&& ...) [with T = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >; Args = {const std::vector<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>, std::allocator<nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer> > >&}; ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer]'
    
    /opt/compiler-explorer/libs/nlohmann_json/v3.6.0/single_include/nlohmann/json.hpp:13754:36:   required from 'nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::json_value::json_value(const array_t&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer; nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::array_t = std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >]'
    
    /opt/compiler-explorer/libs/nlohmann_json/v3.6.0/single_include/nlohmann/json.hpp:14539:25:   required from 'nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::basic_json(const nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>&) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long int; NumberUnsignedType = long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer]'
    
    <source>:34:43:   required from here
    
    /opt/compiler-explorer/gcc-10.1.0/include/c++/10.1.0/bits/stl_uninitialized.h:137:72: error: 'value' is not a member of 'std::is_constructible<nlohmann::basic_json<>, const nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long int, long unsigned int, double, std::allocator, nlohmann::adl_serializer>&>'
    
      137 |       static_assert(is_constructible<_ValueType2, decltype(*__first)>::value,
    
          |                                                                        ^~~~~
    
    Execution build compiler returned: 1
    
  • Modify Conanfile to package expected as a header-only library

    Modify Conanfile to package expected as a header-only library

    Before describing the PR, I'd like to offer an alternative outcome for this PR: deletion of the Conanfile.

    Since submitting the Conanfile, I've had some time to reflect on what C++ packaging ought to be like, and I'm coming around to Robert Schumacher's thesis on packaging, e.g. that libraries should be packageable but not packaged. In line with that, I'd be happy to maintain a conan-expected repository elsewhere.

    That said, onto the PR:


    Previously, the Conanfile had the package ID depend on settings like the target architecture and compiler. expected's test executable depends on this information, but the library itself does not.

    This commit modifies the Conanfile as follows:

    • eliminates tests (not needed if we just want the library)
    • eliminates the influence of build settings on the package ID
  • Expected doesn't work with inheritance and unique_ptr

    Expected doesn't work with inheritance and unique_ptr

    A fairly serious bug:

    #include <memory>
    #include <system_error>
    #include <iostream>
    using namespace tl;
    
    struct parent {};
    struct child : public parent {};
    
    auto doit() -> expected<std::unique_ptr<child>, std::error_code>
    {
        return make_unexpected(std::error_code{});
    }
    
    int main ()
    {
        // Works
        expected<std::unique_ptr<child>, std::error_code> res1 = doit();
        if (res1) std::cout << "All OK\n"; else std::cout << "An error!" << std::endl;
        
        // Upcast removes the error!
        expected<std::unique_ptr<parent>, std::error_code> res2 = doit();
        if (res2) std::cout << "All OK\n"; else std::cout << "An error!" << std::endl;
        return 0;
    }
    

    Output:

    An error!
    All OK
    

    Expected output:

    An error!
    An error!
    
  • Is there a way to enforce a shared pointer copy, and never a move?

    Is there a way to enforce a shared pointer copy, and never a move?

    I'm using tl::expected<std::shared_ptr<MyType>, MyError> and want to ensure that the pointer is never moved, and only copied (for thread safety reasons). Is there something I can do about my type to enforce this so that I can work with the pointer in a thread-safe manner in other locations outside of the expected object? I'm running into a scenario where I believe the reference count is getting decremented elsewhere (in a library I don't control) while the pointer is getting moved by expected when I create the object.

    Specifically, I'm curious about the mechanics of tl::expected, and would like to avoid writing a wrapper class for std::shared_ptr that deletes the move constructor or something of similar effect. I would use a mutex, but don't have the ability to do so from within other libraries, only outside

  • Revamp CMake to be correct and easy to understand

    Revamp CMake to be correct and easy to understand

    :art: Cleanup include path for catch2

    :sparkles: Add basic .deb generation support WiX and RPM support is still needed (if even desired?)

    :arrow_up: Upgrade minimum CMake version to 3.14 :arrow_up: Upgrade Catch to Catch v2.9.2 :pushpin: Pin Catch to v2.9.2

    These changes come courtesy of our brief conversation we had. I will be doing the same for tl/optional soon. :)

  • "Fix issue#126"

    definition of implicit copy constructor for 'expected_storage_base<int, int, true, true>' is deprecated

    expected.hpp:: error: definition of implicit copy constructor for 'expected_storage_base<int, int, true, true>' is deprecated because it has a user-declared destructor [-Werror,-Wdeprecated] ~expected_storage_base() = default;

  • Fix the warning for deprecated copy ctor in case the dtor is user defined

    Fix the warning for deprecated copy ctor in case the dtor is user defined

    definition of implicit copy constructor for 'expected_storage_base<int, int, true, true>' is deprecated

    expected.hpp:: error: definition of implicit copy constructor for 'expected_storage_base<int, int, true, true>' is deprecated because it has a user-declared destructor [-Werror,-Wdeprecated] ~expected_storage_base() = default; ^

  • Allow construction directly from an unexpected type

    Allow construction directly from an unexpected type

    This allows a more convenient usage, skipping make_unexpected except in ambiguous cases. I don't think this breaks anything. It doesn't in my use cases anyway.

  • `tl::make_unexpected()` should be `[[nodiscard]]`

    `tl::make_unexpected()` should be `[[nodiscard]]`

    As the titles says, the function tl::make_unexpected() should be marked [[nodiscard]].

    It would prevent misuse when forgetting to return the unexpected value:

    enum class ErrorType{
        kNullParameter
    };
    struct ResultType{};
    
    tl::expected<ResultType, ErrorType> myFunction(int nonNullParameter)
    {
        if(nonNullParameter== 0) {
            tl::make_unexpected(ErrorType::kNullParameter);
        }
        
        return ResultType{};
    }
    

    Here I forgot to return the unexpected value and there's no warning whatsoever.

  • Rename test folder to test (for evoke), fix warnings in pedantic

    Rename test folder to test (for evoke), fix warnings in pedantic

    • remove unused parameter names
    • mark unused variables as maybe_unused
    • resolve collision with catch's STATIC_REQUIRE
    • make assignment operators return a value
    • remove the "test.cpp" file which is lacking includes and excluded from build

    and rename the test folder to "test", so that evoke picks it up and runs it (rather than just building it).

    Checked with @TartanLlama beforehand for OK on the changes in principle.

  • Add `error_or` to round off functional interface

    Add `error_or` to round off functional interface

    error_or would be the compliment to value_or

    The motivation here is to flesh out the error handling path. When an error occurs it's likely that other expected functions may be called to cleanup. At the moment this would look like this:

    auto delete_file = [](auto&& err){
        return do_delete()
           .and_then([err = srd::move(err ](auto&& _) { return tl::make_unexpected(err); }) // propogage initial error
           .error()
    };
    
    create_file()
       .and_then(write_file)
       .map_error(delete_file)
       .and_then(rename_file)
    

    With error_or, delete_file could look like:

    auto delete_file = [](auto&& err) {
        return do_delete()
           .error_or(err);
    };
    

    on_error is a more powerful or_else, it could conditionally turn an error into a value again. This would also be useful during error handling, perhaps as a "cleanup and try a different technique that might still fail" type function.

    This is also suggested in the proposal: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2505r4.html

expected lite - Expected objects in C++11 and later in a single-file header-only library

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

Jan 4, 2023
A standard conforming C++20 implementation of std::optional.

A standard conforming C++20 implementation of std::optional.

Aug 24, 2022
A collection of std-like single-header C++ libraries

itlib: iboB's Template Libraries A collection of small single-header C++ libraries similar to or extending the C++ standard library. See below for a l

Dec 29, 2022
Range library for C++14/17/20, basis for C++20's std::ranges

range-v3 Range library for C++14/17/20. This code was the basis of a formal proposal to add range support to the C++ standard library. That proposal e

Dec 29, 2022
Monadic interface for std::optional

optional Simple monadic interface for std::optional Installation Just copy and include optional.h header in your project Usage All operations are in b

Apr 15, 2022
Allows a programmer to print table-like outputs over std::ostream.

tableprinter Allows a programmer to print table-like outputs over std::ostream. It is a header only library. No other dependency than STL. Provides re

Jul 7, 2022
Improved and configurable drop-in replacement to std::function that supports move only types, multiple overloads and more

fu2::function an improved drop-in replacement to std::function Provides improved implementations of std::function: copyable fu2::function move-only fu

Dec 12, 2022
C++11/14/17 std::optional with functional-style extensions and reference support

optional Single header implementation of std::optional with functional-style extensions and support for references. Clang + GCC: MSVC: std::optional i

Dec 23, 2022
expected lite - Expected objects in C++11 and later in a single-file header-only library

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

Jan 4, 2023
A wrapper of C++ std::vector for functional programming

Say hello to functional C++ vectors A wrapper for C++ std::vector geared towards functional programming and fluent APIs. The primary focus is readabil

Jul 26, 2022
Functional programming style pattern-matching library for C++
Functional programming style pattern-matching library for C++

Mach7: Pattern Matching for C++ by Yuriy Solodkyy, Gabriel Dos Reis, Bjarne Stroustrup Abstract Pattern matching is an abstraction mechanism that can

Dec 26, 2022
Simple EFI runtime driver that hooks GetVariable function and returns data expected by Windows to make it think that it's running with secure boot enabled (faking secure boot)
Simple EFI runtime driver that hooks GetVariable function and returns data expected by Windows to make it think that it's running with secure boot enabled (faking secure boot)

SecureFakePkg is a simple EFI runtime driver that hooks GetVariable function and returns data expected by Windows to make it think that it's running with secure boot enabled. In other words, it fakes secure boot status.

Dec 30, 2022
Fast C++ container combining the best features of std::vector and std::deque

veque The double-ended vector A very fast C++17 container combining the best features of std::vector and std::deque "In Most Cases, Prefer Using deque

Nov 26, 2022
Invoke.hpp - std::invoke/std::apply analogs for C++11/14

invoke.hpp std::invoke/std::apply analogs for C++11/14 Requirements gcc >= 4.9 clang >= 3.8 msvc >= 2015 Installation invoke.hpp is a header-only libr

Dec 1, 2022
Cpp-std-fwd - forward declarations for C++ std headers

cpp-std-fwd Forward declarations for most useful runtime classes of the C++ 17 standard library. DISCLAIMER: This project is meant as a proof-of-conce

Jul 10, 2022
Library to build PHP extensions with C++

PHP-CPP The PHP-CPP library is a C++ library for developing PHP extensions. It offers a collection of well documented and easy-to-use classes that can

Dec 24, 2022
🥑 ArangoDB is a native multi-model database with flexible data models for documents, graphs, and key-values. Build high performance applications using a convenient SQL-like query language or JavaScript extensions.
🥑 ArangoDB is a native multi-model database with flexible data models for documents, graphs, and key-values. Build high performance applications using a convenient SQL-like query language or JavaScript extensions.

?? ArangoDB is a native multi-model database with flexible data models for documents, graphs, and key-values. Build high performance applications using a convenient SQL-like query language or JavaScript extensions.

Jan 9, 2023
First-up chord-send and tap-and-hold chord repeat extensions to QMK.

Quantum Mechanical Keyboard Firmware This is a keyboard firmware based on the tmk_keyboard firmware with some useful features for Atmel AVR and ARM co

Dec 14, 2022
Microsoft RDP Client Extensions

Microsoft RDP Extensions (msrdpex) The official Microsoft RDP client is the only one with an exhaustive implementation of the entire feature set. Whil

Nov 25, 2022
C Extensions i made for DragonRuby!

drext C Extensions i made for DragonRuby NOTE: DragonRuby Pro required for C Extensions. List Name Description Platforms drbat Battery information lib

Dec 5, 2022