diff --git a/expected.hpp b/expected.hpp index 0d5b131..82d28e1 100644 --- a/expected.hpp +++ b/expected.hpp @@ -15,17 +15,88 @@ #define TL_EXPECTED_HPP #include +#include #include #include +#if (defined(_MSC_VER) && _MSC_VER == 1900) +#define TL_EXPECTED_MSVC2015 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9) +#define TL_EXPECTED_GCC49 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4) +#define TL_EXPECTED_GCC54 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9) +#define TL_EXPECTED_NO_CONSTRR +#endif + +#if __cplusplus > 201103L +#define TL_EXPECTED_CXX14 +#endif + +#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \ + defined(TL_EXPECTED_GCC49)) && \ + !defined(TL_EXPECTED_GCC50) +/// \exclude +#define TL_EXPECTED_11_CONSTEXPR +#else +/// \exclude +#define TL_EXPECTED_11_CONSTEXPR constexpr +#endif + namespace tl { namespace detail { template using enable_if_t = typename std::enable_if::type; +template using decay_t = typename std::decay::type; + +// std::invoke from C++17 +// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround +template >{}>, + int = 0> +constexpr auto invoke(Fn &&f, Args &&... args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); } -#ifndef TL_IN_PLACE_T_DEFINED -#define TL_IN_PLACE_T_DEFINED +template >{}>> +constexpr auto invoke(Fn &&f, Args &&... args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} + +// std::invoke_result from C++17 +template struct invoke_result_impl; + +template +struct invoke_result_impl< + F, decltype(invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = decltype(invoke(std::declval(), std::declval()...)); +}; + +template +using invoke_result = invoke_result_impl; + +template +using invoke_result_t = typename invoke_result::type; + +} // namespace detail + +#ifndef TL_IN_PLACE_MONOSTATE_DEFINED +#define TL_IN_PLACE_MONOSTATE_DEFINED +/// \brief Used to represent an optional with no data; essentially a bool +class monostate {}; + /// \brief A tag type to tell optional to construct its value in-place struct in_place_t { explicit in_place_t() = default; @@ -353,6 +424,66 @@ public: typedef E error_type; typedef unexpected unexpected_type; +#ifdef TL_EXPECTED_CXX14 + /// \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), + /// value())`. Returns a `std::expected`. The return value is empty if + /// `*this` is empty, otherwise an `expected` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. \group map \synopsis template auto map(F &&f) &; + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return map_impl(*this, std::forward(f)); + } + + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return map_impl(std::move(*this), std::forward(f)); + } + + template constexpr auto map(F &&f) const & { + return map_impl(*this, std::forward(f)); + } + + template constexpr auto map(F &&f) const && { + return map_impl(std::move(*this), std::forward(f)); + } +#else + /// \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), + /// value())`. Returns a `std::expected`. The return value is empty if + /// `*this` is empty, otherwise an `expected` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. \group map \synopsis template auto map(F &&f) &; + template + TL_EXPECTED_11_CONSTEXPR decltype(map_impl(std::declval(), + std::declval())) + map(F &&f) & { + return map_impl(*this, std::forward(f)); + } + + template + TL_EXPECTED_11_CONSTEXPR decltype(map_impl(std::declval(), + std::declval())) + map(F &&f) && { + return map_impl(std::move(*this), std::forward(f)); + } + + template + constexpr decltype(map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + constexpr expected() = default; template (std::forward(v)); } + +private: +#ifdef TL_EXPECTED_CX14 + template (), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + constexpr auto map_impl(Exp &&exp, F &&f) { + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : unexpected(std::forward(exp).error()); + } + + template (), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + auto map_impl(Exp &&exp, F &&f) { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return monostate{}; + } + + return unexpected(std::forward(exp).error()); + } +#else + template (), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + + constexpr auto map_impl(Exp &&exp, F &&f) + -> expected { + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : unexpected(std::forward(exp).error()); + } + + template (), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + + auto map_impl(Exp &&exp, F &&f) + -> expected { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return std::forward(exp).error(); + } + + return unexpected(std::forward(exp).error()); + } +#endif }; // TODO