Merge branch 'master' into trivial_copy_fix

This commit is contained in:
Simon Brand
2017-12-05 20:19:46 +00:00
committed by GitHub
4 changed files with 139 additions and 29 deletions

View File

@@ -11,6 +11,9 @@ target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})
set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/tests/main.cpp set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/tests/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tests/extensions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/extensions.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tests/assignment.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/assignment.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tests/emplace.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tests/bases.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tests/observers.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tests/constructors.cpp) ${CMAKE_CURRENT_SOURCE_DIR}/tests/constructors.cpp)
add_executable(tests ${TEST_SOURCES}) add_executable(tests ${TEST_SOURCES})

View File

@@ -49,7 +49,7 @@ The interface is the same as `std::expected` as proposed in [p0323r3](http://www
* `tl::expected<std::size_t,std::error_code> s = exp_string.map(&std::string::size);` * `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. - `map_error`: carries out some operation on the unexpected object if there is one.
* `my_error_code translate_error (std::error_code);` * `my_error_code translate_error (std::error_code);`
* `tl::expected<int,my_error_code> s = exp_int.map(translate_error);` * `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`. - `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> parse (const std::string& s);`
* `tl::expected<ast, fail_reason> exp_ast = exp_string.and_then(parse);` * `tl::expected<ast, fail_reason> exp_ast = exp_string.and_then(parse);`
@@ -82,6 +82,10 @@ Requires [Standardese](https://github.com/foonathan/standardese) for generating
Requires [Catch](https://github.com/philsquared/Catch) for testing. This is bundled in the test directory. Requires [Catch](https://github.com/philsquared/Catch) for testing. This is bundled in the test directory.
### Acknowledgements
Thanks to [Kévin Alexandre Boissonneault](https://github.com/KABoissonneault) for various bug fixes.
---------- ----------
[![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png)]("http://creativecommons.org/publicdomain/zero/1.0/") [![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png)]("http://creativecommons.org/publicdomain/zero/1.0/")

View File

@@ -158,8 +158,8 @@ constexpr bool operator>=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
/// *Example:* /// *Example:*
/// auto e1 = tl::make_unexpected(42); /// auto e1 = tl::make_unexpected(42);
/// unexpected<int> e2 (42); //same semantics /// unexpected<int> e2 (42); //same semantics
template <class E> unexpected<E> make_unexpected(E &&e) { template <class E> unexpected<typename std::decay<E>::type> make_unexpected(E &&e) {
return unexpected<E>(std::forward<E>(e)); return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
} }
/// \brief A tag type to tell expected to construct the unexpected value /// \brief A tag type to tell expected to construct the unexpected value
@@ -631,7 +631,8 @@ struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
// move constructible // move constructible
#ifndef TL_EXPECTED_GCC49 #ifndef TL_EXPECTED_GCC49
template <class T, class E, template <class T, class E,
bool = std::is_trivially_move_constructible<T>::value> bool = std::is_trivially_move_constructible<T>::value
&& std::is_trivially_move_constructible<E>::value>
struct expected_move_base : expected_copy_base<T, E> { struct expected_move_base : expected_copy_base<T, E> {
using expected_copy_base<T, E>::expected_copy_base; using expected_copy_base<T, E>::expected_copy_base;
}; };
@@ -662,7 +663,10 @@ struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
template <class T, class E, template <class T, class E,
bool = IS_TRIVIALLY_COPY_ASSIGNABLE(T) && bool = IS_TRIVIALLY_COPY_ASSIGNABLE(T) &&
IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) && IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) &&
IS_TRIVIALLY_DESTRUCTIBLE(T)> IS_TRIVIALLY_DESTRUCTIBLE(T) &&
IS_TRIVIALLY_COPY_ASSIGNABLE(E) &&
IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E) &&
IS_TRIVIALLY_DESTRUCTIBLE(E)>
struct expected_copy_assign_base : expected_move_base<T, E> { struct expected_copy_assign_base : expected_move_base<T, E> {
using expected_move_base<T, E>::expected_move_base; using expected_move_base<T, E>::expected_move_base;
}; };
@@ -692,7 +696,10 @@ struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
template <class T, class E, template <class T, class E,
bool = std::is_trivially_destructible<T>::value bool = std::is_trivially_destructible<T>::value
&&std::is_trivially_move_constructible<T>::value &&std::is_trivially_move_constructible<T>::value
&&std::is_trivially_move_assignable<T>::value> &&std::is_trivially_move_assignable<T>::value
&&std::is_trivially_destructible<E>::value
&&std::is_trivially_move_constructible<E>::value
&&std::is_trivially_move_assignable<E>::value>
struct expected_move_assign_base : expected_copy_assign_base<T, E> { struct expected_move_assign_base : expected_copy_assign_base<T, E> {
using expected_copy_assign_base<T, E>::expected_copy_assign_base; using expected_copy_assign_base<T, E>::expected_copy_assign_base;
}; };
@@ -709,9 +716,12 @@ struct expected_move_assign_base<T, E, false>
expected_move_assign_base(const expected_move_assign_base &rhs) = default; expected_move_assign_base(const expected_move_assign_base &rhs) = default;
expected_move_assign_base(expected_move_assign_base &&rhs) = default; expected_move_assign_base(expected_move_assign_base &&rhs) = default;
expected_move_assign_base & expected_move_assign_base &
operator=(const expected_move_assign_base &rhs) = default; operator=(const expected_move_assign_base &rhs) = default;
expected_move_assign_base & expected_move_assign_base &
operator=(expected_move_assign_base &&rhs) noexcept( operator=(expected_move_assign_base &&rhs) noexcept(
std::is_nothrow_move_constructible<T>::value std::is_nothrow_move_constructible<T>::value
&&std::is_nothrow_move_assignable<T>::value) { &&std::is_nothrow_move_assignable<T>::value) {
@@ -929,9 +939,9 @@ public:
/// \group and_then /// \group and_then
/// Carries out some operation which returns an expected on the stored object /// Carries out some operation which returns an expected on the stored object
/// if there is one. \requires `std::invoke(std::forward<F>(f), value())` /// if there is one. \requires `std::invoke(std::forward<F>(f), value())`
/// returns a `std::expected<U>` for some `U`. \returns Let `U` be the result /// returns an `expected<U>` for some `U`. \returns Let `U` be the result
/// of `std::invoke(std::forward<F>(f), value())`. Returns a /// of `std::invoke(std::forward<F>(f), value())`. Returns an
/// `std::expected<U>`. The return value is empty if `*this` is empty, /// `expected<U>`. The return value is empty if `*this` is empty,
/// otherwise the return value of `std::invoke(std::forward<F>(f), value())` /// otherwise the return value of `std::invoke(std::forward<F>(f), value())`
/// is returned. /// is returned.
/// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &; /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &;
@@ -983,9 +993,9 @@ public:
/// \group and_then /// \group and_then
/// Carries out some operation which returns an expected on the stored object /// Carries out some operation which returns an expected on the stored object
/// if there is one. \requires `std::invoke(std::forward<F>(f), value())` /// if there is one. \requires `std::invoke(std::forward<F>(f), value())`
/// returns a `std::expected<U>` for some `U`. \returns Let `U` be the result /// returns an `expected<U>` for some `U`. \returns Let `U` be the result
/// of `std::invoke(std::forward<F>(f), value())`. Returns a /// of `std::invoke(std::forward<F>(f), value())`. Returns an
/// `std::expected<U>`. The return value is empty if `*this` is empty, /// `expected<U>`. The return value is empty if `*this` is empty,
/// otherwise the return value of `std::invoke(std::forward<F>(f), value())` /// otherwise the return value of `std::invoke(std::forward<F>(f), value())`
/// is returned. /// is returned.
/// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &; /// \synopsis template <class F>\nconstexpr auto and_then(F &&f) &;
@@ -1042,7 +1052,8 @@ public:
!defined(TL_EXPECTED_GCC54) !defined(TL_EXPECTED_GCC54)
/// \brief Carries out some operation on the stored object if there is one. /// \brief Carries out some operation on the stored object if there is one.
/// \returns Let `U` be the result of `std::invoke(std::forward<F>(f), /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
/// value())`. Returns a `std::expected<U,E>`. If `*this` is unexpected, the /// value())`. If `U` is `void`, returns an `expected<monostate,E>, otherwise
// returns an `expected<U,E>`. If `*this` is unexpected, the
/// result is `*this`, otherwise an `expected<U,E>` is constructed from the /// result is `*this`, otherwise an `expected<U,E>` is constructed from the
/// return value of `std::invoke(std::forward<F>(f), value())` and is /// return value of `std::invoke(std::forward<F>(f), value())` and is
/// returned. /// returned.
@@ -1073,7 +1084,8 @@ public:
#else #else
/// \brief Carries out some operation on the stored object if there is one. /// \brief Carries out some operation on the stored object if there is one.
/// \returns Let `U` be the result of `std::invoke(std::forward<F>(f), /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
/// value())`. Returns a `std::expected<U,E>`. If `*this` is unexpected, the /// value())`. If `U` is `void`, returns an `expected<monostate,E>, otherwise
// returns an `expected<U,E>`. If `*this` is unexpected, the
/// result is `*this`, otherwise an `expected<U,E>` is constructed from the /// result is `*this`, otherwise an `expected<U,E>` is constructed from the
/// return value of `std::invoke(std::forward<F>(f), value())` and is /// return value of `std::invoke(std::forward<F>(f), value())` and is
/// returned. /// returned.
@@ -1122,7 +1134,8 @@ public:
/// \brief Carries out some operation on the stored unexpected object if there /// \brief Carries out some operation on the stored unexpected object if there
/// is one. /// is one.
/// \returns Let `U` be the result of `std::invoke(std::forward<F>(f), /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
/// value())`. Returns a `std::expected<T,U>`. If `*this` has an expected /// value())`. If `U` is `void`, returns an `expected<T,monostate>`, otherwise
/// returns an `expected<T,U>`. If `*this` has an expected
/// value, the result is `*this`, otherwise an `expected<T,U>` is constructed /// value, the result is `*this`, otherwise an `expected<T,U>` is constructed
/// from `make_unexpected(std::invoke(std::forward<F>(f), value()))` and is /// from `make_unexpected(std::invoke(std::forward<F>(f), value()))` and is
/// returned. /// returned.
@@ -1154,7 +1167,7 @@ public:
/// \brief Carries out some operation on the stored unexpected object if there /// \brief Carries out some operation on the stored unexpected object if there
/// is one. /// is one.
/// \returns Let `U` be the result of `std::invoke(std::forward<F>(f), /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
/// value())`. Returns a `std::expected<T,U>`. If `*this` has an expected /// value())`. Returns an `expected<T,U>`. If `*this` has an expected
/// value, the result is `*this`, otherwise an `expected<T,U>` is constructed /// value, the result is `*this`, otherwise an `expected<T,U>` is constructed
/// from `make_unexpected(std::invoke(std::forward<F>(f), value()))` and is /// from `make_unexpected(std::invoke(std::forward<F>(f), value()))` and is
/// returned. /// returned.
@@ -1630,6 +1643,7 @@ public:
/// \exclude /// \exclude
namespace detail { namespace detail {
template <class Exp> using exp_t = typename detail::decay_t<Exp>::error_type;
template <class Exp> using err_t = typename detail::decay_t<Exp>::error_type; template <class Exp> using err_t = typename detail::decay_t<Exp>::error_type;
template <class Exp, class Ret> using ret_t = expected<Ret, err_t<Exp>>; template <class Exp, class Ret> using ret_t = expected<Ret, err_t<Exp>>;
@@ -1639,7 +1653,7 @@ template <class Exp, class F,
*std::declval<Exp>())), *std::declval<Exp>())),
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr> detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
constexpr auto map_impl(Exp &&exp, F &&f) { constexpr auto map_impl(Exp &&exp, F &&f) {
using result = ret_t<Exp, Ret>; using result = ret_t<Exp, detail::decay_t<Ret>>;
return exp.has_value() ? result(detail::invoke(std::forward<F>(f), return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
*std::forward<Exp>(exp))) *std::forward<Exp>(exp)))
: result(unexpect, std::forward<Exp>(exp).error()); : result(unexpect, std::forward<Exp>(exp).error());
@@ -1664,8 +1678,8 @@ template <class Exp, class F,
*std::declval<Exp>())), *std::declval<Exp>())),
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr> detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
constexpr auto map_impl(Exp &&exp, F &&f) -> ret_t<Exp, Ret> { constexpr auto map_impl(Exp &&exp, F &&f) -> ret_t<Exp, detail::decay_t<Ret>> {
using result = ret_t<Exp, Ret>; using result = ret_t<Exp, detail::decay_t<Ret>>;
return exp.has_value() ? result(detail::invoke(std::forward<F>(f), return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
*std::forward<Exp>(exp))) *std::forward<Exp>(exp)))
@@ -1691,26 +1705,57 @@ auto map_impl(Exp &&exp, F &&f) -> expected<monostate, err_t<Exp>> {
!defined(TL_EXPECTED_GCC54) !defined(TL_EXPECTED_GCC54)
template <class Exp, class F, template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(), class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Exp>()))> *std::declval<Exp>())),
detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
constexpr auto map_error_impl(Exp &&exp, F &&f) { constexpr auto map_error_impl(Exp &&exp, F &&f) {
using result = ret_t<Exp, Ret>; using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
return exp.has_value() return exp.has_value()
? result(*std::forward<Exp>(exp)) ? result(*std::forward<Exp>(exp))
: result(unexpect, detail::invoke(std::forward<F>(f), : result(unexpect, detail::invoke(std::forward<F>(f),
std::forward<Exp>(exp).error())); std::forward<Exp>(exp).error()));
} }
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>
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));
}
detail::invoke(std::forward<F>(f),
std::forward<Exp>(exp).error());
return result(unexpect, monostate{});
}
#else #else
template <class Exp, class F, template <class Exp, class F,
class Ret = decltype(detail::invoke(std::declval<F>(), class Ret = decltype(detail::invoke(std::declval<F>(),
*std::declval<Exp>()))> *std::declval<Exp>())),
constexpr auto map_error_impl(Exp &&exp, F &&f) -> ret_t<Exp, Ret> { detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
using result = ret_t<Exp, Ret>; constexpr auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
using result = ret_t<Exp, detail::decay_t<Ret>>;
return exp.has_value() return exp.has_value()
? result(*std::forward<Exp>(exp)) ? result(*std::forward<Exp>(exp))
: result(unexpect, detail::invoke(std::forward<F>(f), : result(unexpect, detail::invoke(std::forward<F>(f),
std::forward<Exp>(exp).error())); std::forward<Exp>(exp).error()));
} }
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>
auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
using result = expected<exp_t<Exp>, monostate>;
if (exp.has_value()) {
return result(*std::forward<Exp>(exp));
}
detail::invoke(std::forward<F>(f),
std::forward<Exp>(exp).error());
return result(unexpect, monostate{});
}
#endif #endif
template <class Exp, class F, template <class Exp, class F,

View File

@@ -130,6 +130,15 @@ TEST_CASE("Map extensions", "[extensions.map]") {
STATIC_REQUIRE( STATIC_REQUIRE(
(std::is_same<decltype(ret), tl::expected<tl::monostate, int>>::value)); (std::is_same<decltype(ret), tl::expected<tl::monostate, int>>::value));
} }
// mapping functions which return references
{
tl::expected<int, int> e(42);
auto ret = e.map([](int& i) -> int& { return i; });
REQUIRE(ret);
REQUIRE(ret == 42);
}
} }
TEST_CASE("Map error extensions", "[extensions.map_error]") { TEST_CASE("Map error extensions", "[extensions.map_error]") {
@@ -191,6 +200,55 @@ TEST_CASE("Map error extensions", "[extensions.map_error]") {
REQUIRE(!ret); REQUIRE(!ret);
REQUIRE(ret.error() == 42); REQUIRE(ret.error() == 42);
} }
{
tl::expected<int, int> e = 21;
auto ret = e.map_error(ret_void);
REQUIRE(ret);
}
{
const tl::expected<int, int> e = 21;
auto ret = e.map_error(ret_void);
REQUIRE(ret);
}
{
tl::expected<int, int> e = 21;
auto ret = std::move(e).map_error(ret_void);
REQUIRE(ret);
}
{
const tl::expected<int, int> e = 21;
auto ret = std::move(e).map_error(ret_void);
REQUIRE(ret);
}
{
tl::expected<int, int> e(tl::unexpect, 21);
auto ret = e.map_error(ret_void);
REQUIRE(!ret);
}
{
const tl::expected<int, int> e(tl::unexpect, 21);
auto ret = e.map_error(ret_void);
REQUIRE(!ret);
}
{
tl::expected<int, int> e(tl::unexpect, 21);
auto ret = std::move(e).map_error(ret_void);
REQUIRE(!ret);
}
{
const tl::expected<int, int> e(tl::unexpect, 21);
auto ret = std::move(e).map_error(ret_void);
REQUIRE(!ret);
}
} }
TEST_CASE("And then extensions", "[extensions.and_then]") { TEST_CASE("And then extensions", "[extensions.and_then]") {