Support C++14

This commit is contained in:
Simon Brand
2017-10-02 14:00:56 +01:00
parent 6a7d7f49e3
commit d8b6da9b3b
3 changed files with 183 additions and 139 deletions

View File

@@ -19,4 +19,4 @@ set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/tests/main.cpp
add_executable(tests ${TEST_SOURCES})
target_link_libraries(tests Catch)
set_property(TARGET tests PROPERTY CXX_STANDARD 17)
set_property(TARGET tests PROPERTY CXX_STANDARD 14)

View File

@@ -106,12 +106,12 @@ template <class F, class... Us>
using invoke_result_t = typename invoke_result<F, Us...>::type;
template <class U>
using fixup_void = conditional_t<std::is_void_v<U>, monostate, U>;
using fixup_void = conditional_t<std::is_void<U>::value, monostate, U>;
// TODO check if we need std::invoke_result here
template <class F, class U> struct get_invoke_optional_ret {
using type = result_of_t<
conditional_t<std::is_lvalue_reference_v<F>,
conditional_t<std::is_lvalue_reference<F>::value,
typename std::remove_reference_t<F>::value_type &,
typename std::remove_reference_t<F>::value_type &&>(U)>;
};
@@ -126,6 +126,18 @@ using get_map_return = optional<fixup_void<get_invoke_ret<F, U>>>;
template <class F, class U>
using returns_void = std::is_void<get_invoke_ret<F, U>>;
template <class T>
using disable_if_optional = enable_if_t<!is_optional<T>::value>;
template <class T>
using enable_if_optional = enable_if_t<is_optional<T>::value>;
template <class T, class U>
using enable_if_ret_void = enable_if_t<returns_void<T &&, U>::value>;
template <class T, class U>
using disable_if_ret_void = enable_if_t<!returns_void<T &&, U>::value>;
}
struct in_place_t {
@@ -744,7 +756,7 @@ public:
}
}
template <class F> constexpr auto bind(F &&f) & {
template <class F> constexpr detail::invoke_result_t<F, T> bind(F &&f) & {
using result = detail::invoke_result_t<F, T>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
@@ -754,7 +766,7 @@ public:
return detail::invoke(std::forward<F>(f), value());
}
template <class F> constexpr auto bind(F &&f) && {
template <class F> constexpr detail::invoke_result_t<F, T> bind(F &&f) && {
using result = detail::invoke_result_t<F, T>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
@@ -765,7 +777,8 @@ public:
return detail::invoke(std::forward<F>(f), std::move(value()));
}
template <class F> constexpr auto bind(F &&f) const & {
template <class F>
constexpr detail::invoke_result_t<F, T> bind(F &&f) const & {
using result = detail::invoke_result_t<F, T>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
@@ -776,7 +789,8 @@ public:
return detail::invoke(std::forward<F>(f), value());
}
template <class F> constexpr auto bind(F &&f) const && {
template <class F>
constexpr detail::invoke_result_t<F, T> bind(F &&f) const && {
using result = detail::invoke_result_t<F, T>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
@@ -787,7 +801,8 @@ public:
return detail::invoke(std::forward<F>(f), std::move(value()));
}
template <class F, class E> constexpr auto bind(F &&f, E &&e) & {
template <class F, class E>
constexpr detail::invoke_result_t<F, T> bind(F &&f, E &&e) & {
using result = detail::invoke_result_t<F, T>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
@@ -801,7 +816,8 @@ public:
return ret;
}
template <class F, class E> constexpr auto bind(F &&f, E &&e) && {
template <class F, class E>
constexpr detail::invoke_result_t<F, T> bind(F &&f, E &&e) && {
using result = detail::invoke_result_t<F, T>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
@@ -815,7 +831,8 @@ public:
return ret;
}
template <class F, class E> constexpr auto bind(F &&f, E &&e) const & {
template <class F, class E>
constexpr detail::invoke_result_t<F, T> bind(F &&f, E &&e) const & {
using result = detail::invoke_result_t<F, T>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
@@ -829,7 +846,8 @@ public:
return ret;
}
template <class F, class E> constexpr auto bind(F &&f, E &&e) const && {
template <class F, class E>
constexpr detail::invoke_result_t<F, T> bind(F &&f, E &&e) const && {
using result = detail::invoke_result_t<F, T>;
static_assert(detail::is_optional<result>::value,
"F must return an optional");
@@ -843,128 +861,154 @@ public:
return ret;
}
template <class F> constexpr detail::get_map_return<F, T &> map(F &&f) & {
if
constexpr(detail::is_optional<F>::value) {
if (!f.has_value() || !has_value())
return nullopt;
if
constexpr(detail::returns_void<F, T &>::value) {
detail::invoke(std::forward<F>(f).value(), value());
return nullopt;
}
else {
return detail::invoke(std::forward<F>(f).value(), value());
}
}
else {
if (!has_value())
return nullopt;
if
constexpr(detail::returns_void<F, T &>::value) {
detail::invoke(std::forward<F>(f), value());
return {};
}
else {
return detail::invoke(std::forward<F>(f), value());
}
}
template <class F, detail::disable_if_optional<F> * = nullptr,
detail::disable_if_ret_void<F, T &> * = nullptr>
constexpr detail::get_map_return<F, T &> map(F &&f) & {
if (has_value())
return detail::invoke(std::forward<F>(f), value());
return nullopt;
}
template <class F> constexpr detail::get_map_return<F, T &&> map(F &&f) && {
if
constexpr(detail::is_optional<F>::value) {
if (!f.has_value() || !has_value())
return nullopt;
template <class F, detail::disable_if_optional<F> * = nullptr,
detail::enable_if_ret_void<F, T &> * = nullptr>
constexpr detail::get_map_return<F, T &> map(F &&f) & {
if (!has_value())
return nullopt;
if
constexpr(detail::returns_void<F, T &&>::value) {
detail::invoke(std::forward<F>(f).value(), std::move(value()));
return {};
}
else {
return detail::invoke(std::forward<F>(f).value(), std::move(value()));
}
}
else {
if (!has_value())
return nullopt;
if
constexpr(detail::returns_void<F, T &&>::value) {
detail::invoke(std::forward<F>(f), std::move(value()));
return {};
}
else {
return detail::invoke(std::forward<F>(f), std::move(value()));
}
}
detail::invoke(std::forward<F>(f), value());
return {};
}
template <class F>
constexpr detail::get_map_return<F, T &> map(F &&f) const & {
if
constexpr(detail::is_optional<F>::value) {
if (!f.has_value() || !has_value())
return nullopt;
template <class F, detail::enable_if_optional<F> * = nullptr,
detail::disable_if_ret_void<F, T &> * = nullptr>
constexpr detail::get_map_return<F, T &> map(F &&f) & {
if (!f.has_value() || !has_value())
return nullopt;
if
constexpr(detail::returns_void<F, T &>::value) {
detail::invoke(std::forward<F>(f).value(), value());
return {};
}
else {
return detail::invoke(std::forward<F>(f).value(), value());
}
}
else {
if (!has_value())
return nullopt;
if
constexpr(detail::returns_void<F, T &>::value) {
detail::invoke(std::forward<F>(f), value());
return {};
}
else {
return detail::invoke(std::forward<F>(f), value());
}
}
return detail::invoke(std::forward<F>(f).value(), value());
}
template <class F>
constexpr detail::get_map_return<F, T &&> map(F &&f) const && {
if
constexpr(detail::is_optional<F>::value) {
if (!f.has_value() || !has_value())
return nullopt;
template <class F, detail::enable_if_optional<F> * = nullptr,
detail::enable_if_ret_void<F, T &> * = nullptr>
constexpr detail::get_map_return<F, T &> map(F &&f) & {
if (!f.has_value() || !has_value())
return nullopt;
if
constexpr(detail::returns_void<F, T &&>::value) {
detail::invoke(std::forward<F>(f).value(), std::move(value()));
return {};
}
else {
return detail::invoke(std::forward<F>(f).value(), std::move(value()));
}
}
else {
if (!has_value())
return nullopt;
detail::invoke(std::forward<F>(f).value(), value());
return {};
}
if
constexpr(detail::returns_void<F, T &&>::value) {
detail::invoke(std::forward<F>(f), std::move(value()));
return {};
}
else {
return detail::invoke(std::forward<F>(f), std::move(value()));
}
}
template <class F, detail::disable_if_optional<F> * = nullptr,
detail::disable_if_ret_void<F, T &&> * = nullptr>
constexpr detail::get_map_return<F, T &&> map(F &&f) && {
if (has_value())
return detail::invoke(std::forward<F>(f), std::move(value()));
return {};
}
template <class F, detail::disable_if_optional<F> * = nullptr,
detail::enable_if_ret_void<F, T &&> * = nullptr>
constexpr detail::get_map_return<F, T &&> map(F &&f) && {
if (!has_value())
return nullopt;
detail::invoke(std::forward<F>(f), std::move(value()));
return {};
}
template <class F, detail::enable_if_optional<F> * = nullptr,
detail::disable_if_ret_void<F, T &&> * = nullptr>
constexpr detail::get_map_return<F, T &&> map(F &&f) && {
if (!f.has_value() || !has_value())
return nullopt;
return detail::invoke(std::forward<F>(f).value(), std::move(value()));
}
template <class F, detail::enable_if_optional<F> * = nullptr,
detail::enable_if_ret_void<F, T &&> * = nullptr>
constexpr detail::get_map_return<F, T &&> map(F &&f) && {
if (!f.has_value() || !has_value())
return nullopt;
detail::invoke(std::forward<F>(f).value(), std::move(value()));
return {};
}
template <class F, detail::disable_if_optional<F> * = nullptr,
detail::disable_if_ret_void<F, T const &> * = nullptr>
constexpr detail::get_map_return<F, T const &> map(F &&f) const & {
if (has_value())
return detail::invoke(std::forward<F>(f), value());
return nullopt;
}
template <class F, detail::disable_if_optional<F> * = nullptr,
detail::enable_if_ret_void<F, T const &> * = nullptr>
constexpr detail::get_map_return<F, T const &> map(F &&f) const & {
if (!has_value())
return nullopt;
detail::invoke(std::forward<F>(f), value());
return {};
}
template <class F, detail::enable_if_optional<F> * = nullptr,
detail::disable_if_ret_void<F, T const &> * = nullptr>
constexpr detail::get_map_return<F, T const &> map(F &&f) const & {
if (!f.has_value() || !has_value())
return nullopt;
return detail::invoke(std::forward<F>(f).value(), value());
}
template <class F, detail::enable_if_optional<F> * = nullptr,
detail::enable_if_ret_void<F, T const &> * = nullptr>
constexpr detail::get_map_return<F, T const &> map(F &&f) const & {
if (!f.has_value() || !has_value())
return nullopt;
detail::invoke(std::forward<F>(f).value(), value());
return {};
}
template <class F, detail::disable_if_optional<F> * = nullptr,
detail::disable_if_ret_void<F, T const &&> * = nullptr>
constexpr detail::get_map_return<F, T const &&> map(F &&f) const && {
if (has_value())
return detail::invoke(std::forward<F>(f), std::move(value()));
return nullopt;
}
template <class F, detail::disable_if_optional<F> * = nullptr,
detail::enable_if_ret_void<F, T const &&> * = nullptr>
constexpr detail::get_map_return<F, T const &&> map(F &&f) const && {
if (!has_value())
return nullopt;
detail::invoke(std::forward<F>(f), std::move(value()));
return {};
}
template <class F, detail::enable_if_optional<F> * = nullptr,
detail::disable_if_ret_void<F, T const &&> * = nullptr>
constexpr detail::get_map_return<F, T const &&> map(F &&f) const && {
if (!f.has_value() || !has_value())
return nullopt;
return detail::invoke(std::forward<F>(f).value(), std::move(value()));
}
template <class F, detail::enable_if_optional<F> * = nullptr,
detail::enable_if_ret_void<F, T const &&> * = nullptr>
constexpr detail::get_map_return<F, T &> map(F &&f) const && {
if (!f.has_value() || !has_value())
return nullopt;
detail::invoke(std::forward<F>(f).value(), std::move(value()));
return {};
}
};
template <class T> optional(T) -> optional<T>;
//template <class T> optional(T)->optional<T>;
}

View File

@@ -1,18 +1,18 @@
#include "catch.hpp"
#include "optional.hpp"
template <class> struct TC;
// What is Clang Format up to?!
TEST_CASE("Monadic operations",
"[monadic]"){SECTION("map"){// lhs is empty
tl::optional<int> o1;
auto o1r = o1.map([](int i) { return i + 2; });
static_assert(std::is_same_v<decltype(o1r), tl::optional<int>>);
static_assert(std::is_same<decltype(o1r), tl::optional<int>>::value);
REQUIRE(!o1r);
// lhs has value
tl::optional<int> o2 = 40;
auto o2r = o2.map([](int i) { return i + 2; });
static_assert(std::is_same_v<decltype(o2r), tl::optional<int>>);
static_assert(std::is_same<decltype(o2r), tl::optional<int>>::value);
REQUIRE(o2r.value() == 42);
struct rval_call_map {
@@ -22,33 +22,33 @@ struct rval_call_map {
// ensure that function object is forwarded
tl::optional<int> o3 = 42;
auto o3r = o3.map(rval_call_map{});
static_assert(std::is_same_v<decltype(o3r), tl::optional<double>>);
static_assert(std::is_same<decltype(o3r), tl::optional<double>>::value);
REQUIRE(o3r.value() == 42);
// ensure that lhs is forwarded
tl::optional<int> o4 = 40;
auto o4r = std::move(o4).map([](int &&i) { return i + 2; });
static_assert(std::is_same_v<decltype(o4r), tl::optional<int>>);
static_assert(std::is_same<decltype(o4r), tl::optional<int>>::value);
REQUIRE(o4r.value() == 42);
// ensure that lhs is const-propagated
const tl::optional<int> o5 = 40;
auto o5r = o5.map([](const int &i) { return i + 2; });
static_assert(std::is_same_v<decltype(o5r), tl::optional<int>>);
static_assert(std::is_same<decltype(o5r), tl::optional<int>>::value);
REQUIRE(o5r.value() == 42);
// test applicative functor
tl::optional<int> o6 = 40;
auto f6 = tl::optional{[](const int &i) { return i + 2; }};
auto f6 = tl::make_optional([](const int &i) { return i + 2; });
auto o6r = o6.map(f6);
static_assert(std::is_same_v<decltype(o6r), tl::optional<int>>);
static_assert(std::is_same<decltype(o6r), tl::optional<int>>::value);
REQUIRE(o6r.value() == 42);
// test void return
tl::optional<int> o7 = 40;
auto f7 = tl::optional{[](const int &i) { return; }};
auto f7 = tl::make_optional([](const int &i) { return; });
auto o7r = o7.map(f7);
static_assert(std::is_same_v<decltype(o7r), tl::optional<tl::monostate>>);
static_assert(std::is_same<decltype(o7r), tl::optional<tl::monostate>>::value);
REQUIRE(o6r.has_value());
}
@@ -57,25 +57,25 @@ SECTION("bind") {
// lhs is empty
tl::optional<int> o1;
auto o1r = o1.bind([](int i) { return tl::optional<float>{42}; });
static_assert(std::is_same_v<decltype(o1r), tl::optional<float>>);
static_assert(std::is_same<decltype(o1r), tl::optional<float>>::value);
REQUIRE(!o1r);
// lhs has value
tl::optional<int> o2 = 12;
auto o2r = o2.bind([](int i) { return tl::optional<float>{42}; });
static_assert(std::is_same_v<decltype(o2r), tl::optional<float>>);
static_assert(std::is_same<decltype(o2r), tl::optional<float>>::value);
REQUIRE(o2r.value() == 42.f);
// lhs is empty, rhs returns empty
tl::optional<int> o3;
auto o3r = o3.bind([](int i) { return tl::optional<float>{}; });
static_assert(std::is_same_v<decltype(o3r), tl::optional<float>>);
static_assert(std::is_same<decltype(o3r), tl::optional<float>>::value);
REQUIRE(!o3r);
// rhs returns empty
tl::optional<int> o4 = 12;
auto o4r = o4.bind([](int i) { return tl::optional<float>{}; });
static_assert(std::is_same_v<decltype(o4r), tl::optional<float>>);
static_assert(std::is_same<decltype(o4r), tl::optional<float>>::value);
REQUIRE(!o4r);
struct rval_call_bind {
@@ -85,20 +85,20 @@ SECTION("bind") {
// ensure that function object is forwarded
tl::optional<int> o5 = 42;
auto o5r = o5.bind(rval_call_bind{});
static_assert(std::is_same_v<decltype(o5r), tl::optional<double>>);
static_assert(std::is_same<decltype(o5r), tl::optional<double>>::value);
REQUIRE(o5r.value() == 42);
// ensure that lhs is forwarded
tl::optional<int> o6 = 42;
auto o6r =
std::move(o6).bind([](int &&i) { return tl::optional<double>(i); });
static_assert(std::is_same_v<decltype(o6r), tl::optional<double>>);
static_assert(std::is_same<decltype(o6r), tl::optional<double>>::value);
REQUIRE(o6r.value() == 42);
// ensure that function object is const-propagated
const tl::optional<int> o7 = 42;
auto o7r = o7.bind([](const int &i) { return tl::optional<double>(i); });
static_assert(std::is_same_v<decltype(o7r), tl::optional<double>>);
static_assert(std::is_same<decltype(o7r), tl::optional<double>>::value);
REQUIRE(o7r.value() == 42);
}
}