diff --git a/include/fplus/variant.hpp b/include/fplus/variant.hpp index 98ef382c..c6369278 100644 --- a/include/fplus/variant.hpp +++ b/include/fplus/variant.hpp @@ -9,7 +9,6 @@ #include #include -#include #include #include @@ -267,6 +266,30 @@ struct variant return nothing>(); } + template + void effect_one(F f) const + { + using T = typename internal::function_first_input_type::type; + using Ret = internal::invoke_result_t; + internal::trigger_static_asserts(); + + static_assert( + internal::is_one_of< + typename internal::function_first_input_type::type, + Types...>::value + , "Function input must match one variant type."); + + static_assert(std::is_same, void>::value, + "Function must return void type."); + + const auto ptr = + std::get::value>(shared_ptrs_); + if (ptr) + { + internal::invoke(f, *ptr); + } + } + template auto visit(Fs ... fs) const -> typename internal::unary_function_result_type< @@ -308,11 +331,54 @@ struct variant internal::type_set_eq>::value, "Functions do not cover all possible types."); - const auto results = justs(visit_helper(fs...)); + static_assert(!std::is_same, void>::value, + "Function must return non-void type."); + + const auto results = justs(go_visit_one(fs...)); assert(size_of_cont(results) == 1); return head(results); } + template + void effect(Fs ... fs) const + { + + static_assert( + sizeof...(Fs) >= std::tuple_size::value, + "Too few functions provided."); + + static_assert( + sizeof...(Fs) <= std::tuple_size::value, + "Too many functions provided."); + + + typedef typename internal::transform_parameter_pack< + std::tuple, + internal::unary_function_result_type, + Fs... + >::type return_types_tuple; + + typedef typename internal::transform_parameter_pack< + std::tuple, + internal::function_first_input_type, + Fs... + >::type function_first_input_types_tuple; + + static_assert( + internal::is_unique::value, + "Only one function per input type allowed."); + + static_assert( + internal::are_same::value, + "All Functions must return the same type."); + + static_assert( + internal::type_set_eq>::value, + "Functions do not cover all possible types."); + + go_effect_one(fs...); + } + template variant transform(Fs ... fs) const { @@ -347,17 +413,36 @@ struct variant return visit(fs...); } + template + maybe get() const + { + return visit_one(identity); + } + private: template - std::vector> visit_helper(F f) const + std::vector> go_visit_one(F f) const { return {visit_one(f)}; } template - std::vector> visit_helper(F f, Fs ... fs) const + std::vector> go_visit_one(F f, Fs ... fs) const + { + return fplus::append(go_visit_one(f), go_visit_one(fs...)); + } + + template + void go_effect_one(F f) const + { + effect_one(f); + } + + template + void go_effect_one(F f, Fs ... fs) const { - return fplus::append(visit_helper(f), visit_helper(fs...)); + go_effect_one(f); + go_effect_one(fs...); } typedef typename internal::transform_parameter_pack< diff --git a/include_all_in_one/include/fplus/fplus.hpp b/include_all_in_one/include/fplus/fplus.hpp index 9345ebd8..32acc18e 100644 --- a/include_all_in_one/include/fplus/fplus.hpp +++ b/include_all_in_one/include/fplus/fplus.hpp @@ -14068,7 +14068,6 @@ class stopwatch -#include #include #include @@ -14326,6 +14325,30 @@ struct variant return nothing>(); } + template + void effect_one(F f) const + { + using T = typename internal::function_first_input_type::type; + using Ret = internal::invoke_result_t; + internal::trigger_static_asserts(); + + static_assert( + internal::is_one_of< + typename internal::function_first_input_type::type, + Types...>::value + , "Function input must match one variant type."); + + static_assert(std::is_same, void>::value, + "Function must return void type."); + + const auto ptr = + std::get::value>(shared_ptrs_); + if (ptr) + { + internal::invoke(f, *ptr); + } + } + template auto visit(Fs ... fs) const -> typename internal::unary_function_result_type< @@ -14367,11 +14390,54 @@ struct variant internal::type_set_eq>::value, "Functions do not cover all possible types."); - const auto results = justs(visit_helper(fs...)); + static_assert(!std::is_same, void>::value, + "Function must return non-void type."); + + const auto results = justs(go_visit_one(fs...)); assert(size_of_cont(results) == 1); return head(results); } + template + void effect(Fs ... fs) const + { + + static_assert( + sizeof...(Fs) >= std::tuple_size::value, + "Too few functions provided."); + + static_assert( + sizeof...(Fs) <= std::tuple_size::value, + "Too many functions provided."); + + + typedef typename internal::transform_parameter_pack< + std::tuple, + internal::unary_function_result_type, + Fs... + >::type return_types_tuple; + + typedef typename internal::transform_parameter_pack< + std::tuple, + internal::function_first_input_type, + Fs... + >::type function_first_input_types_tuple; + + static_assert( + internal::is_unique::value, + "Only one function per input type allowed."); + + static_assert( + internal::are_same::value, + "All Functions must return the same type."); + + static_assert( + internal::type_set_eq>::value, + "Functions do not cover all possible types."); + + go_effect_one(fs...); + } + template variant transform(Fs ... fs) const { @@ -14406,17 +14472,36 @@ struct variant return visit(fs...); } + template + maybe get() const + { + return visit_one(identity); + } + private: template - std::vector> visit_helper(F f) const + std::vector> go_visit_one(F f) const { return {visit_one(f)}; } template - std::vector> visit_helper(F f, Fs ... fs) const + std::vector> go_visit_one(F f, Fs ... fs) const + { + return fplus::append(go_visit_one(f), go_visit_one(fs...)); + } + + template + void go_effect_one(F f) const + { + effect_one(f); + } + + template + void go_effect_one(F f, Fs ... fs) const { - return fplus::append(visit_helper(f), visit_helper(fs...)); + go_effect_one(f); + go_effect_one(fs...); } typedef typename internal::transform_parameter_pack< diff --git a/test/variant_test.cpp b/test/variant_test.cpp index 890dec0c..8345906f 100644 --- a/test/variant_test.cpp +++ b/test/variant_test.cpp @@ -28,6 +28,16 @@ namespace { return true; } + void print_int_effect(int x) + { + print_output = "int " + std::to_string(x); + } + + void print_string_effect(const std::string& str) + { + print_output = "string " + str; + } + std::string show_int(int x) { return fplus::show(x); @@ -135,3 +145,43 @@ TEST_CASE("variant_test - visit") // should not compile //std::cout << int_or_string.visit(show_int) << std::endl; } + +TEST_CASE("variant_test - effect") +{ + using namespace fplus; + + // should not compile + //int_or_double.effect_one(print_string); + + fplus::variant int_or_string(3); + // + REQUIRE(int_or_string.is()); + REQUIRE_FALSE(int_or_string.is()); + // + int_or_string.effect(print_int_effect, print_string_effect); + REQUIRE_EQ(print_output, "int 3"); + print_output.clear(); + + const auto transform_result = + int_or_string.transform(show_int, show_string); + transform_result.effect_one(print_string_effect); + REQUIRE_EQ(print_output, "string 3"); + print_output.clear(); +} + +TEST_CASE("variant_test - get") +{ + using namespace fplus; + fplus::variant int_or_string_i(3); + fplus::variant int_or_string_s(std::string("hi")); + + REQUIRE_EQ(int_or_string_i.get(), just(3)); + REQUIRE_EQ(int_or_string_i.get(), nothing()); + + REQUIRE_EQ(int_or_string_s.get(), just("hi")); + REQUIRE_EQ(int_or_string_s.get(), nothing()); + + // should not compile (type not in variant) + // REQUIRE_EQ(int_or_string_i.get(), nothing()); + // REQUIRE_EQ(int_or_string_s.get(), nothing()); +} \ No newline at end of file