Giter Club home page Giter Club logo

expected's People

Contributors

adah1972 avatar andy-byers avatar apmorton avatar bhvrkaboissonneault avatar bobbleclank avatar bruxisma avatar daira avatar ericlemanissier avatar hannahwhy avatar j-mew-s avatar jharmer95 avatar kaboissonneault avatar kbenzie avatar ksdd avatar le-migou avatar lesleylai avatar rollbear avatar tartanllama avatar vpodzime avatar zhuhaow avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

expected's Issues

.and_then() does not work with non-constexpr T

First of all, thanks for sharing a great implementation an enhanced std::expected!

I ran into a problem when trying to use a type that does not have a constexpr constructor with tl::expected<T, E> when using .and_then(). Here's a minimal example that reproduces the problem:

#include "expected.hpp"
#include <string>

tl::expected<int, int> operation1() { return 42; }

tl::expected<std::string, int> operation2(int const val) { return "Bananas"; }

tl::expected<std::string, int> do_something()
{
	auto const intermediate_result = operation1();

	// DOES NOT COMPILE
	return intermediate_result.and_then(operation2);

	// Must do this instead
	//if (!intermediate_result)
	//	return tl::make_unexpected(intermediate_result.error());
	//return operation2(intermediate_result.value());
}

As noted, the line "does not compile" causes the compiler error "error C2476: 'constexpr' constructor does not initialize all members" with Visual Studio 2015 and 2017. Two workarounds that solve the issue are: 1) Remove constexpr from the expected_storage_base<T, E, false, true> constructor on line 426 of expected.hpp (not ideal), or 2) add char dummy; as the first member of the union in that class and then initialize dummy on line 426. This constructor does not explicitly initialize either union member so the compiler will default initialize the first union member; however, since this constructor is marked constexpr, the first union member must also have a constexpr constructor. The first workaround removes constexpr from the constructor so the the first union member is not required to also have a constexpr constructor, while the second workaround adds another member to the union with a constexpr constructor (default initialization for char). Initializing the second member of the union instead of the first is not an option since tl::unexpected<E>() is deleted.

I think a correct solution would be to use something like std::aligned_storage<sizeof(T), alignof(T)>::type in the union instead of T directly.

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?

Make an nullopt if an error is returned

Hi!

More as a nice thing to have than anything, would it be possible somehow to write :

tl::optional<T> opt = my_function(...).value_or(tl::nullopt);

assuming my_function returns a tl::expected<T, E>? I tried some different syntax but had to fall back to a more classical if (which is fine).

swap noexcept specifier "depends on itself"

Compiler

GCC 8.2 (uses concepts, can't try elsewhere)

Environment

Ubuntu 18.04 (WSL)

Flags

/usr/bin/g++-8                                                                                                      \
   -I../projects/ranges/include                                                                                     \
   -I../projects/expected                                                                                           \
   -I../../../include                                                                                               \
   -isystem ${HOME}/.conan/data/range-v3/1.0.0/cjdb/stable/package/108b10878ddfbada81a0ed2fb115999aafcd316c/include \
   -g                                                                                                               \
   -Wall                                                                                                            \
   -Wextra                                                                                                          \
   -Wno-attributes                                                                                                  \
   -Wsign-promo                                                                                                     \
   -Woverloaded-virtual                                                                                             \
   -Wnon-virtual-dtor                                                                                               \
   -Wodr                                                                                                            \
   -Werror                                                                                                          \
   -DRANGES_DEEP_STL_INTEGRATION=1                                                                                  \
   -Wcast-align                                                                                                     \
   -Wconversion                                                                                                     \
   -Wduplicated-cond                                                                                                \
   -Wduplicated-branches                                                                                            \
   -Wdouble-promotion                                                                                               \
   -Wlogical-op                                                                                                     \
   -Wnull-dereference                                                                                               \
   -Wold-style-cast                                                                                                 \
   -Wpedantic                                                                                                       \
   -Wshadow                                                                                                         \
   -Wsign-conversion                                                                                                \
   -Wmisleading-indentation                                                                                         \
   -Wunused                                                                                                         \
   -Wuseless-cast                                                                                                   \
   -Wformat=2                                                                                                       \
   -fstack-protector-all                                                                                            \
   -fdiagnostics-color=always                                                                                       \
   -fconcepts                                                                                                       \
   -std=c++2a                                                                                                       \
   -MD -MT                                                                                                          \
   source/lexer/CMakeFiles/source.lexer.scan_whitespace.dir/scan_whitespace.cpp.o                                   \
   -MF                                                                                                              \
   source/lexer/CMakeFiles/source.lexer.scan_whitespace.dir/scan_whitespace.cpp.o.d                                 \
   -o source/lexer/CMakeFiles/source.lexer.scan_whitespace.dir/scan_whitespace.cpp.o                                \
   -c ../../../source/lexer/scan_whitespace.cpp

Diagnostic

../expected/tl/expected.hpp:1771:52: error: exception specification of ‘void tl::expected<T, E>::swap(tl::expected<T, E>&) [with T = ltcpp::source_coordinate; E = std::variant<ltcpp::detail_lexer::not_comment, ltcpp::detail_lexer::unterminated_comment_error>]’ depends on itself
       std::is_nothrow_move_constructible<T>::value &&noexcept(
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           swap(std::declval<T &>(), std::declval<T &>())) &&
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       std::is_nothrow_move_constructible<E>::value &&
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
       noexcept(swap(std::declval<E &>(), std::declval<E &>()))) {
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../expected/tl/expected.hpp:1770:15: error: ‘swap’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
           swap(std::declval<T &>(), std::declval<T &>())) &&
           ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../expected/tl/expected.hpp:1770:15: note: declarations in dependent base ‘tl::expected<ltcpp::source_coordinate, std::variant<ltcpp::detail_lexer::not_comment, ltcpp::detail_lexer::unterminated_comment_error> >’ are not found by unqualified lookup
../expected/tl/expected.hpp:1770:15: note: use ‘expected::swap’ instead
../expected/tl/expected.hpp:1770:15: error: no matching function for call to ‘tl::expected<ltcpp::source_coordinate, std::variant<ltcpp::detail_lexer::not_comment, ltcpp::detail_lexer::unterminated_comment_error> >::swap(ltcpp::source_coordinate&, ltcpp::source_coordinate&)’
../expected/tl/expected.hpp:1768:8: note: candidate: ‘void tl::expected<T, E>::swap(tl::expected<T, E>&) [with T = ltcpp::source_coordinate; E = std::variant<ltcpp::detail_lexer::not_comment, ltcpp::detail_lexer::unterminated_comment_error>]’
   void swap(expected &rhs) noexcept(
        ^~~~
../expected/tl/expected.hpp:1768:8: note:   candidate expects 1 argument, 2 provided
ninja: build stopped: subcommand failed.

Doesn't compile with clang under C++11 mode

$ git clone https://github.com/TartanLlama/expected.git && echo '#include <tl/expected.hpp>' > test.cpp && clang++-9 -c -I./expected -std=c++11 test.cpp
In file included from test.cpp:1:
./expected/tl/expected.hpp:768:18: warning: 'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const' to avoid a change in behavior
      [-Wconstexpr-not-const]
  constexpr void destroy_val() {
                 ^
                               const
./expected/tl/expected.hpp:768:18: error: constexpr function's return type 'void' is not a literal type
./expected/tl/expected.hpp:823:18: warning: 'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const' to avoid a change in behavior
      [-Wconstexpr-not-const]
  constexpr void destroy_val() {
                 ^
                               const
./expected/tl/expected.hpp:823:18: error: constexpr function's return type 'void' is not a literal type
2 warnings and 2 errors generated.

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!

`map_error` requires being callable with the expected type

struct S {
    int x;
};

struct F {
    int x;
};

tl::expected<S, F> f(bool succeed) {
    if(succeed) {
        return S{42};
    }
    return tl::expected<S, F>{tl::unexpect, F{1337}};
}

template<typename>
struct inspect;

int main() {
    auto res = f(true);
    
    res.map([](S s) {
        printf("%d", s.x);
    });
    res.map_error([](auto r) {
        if constexpr(std::is_same_v<F, decltype(r)>) {
            printf("%d", r.x);
        }
        else {
            inspect<decltype(f)>{};
        }
    });

This should compile

Warnings about unused parameters

Even after #8, I see a lot of warnings under Clang (using -W -Wall):

./tl/expected.hpp:690:30: warning: unused parameter 'rhs' [-Wunused-parameter]
  void construct_with(Rhs && rhs) noexcept {
                             ^
./tl/expected.hpp:2060:48: warning: unused parameter 'x' [-Wunused-parameter]
constexpr bool operator<(const expected<T, E> &x, const unexpected<E> &e) {
                                               ^
./tl/expected.hpp:2060:72: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator<(const expected<T, E> &x, const unexpected<E> &e) {
                                                                       ^
./tl/expected.hpp:2064:47: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator<(const unexpected<E> &e, const expected<T, E> &x) {
                                              ^
./tl/expected.hpp:2068:73: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator<=(const expected<T, E> &x, const unexpected<E> &e) {
                                                                        ^
./tl/expected.hpp:2072:48: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator<=(const unexpected<E> &e, const expected<T, E> &x) {
                                               ^
./tl/expected.hpp:2072:73: warning: unused parameter 'x' [-Wunused-parameter]
constexpr bool operator<=(const unexpected<E> &e, const expected<T, E> &x) {
                                                                        ^
./tl/expected.hpp:2076:72: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator>(const expected<T, E> &x, const unexpected<E> &e) {
                                                                       ^
./tl/expected.hpp:2080:47: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator>(const unexpected<E> &e, const expected<T, E> &x) {
                                              ^
./tl/expected.hpp:2080:72: warning: unused parameter 'x' [-Wunused-parameter]
constexpr bool operator>(const unexpected<E> &e, const expected<T, E> &x) {
                                                                       ^
./tl/expected.hpp:2084:49: warning: unused parameter 'x' [-Wunused-parameter]
constexpr bool operator>=(const expected<T, E> &x, const unexpected<E> &e) {
                                                ^
./tl/expected.hpp:2084:73: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator>=(const expected<T, E> &x, const unexpected<E> &e) {
                                                                        ^
./tl/expected.hpp:2088:48: warning: unused parameter 'e' [-Wunused-parameter]
constexpr bool operator>=(const unexpected<E> &e, const expected<T, E> &x) {
                                               ^

The simplest fix is, of course, just remove the unused parameter names. But it only makes sense for the first warning (I misunderstood when I first reported the issue: now edited). The key problem is:

  • Does ordering make sense between values and errors?

I have two alternatives:

  • Remove these functions.
  • Define an ErrorExists error, and make the return type expected<bool, ErrorExists>. A boolean return value is used when both arguments have values. When at least one argument does not have a valid value, ErrorExists is returned instead of a fake boolean value. Comparing order of errors seems useless to me: think about comparing two errnos, or two exception_ptrs.

What do you think?

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( []() {} );
                       ^

1.0.1+ release (for e.g. inclusion in conan-center-index)

I'm submitting my Conan recipe for tl::expected to conan-center-index: conan-io/conan-center-index#556. If accepted, this will deprecate https://github.com/yipdw/conan-tl-expected and the name of the package will be tl-expected/x.y.z (no user/channel part required).

conan-center-index has a policy that only official releases of a package can be included in the index, and tl::expected 1.0.1 fixes a bug that I've hit in the past. Can a release including that bugfix be made? If there are blockers, is there anything I can do to help?

Should tl::expected be nodiscard?

I'm guessing that this is maybe controversial in the wider community, but I'm looking into adopting tl::expected and I'm not quite sure why it's not [[nodiscard]], is there a reason for this?

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) };
}

Compilation error on MSVC 2019 Preview 3

The MSVC preview doesn't seem to like the and_then implementation

Happened with /std:c++latest /permissive-. I haven't tested to see if it works with flags other than that.

src\common/expected.hpp(1227): error C2355: 'this': can only be referenced inside non-static member functions or non-static data member initializers

src\common/expected.hpp(1895): note: see reference to class template instantiation 'tl::expected<T,E>' being compiled

src\common/expected.hpp(1235): error C2355: 'this': can only be referenced inside non-static member functions or non-static data member initializers

src\common/expected.hpp(1243): error C2355: 'this': can only be referenced inside non-static member functions or non-static data member initializers

src\common/expected.hpp(1252): error C2355: 'this': can only be referenced inside non-static member functions or non-static data member initializers

const version of `valptr()`

My LLVM complains when compiling expected::operator->() const.
I've had to add:

const T *valptr() const { return std::addressof(this->m_val); }

Request for std::launder usage in C++17 and later

I believe that using tl::expected with a class X that contains const member variables is undefined behaviour. The same can actually be said for some STL containers, such as std::vector! This is due to assumptions made by the compiler about the value held at the address of a const member variable. The paper on std::launder gives some really good examples about this (it even uses optional as an example), so I'll let it do the explaining.

It more or less comes down to the usage of placement new and accessing the object at that address. Whenever the object (at the address passed to placement new) is accessed, it needs to be accessed through the pointer returned by std::launder. As an example, when the destructor gets invoked here:

~expected_storage_base() {
    if (m_has_val) {
      m_val.~T();
    } else {
      m_unexpect.~unexpected<E>();
    }
  }

It's my understanding that this would need to be replaced with:
std::launder(&m_val)->~T();
The same applies for tl::optional, I believe :) thank you for the great work you've done on both of these!

What is the idiomatic way of returning success from void function?

Is the following snipped the idiomatic way or is there some helper or shortcut that can be used to avoid writing out the full type?
What is the performance implication of returning a void expected object compared to an option or no error wrapper?

tl::expected<void, std::string> void_func()
{
  return tl::expected<void, std::string>();
}

Can't call `map_error` on `expected<void>`

Hi,

The minimal example to reproduce it is:

void test() {
    expected<void, int> res = {};
    res.map_error([](auto i) { return i; });
}

I think there is something wrong with the expect<void> specialization?

Is the line

template <class Exp, class F,
          class Ret = decltype(detail::invoke(std::declval<F>(),
                                              std::declval<Exp>().error())),
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
auto map_error_impl(Exp &&exp, F &&f) {
  using result = expected<exp_t<Exp>, monostate>;
  if (exp.has_value()) {
    return result(*std::forward<Exp>(exp));    <------------------- this
  }

  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
  return result(unexpect, monostate{});
}

still available when exp is void?

Currently, I have to use map([](auto v) {return 0;}) to workaround this.

You can find it here:
https://gcc.godbolt.org/z/EFLUdC

Also, I found I can't compile it with GCC 8-.

Unable to assign unexpected to expected<void, std::string>

I think this should work, but it doesn't. Assigning an unexpected attempts to destruct the value which doesn't exist in the case of void.

auto result = tl::expected<void, std::string>{};
result = tl::make_unexpected(std::string{"foo"}); // Fails to compile

Macros redefined

Thanks for creating optional and expected! I like them so much I'm using both in the same project.

However, when both headers are included together, three macros get redefined:

  • IS_TRIVIALLY_COPY_CONSTRUCTIBLE
  • IS_TRIVIALLY_COPY_ASSIGNABLE
  • IS_TRIVIALLY_DESTRUCTIBLE

To address this, the macros could be defined to be unique to each project or wrapped with ifndef.

What do you think? Thanks for considering!

map_error compile problem when different expected type and unexpected type

tl::expected< int, std::string > error() {
	return tl::make_unexpected( std::string( "error1 " ) );
}
std::string maperror( std::string s ) {
	return s + "maperror ";
}
int main(int argc, char *argv[])
{
	auto result = error()
		.map_error( maperror ) ;
	if( result.has_value() )
		std::cout << "result: " << result.value();
	else
		std::cout << "result: fail: " << result.error();
}

it's compile error; ( windows 10, mingw 5.3 32bit )

expected\tl\expected.hpp:2022: error: no matching function for call to 'tl::expected<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >::expected(int)'
              ? result(*std::forward<Exp>(exp))
              ^

but same type is success. Is this right?

tl::expected< std::string, std::string > error() {
.....

VS 2019 Support?

I noticed on the list of supported VC++ compilers VS 2019 was not listed. Is there any reason why this wouldn't work in VS 2019?

Issues with `and_then` on MSVC with `std::string`

Reported via Slack by @nikitablack.

tl::expected<int, string> getInt3(int val)
{
    return val;
}

tl::expected<int, string> getInt2(int val)
{
    return val;
}

tl::expected<int, string> getInt1()
{
    // this works ok
    /*auto int2 = getInt2(5);
    if (!int2) {
        return int2;
    }

    auto int3 = getInt3(*int2);
    if (!int3) {
        return int3;
    }

    return *int3;*/

    // this fails to compile
    return getInt2(5).and_then(getInt3);
}

error C2672: 'tl::expected<int,std::string>::and_then': no matching overloaded function found
error C2893: Failed to specialize function template 'invoke_result_impl<F,void,T&>::type tl::expected<T,std::string>::and_then(F &&) &'
with
[
T=int
]
note: With the following template arguments:
note: 'F=tl::expected<int,std::string> (__cdecl &)(int)'

Is expected meant to be trivial if the template types are trivial?

Is tl::expected meant to be trivial if both template types are trivial? The below snippet demonstrates this:

// Trivial
static_assert(std::is_trivial<VW::vw_error>::value, "");

// Not trivial
static_assert(!std::is_trivial<tl::expected<int, VW::vw_error>>::value, "");
static_assert(!std::is_trivial<tl::expected<void, VW::vw_error>>::value, "");

compilation fails with -std=c++11

I would really like to use your library because I think it's the only one to claim c++11 support (I have to deal with this requirement) and the functional extensions.

Unfortunately it doesn't compile:

git clone https://github.com/TartanLlama/expected.git
echo '#include <tl/expected.hpp>' > test.cpp
g++ -c -I./expected -std=c++11 test.cpp

This result in the following output:

In file included from test.cpp:1:0:
./expected/tl/expected.hpp:123:31: fout: ‘constexpr E& tl::unexpected<E>::value() const &’ cannot be overloaded
   TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; }
                               ^
./expected/tl/expected.hpp:121:22: fout: with ‘constexpr const E& tl::unexpected<E>::value() const &’
   constexpr const E &value() const & { return m_val; }
                      ^
./expected/tl/expected.hpp:127:23: fout: ‘constexpr const E&& tl::unexpected<E>::value() const &&’ cannot be overloaded
   constexpr const E &&value() const && { return std::move(m_val); }
                       ^
./expected/tl/expected.hpp:125:32: fout: with ‘constexpr E&& tl::unexpected<E>::value() const &&’
   TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); }
                                ^
./expected/tl/expected.hpp:659:22: fout: ‘constexpr const T& tl::detail::expected_operations_base<T, E>::get() const &’ cannot be overloaded
   constexpr const T &get() const & { return this->m_val; }
                      ^
./expected/tl/expected.hpp:658:31: fout: with ‘constexpr T& tl::detail::expected_operations_base<T, E>::get() const &’
   TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; }
                               ^
./expected/tl/expected.hpp:662:23: fout: ‘constexpr const T&& tl::detail::expected_operations_base<T, E>::get() const &&’ cannot be overloaded
   constexpr const T &&get() const && { return std::move(this->m_val); }
                       ^
./expected/tl/expected.hpp:660:32: fout: with ‘constexpr T&& tl::detail::expected_operations_base<T, E>::get() const &&’
   TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); }
                                ^
./expected/tl/expected.hpp:668:34: fout: ‘constexpr const tl::unexpected<E>& tl::detail::expected_operations_base<T, E>::geterr() const &’ cannot be overloaded
   constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
                                  ^
./expected/tl/expected.hpp:665:43: fout: with ‘constexpr tl::unexpected<E>& tl::detail::expected_operations_base<T, E>::geterr() const &’
   TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
                                           ^
./expected/tl/expected.hpp:673:35: fout: ‘constexpr const tl::unexpected<E>&& tl::detail::expected_operations_base<T, E>::geterr() const &&’ cannot be overloaded
   constexpr const unexpected<E> &&geterr() const && {
                                   ^
./expected/tl/expected.hpp:669:44: fout: with ‘constexpr tl::unexpected<E>&& tl::detail::expected_operations_base<T, E>::geterr() const &&’
   TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
                                            ^
./expected/tl/expected.hpp:724:34: fout: ‘constexpr const tl::unexpected<E>& tl::detail::expected_operations_base<void, E>::geterr() const &’ cannot be overloaded
   constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
                                  ^
./expected/tl/expected.hpp:721:43: fout: with ‘constexpr tl::unexpected<E>& tl::detail::expected_operations_base<void, E>::geterr() const &’
   TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
                                           ^
./expected/tl/expected.hpp:729:35: fout: ‘constexpr const tl::unexpected<E>&& tl::detail::expected_operations_base<void, E>::geterr() const &&’ cannot be overloaded
   constexpr const unexpected<E> &&geterr() const && {
                                   ^
./expected/tl/expected.hpp:725:44: fout: with ‘constexpr tl::unexpected<E>&& tl::detail::expected_operations_base<void, E>::geterr() const &&’
   TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
                                            ^
In file included from test.cpp:1:0:
./expected/tl/expected.hpp:1331:41: fout: ‘template<class T, class E> template<class F> constexpr tl::expected<T, E> tl::expected<T, E>::or_else(F&&) const &’ cannot be overloaded
   template <class F> expected constexpr or_else(F &&f) const & {
                                         ^
./expected/tl/expected.hpp:1323:56: fout: with ‘template<class T, class E> template<class F> constexpr tl::expected<T, E> tl::expected<T, E>::or_else(F&&) const &’
   template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & {
                                                        ^
./expected/tl/expected.hpp:1335:41: fout: ‘template<class T, class E> template<class F> constexpr tl::expected<T, E> tl::expected<T, E>::or_else(F&&) const &&’ cannot be overloaded
   template <class F> expected constexpr or_else(F &&f) const && {
                                         ^
./expected/tl/expected.hpp:1327:56: fout: with ‘template<class T, class E> template<class F> constexpr tl::expected<T, E> tl::expected<T, E>::or_else(F&&) const &&’
   template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && {
                                                        ^
./expected/tl/expected.hpp:1677:31: fout: ‘constexpr T* tl::expected<T, E>::operator->() const’ cannot be overloaded
   TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); }
                               ^
./expected/tl/expected.hpp:1675:22: fout: with ‘constexpr const T* tl::expected<T, E>::operator->() const’
   constexpr const T *operator->() const { return valptr(); }
                      ^
./expected/tl/expected.hpp:1737:31: fout: ‘constexpr E& tl::expected<T, E>::error() const &’ cannot be overloaded
   TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); }
                               ^
./expected/tl/expected.hpp:1735:22: fout: with ‘constexpr const E& tl::expected<T, E>::error() const &’
   constexpr const E &error() const & { return err().value(); }
                      ^
./expected/tl/expected.hpp:1741:32: fout: ‘constexpr E&& tl::expected<T, E>::error() const &&’ cannot be overloaded
   TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); }
                                ^
./expected/tl/expected.hpp:1739:23: fout: with ‘constexpr const E&& tl::expected<T, E>::error() const &&’
   constexpr const E &&error() const && { return std::move(err().value()); }
                       ^
./expected/tl/expected.hpp: In constructor ‘tl::expected<T, E>::expected(const tl::expected<U, G>&)’:
./expected/tl/expected.hpp:1430:3: fout: constexpr constructor does not have empty body
   }
   ^
./expected/tl/expected.hpp: In constructor ‘tl::expected<T, E>::expected(const tl::expected<U, G>&)’:
./expected/tl/expected.hpp:1446:3: fout: constexpr constructor does not have empty body
   }
   ^
./expected/tl/expected.hpp: In constructor ‘tl::expected<T, E>::expected(tl::expected<U, G>&&)’:
./expected/tl/expected.hpp:1460:3: fout: constexpr constructor does not have empty body
   }
   ^
./expected/tl/expected.hpp: In constructor ‘tl::expected<T, E>::expected(tl::expected<U, G>&&)’:
./expected/tl/expected.hpp:1475:3: fout: constexpr constructor does not have empty body
$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.5' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.5)

or_else calls with uninitialized value on error state

The bug is fairly obvious from the source code:

template <class Exp, class F,
          class Ret = decltype(detail::invoke(std::declval<F>(),
                                              *std::declval<Exp>())),
          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
constexpr detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
  if (exp.has_value()) {
    return std::forward<Exp>(exp);
  }

  return detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
}

template <class Exp, class F,
          class Ret = decltype(detail::invoke(std::declval<F>(),
                                              *std::declval<Exp>())),
          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
  if (exp.has_value()) {
    return std::forward<Exp>(exp);
  }

  detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
  return std::forward<Exp>(exp);
}

The calls to *forward<Exp>(exp) are guaranteed to return a reference to T, from a memory area that holds an ´E`, in the (implicit) else branches.

I presume the correct behaviour is to accept a function that takes an E, and call it with .error()?

g++-7: compile error with expected vector type depending on include order

On Ubuntu 18.04 (g++-7) I got a strange compile error for this sequence of code:

tl::expected<std::vector<char>,bool> v1; auto v2 = v1;

The compiler complains, that copying is disabled. The strange thing is, that this error only occurs, when vector is included before expected.hpp. No error occurs, when the include order is swapped. I don't know, if this is a compiler bug or in implementation issue. I was able to reproduce the bug on the compiler explorer web-site. It seems, that the bug is present in g++ 5.x - 7.x.

Error type can't be non-copyable?

using MaybeDataPtr = tl::expected<int, std::unique_ptr<int>>;

MaybeDataPtr test(int i) noexcept
{
    return std::move(i);
}

MaybeDataPtr test2(int i) noexcept
{
    return std::move(i);
}

auto m = test(10)
         .and_then(test2);

Used with gcc-7.2.0.

Comparisons that involve a possible error

As I mentioned last time, I do not think it good to make functions like operator<(const expected& lhs, const expected& rhs) return a simple bool, if an error might be present. Some potential alternatives for discussion:

  • Change the return type to expected<bool, error_exists>, where error_exists is a special new type. This is what I proposed last time.
  • Change the return type to std::optional<bool>. It is similar to the previous proposal, but can be implemented in a simpler way. The drawback is the requirement of a C++17 compiler.
  • Do not change the return type, but a new error_exists exception should be thrown when either lhs or rhs contains an error.

For broad applicability, I do not prefer the middle approach. The first option conforms to the expected rationale, but makes the client code a bit less straightforward. The last approach allows the most straightforward usage, but it seems a bit weird under the context of expected.

There is another view angle, though. If we take into account "concepts", which will likely make into C++20, a Regular object should be EqualityComparable, which requires that == and != can work and result in a type that can be converted to bool implicitly. Another concept, StrictTotallyOrdered, requires operators like <, and that the result should be convertible to bool. Neither of the first two options can satisfy these requirements, which would make the expected type Semiregular at best. For future safety, I think comparison operators involving expected objects should return bool but may throw. We may provide alternative functions (not operators) like tl::eq and tl::gte, which return an expected object and will not throw (if value comparison does not throw).

What do you think?

Cannot use unique_ptr for unexpected value gcc5.5

This fails to compile with the following error that indicates that the deleted copy constructor is used.

tl::expected<int, std::unique_ptr<std::string>> func()
{
  return 1;
}
/mnt/c/w/repos/perf_tests/perf_tests/../sub/expected/include/tl/expected.hpp: In instantiation of ‘U& tl::expected<T, E>::value() & [with U = int; tl::detail::enable_if_t<(! std::is_void<_Res>::value)>* <anonymous> = 0u; T = int; E = std::unique_ptr<std::__cxx11::basic_string<char> >]’:
/mnt/c/w/repos/perf_tests/perf_tests/expected.cpp:161:37:   required from here
/mnt/c/w/repos/perf_tests/perf_tests/../sub/expected/include/tl/expected.hpp:1903:30: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = std::__cxx11::basic_string<char>; _Dp = std::default_delete<std::__cxx11::basic_string<char> >]’
       detail::throw_exception(bad_expected_access<E>(err().value()));
                              ^
In file included from /usr/include/c++/5/memory:81:0,
                 from /mnt/c/w/repos/perf_tests/perf_tests/expected.cpp:2:
/usr/include/c++/5/bits/unique_ptr.h:359:7: note: declared here
       unique_ptr(const unique_ptr&) = delete;
       ^
In file included from /mnt/c/w/repos/perf_tests/perf_tests/expected.cpp:5:0:
/mnt/c/w/repos/perf_tests/perf_tests/../sub/expected/include/tl/expected.hpp:1191:12: note:   initializing argument 1 of ‘tl::bad_expected_access<E>::bad_expected_access(E) [with E = std::unique_ptr<std::__cxx11::basic_string<char> >]’
   explicit bad_expected_access(E e) : m_val(std::move(e)) {}

Compiler:
g++ (Ubuntu 5.5.0-12ubuntu1~16.04) 5.5.0 20171010

make_unexpected in a function marked noexcept

Hi,

I have a function that returns tl::expected<int, std::exception_ptr> and is marked as noexcept. Inside that function I try to catch any exception that might be in flight and wrap it in tl::expected by calling tl::make_unexpected(std::current_exception()). That, unsurprisingly, compiles just fine. If I then attempt to run the code analysis (VS 2019 16.3) on said function, I get the following warning: C26447: The function is declared 'noexcept' but calls function 'make_unexpected<std::exception_ptr>()' which may throw exceptions (f.6).
Obviously, I can suppress this warning, but I'd like to know, if there's a way to call make_unexpected inside a noexcept function, that avoids triggering this particular warning? Other than modifying expected.hpp and making th make_unexpected fn and unexpected ctors conditionally noexcept (if the error type is noexcept copy-constructible or noexcept move-constructible)?

Usage Issue

Hi, I am trying to use this library but I am having some compilations errors. I tested on gcc 4.8, gcc 5.4, gcc 5.5 and gcc 6.4 using the gcc docker image for each version. I was able to compile the following source code only on gcc 4.8.

The source file:

 #include "expected.hpp"
 int main() {}

Build Command:

g++ source.cpp -std=c++11

Compilation Errors:

expected.hpp:94:3: error: 'is_trivially_copy_constructible' is not a member of 'tl::detail'
tl::detail::is_trivially_copy_constructible
^
expected.hpp:814:32: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
^
expected.hpp:94:3: note: suggested alternative:
tl::detail::is_trivially_copy_constructible
^
expected.hpp:814:32: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
^
In file included from service.cpp:31:0:
/usr/local/include/c++/5.4.0/type_traits:1336:12: note: 'std::is_trivially_copy_constructible'
struct is_trivially_copy_constructible
^
In file included from service.cpp:32:0:
expected.hpp:94:3: error: 'is_trivially_copy_constructible' is not a member of 'tl::detail'
tl::detail::is_trivially_copy_constructible
^
expected.hpp:814:32: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
^
expected.hpp:94:3: note: suggested alternative:
tl::detail::is_trivially_copy_constructible
^
expected.hpp:814:32: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
^
In file included from service.cpp:31:0:
/usr/local/include/c++/5.4.0/type_traits:1336:12: note: 'std::is_trivially_copy_constructible'
struct is_trivially_copy_constructible
^
In file included from service.cpp:32:0:
expected.hpp:94:48: error: template argument 2 is invalid
tl::detail::is_trivially_copy_constructible
^
expected.hpp:814:32: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
^
expected.hpp:815:15: error: 'value' in namespace '::' does not name a type
value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
^
expected.hpp:822:8: error: 'expected_copy_base' is not a class template
struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
^
expected.hpp:94:3: error: 'is_trivially_copy_constructible' is not a member of 'tl::detail'
tl::detail::is_trivially_copy_constructible
^
expected.hpp:879:30: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
^
expected.hpp:94:3: note: suggested alternative:
tl::detail::is_trivially_copy_constructible
^
expected.hpp:879:30: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
^
In file included from service.cpp:31:0:
/usr/local/include/c++/5.4.0/type_traits:1336:12: note: 'std::is_trivially_copy_constructible'
struct is_trivially_copy_constructible
^
In file included from service.cpp:32:0:
expected.hpp:94:3: error: 'is_trivially_copy_constructible' is not a member of 'tl::detail'
tl::detail::is_trivially_copy_constructible
^
expected.hpp:879:30: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
^
expected.hpp:94:3: note: suggested alternative:
tl::detail::is_trivially_copy_constructible
^
expected.hpp:879:30: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
^
In file included from service.cpp:31:0:
/usr/local/include/c++/5.4.0/type_traits:1336:12: note: 'std::is_trivially_copy_constructible'
struct is_trivially_copy_constructible
^
In file included from service.cpp:32:0:
expected.hpp:94:48: error: template argument 2 is invalid
tl::detail::is_trivially_copy_constructible
^
expected.hpp:879:30: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE'
TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
^
expected.hpp:97:55: error: wrong number of template arguments (3, should be 2)
#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible
^
expected.hpp:880:30: note: in expansion of macro 'TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE'
TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value
^
expected.hpp:317:76: note: provided for 'template<class T, class U> using is_void_or = tl::detail::conditional_t<std::is_void<_Tp>::value, std::integral_constant<bool, true>, U>'
using is_void_or = conditional_t<std::is_void::value, std::true_type, U>;
^
expected.hpp:880:74: error: 'value' in namespace '::' does not name a type
TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value
^
expected.hpp:889:8: error: 'expected_copy_assign_base' is not a class template
struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
^

[question] extending and_then with parameters

Apologies in advance for the question-oriented submission. There is no bug, and tl::expected is awesome. I am trying to figuring out the best way to pass varadic parameters with the and_then() monad idiom.

For example, from the canonical example:

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

becomes:

    .and_then(add_bow_tie)

So far so good. But in about 100% of my cases, in reality I have:

    auto with_tie = add_bow_tie(*cropped, tiecolor::red);

Which I've been addressing with a mess of code that looks like:

    auto add_red_bow_tie = [](const image& img) {
        return add_bow_tie(img, tiecolor::red);
    }
    [...]
    .and_then(add_red_bow_tie)

What I would really like is something (?) like:

   .and_then(add_bow_tie, tiecolor::red)

But before I proceed to specialize tl::expected, and probably do it wrong, I was hoping someone might have some suggestions on the correct approach. Or alternatively, confirm I should just stick with all the little lambdas or std::bind. Note I am absolutely not asking nor suggesting tl::expected should be changed. I am just looking to use it right.

void expected requires and_then and or_else.

void expected is not available due to the and_then design.
I am using map instead.
However, it can not be used when error handling is required on the map.
So the code gets messy.

tl :: expected <void, std :: string> result = voidwork ();
if (result) {
     tl :: expected <int, std :: string> work2result = work2 ();
}

if (! result) {
     errorhandling (result.error ())
     return result;
}

to

tl :: expected <int, std :: string> result = voidWork ()
     .and_then (work2);
     .map_error ([&] (std :: string result) {errorhandling (result);
return result;

void expected also requires and_then and or_else.

Trailing spaces

There are quite a few trailing spaces in expected.hpp. They are quite annoying in my Vim using the 80-column text width.

Since this is so trivial, I am not sure whether you want a PR. If you do want a PR, I can do it.

Use of placement new in expected_operations_base.

Hello TL :)

This might be a non-issue, it's likely I've overlooked something, but it would appear that the usage of placement new here:

template <class... Args> void construct(Args &&... args) noexcept {
    new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
    this->m_has_val = true;
  }

  template <class Rhs> void construct_with(Rhs &&rhs) noexcept {
    new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
    this->m_has_val = true;
  }

begins to misbehave in some cases when constructing an expected<T const> from an expected<T>. std::addressof returns a pointer of type T const * which seems to confuse the overload resolution of operator new. E.g.

#include "expected.hpp"
#include <iostream>

class A
{
public:
    A() = default;;
    A(A const &) = default;
    A(A&&) = default;
    ~A() {}

    A& operator=(A const &) = default;
    A& operator=(A &&) = default;

    int i = 5u;
};

template< typename T>
using Expected = tl::expected<T, int>;

Expected<A> getSomeExpected()
{
    return Expected<A>(A{});
}

int main()
{	
    Expected<A const> a (getSomeExpected());
}

Gives the following:

1> error C2665: 'operator new': none of the 4 overloads could convert all the argument types
...
1> expectedtest\expected.hpp(665):  note: while trying to match the argument list '(unsigned __int64, _Ty *)'
1>        with
1>        [
1>            _Ty=const A
1>        ]

Casting the address type passed to placement new works, although I suspect that's probably UB.
Compiled with:
MSVC++ 14.16, _MSC_VER == 1916 (Visual Studio 2017 version 15.9)
/std:c++17

Issue with cmake

Note: I suck at CMake so this might not be a bug but rather a misunderstanding on my part.

For my project, I use CLion on Windows with CMake and git submodules for libraries.
I add libraries to my .gitmodules file and then in my CMakeLists.txt file I do add_subdirectory(libs/expected).

In my .gitmodules the library is included as such:

[submodule "libs/expected"]
	path = libs/expected
	url = https://github.com/TartanLlama/expected

When I sync CMake after including expected it gives an error:

CMake Error at libs/expected/CMakeLists.txt:35 (add_executable):
  add_executable cannot create ALIAS target "tl::expected" because target
  "expected" is not an executable.

To fix this error I modify expected\CMakeLists.txt by removing commenting the following lines:

34 | #if (NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
35 | #  add_executable(tl::expected ALIAS expected)
36 | #endif()

I have no idea why these lines cause an error and my knowledge of CMake is very limited, but every other library I use works fine by default.

If anyone can provide a solution for this or investigate if it's an actual issue that would be cool.
If there is something that I missed in terms of how to use the library with CMake maybe it would be nice to mention in the readme how to avoid this issue? At the very least I hope if someone else ever deals with this they can find a solution here.

Reference support?

Hey Sy

I was wondering if there will be reference support for tl::expected at some point in the future? I've just checked out the latest draft p0323r8 for std::expected and although I'd already expected ( 😁 sorry ) it std::expected is going to be consistent with std::optional and therefor not allow references.

Your tl::optional implementation on the other hand does allow references and even explicitly says so in the description. Is there any particular reason why you'd want support for one type but not the other?

Best regards
Vincent

Implementation of try?

How amenable would people be to some kind of "try" functionality? It's described more fully here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0779r0.pdf

I'm not sure about the status of that paper, but coming from Rust try!() and the ? operator are extremely useful when dealing with types like expected.

Obviously we wouldn't be able to have it as a real keyword/operator, but the less-ideal macro version seems pretty straightforward (TRYX() from the linked paper).

I could of course just send in a PR, but I thought it might be better to discuss it first.

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.

build break with -fno-expeptions

Hello and thanks for sharing this nice library!

With the merge of the swap branch the build without exceptions broke.

          ^
thirdparty/expected/tl/expected.hpp:1889:7: error: cannot use 'throw' with exceptions disabled
      throw;
      ^
thirdparty/expected/tl/expected.hpp:1882:5: error: cannot use 'try' with exceptions disabled
    try {
    ^
thirdparty/expected/tl/expected.hpp:1905:7: error: cannot use 'throw' with exceptions disabled
      throw;
      ^
thirdparty/expected/tl/expected.hpp:1898:5: error: cannot use 'try' with exceptions disabled
    try {

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.