Skip to content

Commit

Permalink
Add non-returning effect/effect_one for variant
Browse files Browse the repository at this point in the history
Add get for variant
  • Loading branch information
tom91136 authored and Dobiasd committed Dec 26, 2023
1 parent db5bc4f commit ec95b80
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 10 deletions.
95 changes: 90 additions & 5 deletions include/fplus/variant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <fplus/container_common.hpp>
#include <fplus/maybe.hpp>

#include <iostream>
#include <memory>
#include <tuple>

Expand Down Expand Up @@ -267,6 +266,30 @@ struct variant
return nothing<std::decay_t<Ret>>();
}

template <typename F>
void effect_one(F f) const
{
using T = typename internal::function_first_input_type<F>::type;
using Ret = internal::invoke_result_t<F, T>;
internal::trigger_static_asserts<internal::unary_function_tag, F, T>();

static_assert(
internal::is_one_of<
typename internal::function_first_input_type<F>::type,
Types...>::value
, "Function input must match one variant type.");

static_assert(std::is_same<std::decay_t<Ret>, void>::value,
"Function must return void type.");

const auto ptr =
std::get<internal::get_index<T, Types...>::value>(shared_ptrs_);
if (ptr)
{
internal::invoke(f, *ptr);
}
}

template <typename ...Fs>
auto visit(Fs ... fs) const ->
typename internal::unary_function_result_type<
Expand Down Expand Up @@ -308,11 +331,54 @@ struct variant
internal::type_set_eq<function_first_input_types_tuple, std::tuple<Types...>>::value,
"Functions do not cover all possible types.");

const auto results = justs(visit_helper<Res>(fs...));
static_assert(!std::is_same<std::decay_t<Res>, void>::value,
"Function must return non-void type.");

const auto results = justs(go_visit_one<Res>(fs...));
assert(size_of_cont(results) == 1);
return head(results);
}

template <typename ...Fs>
void effect(Fs ... fs) const
{

static_assert(
sizeof...(Fs) >= std::tuple_size<shared_ptr_pack>::value,
"Too few functions provided.");

static_assert(
sizeof...(Fs) <= std::tuple_size<shared_ptr_pack>::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<function_first_input_types_tuple>::value,
"Only one function per input type allowed.");

static_assert(
internal::are_same<return_types_tuple>::value,
"All Functions must return the same type.");

static_assert(
internal::type_set_eq<function_first_input_types_tuple, std::tuple<Types...>>::value,
"Functions do not cover all possible types.");

go_effect_one(fs...);
}

template <typename ...Fs>
variant<Types...> transform(Fs ... fs) const
{
Expand Down Expand Up @@ -347,17 +413,36 @@ struct variant
return visit(fs...);
}

template <typename T>
maybe<T> get() const
{
return visit_one(identity<T>);
}

private:
template <typename Res, typename F>
std::vector<fplus::maybe<Res>> visit_helper(F f) const
std::vector<fplus::maybe<Res>> go_visit_one(F f) const
{
return {visit_one(f)};
}

template <typename Res, typename F, typename ...Fs>
std::vector<fplus::maybe<Res>> visit_helper(F f, Fs ... fs) const
std::vector<fplus::maybe<Res>> go_visit_one(F f, Fs ... fs) const
{
return fplus::append(go_visit_one<Res>(f), go_visit_one<Res>(fs...));
}

template <typename F>
void go_effect_one(F f) const
{
effect_one(f);
}

template <typename F, typename ...Fs>
void go_effect_one(F f, Fs ... fs) const
{
return fplus::append(visit_helper<Res>(f), visit_helper<Res>(fs...));
go_effect_one(f);
go_effect_one(fs...);
}

typedef typename internal::transform_parameter_pack<
Expand Down
95 changes: 90 additions & 5 deletions include_all_in_one/include/fplus/fplus.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14068,7 +14068,6 @@ class stopwatch



#include <iostream>
#include <memory>
#include <tuple>

Expand Down Expand Up @@ -14326,6 +14325,30 @@ struct variant
return nothing<std::decay_t<Ret>>();
}

template <typename F>
void effect_one(F f) const
{
using T = typename internal::function_first_input_type<F>::type;
using Ret = internal::invoke_result_t<F, T>;
internal::trigger_static_asserts<internal::unary_function_tag, F, T>();

static_assert(
internal::is_one_of<
typename internal::function_first_input_type<F>::type,
Types...>::value
, "Function input must match one variant type.");

static_assert(std::is_same<std::decay_t<Ret>, void>::value,
"Function must return void type.");

const auto ptr =
std::get<internal::get_index<T, Types...>::value>(shared_ptrs_);
if (ptr)
{
internal::invoke(f, *ptr);
}
}

template <typename ...Fs>
auto visit(Fs ... fs) const ->
typename internal::unary_function_result_type<
Expand Down Expand Up @@ -14367,11 +14390,54 @@ struct variant
internal::type_set_eq<function_first_input_types_tuple, std::tuple<Types...>>::value,
"Functions do not cover all possible types.");

const auto results = justs(visit_helper<Res>(fs...));
static_assert(!std::is_same<std::decay_t<Res>, void>::value,
"Function must return non-void type.");

const auto results = justs(go_visit_one<Res>(fs...));
assert(size_of_cont(results) == 1);
return head(results);
}

template <typename ...Fs>
void effect(Fs ... fs) const
{

static_assert(
sizeof...(Fs) >= std::tuple_size<shared_ptr_pack>::value,
"Too few functions provided.");

static_assert(
sizeof...(Fs) <= std::tuple_size<shared_ptr_pack>::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<function_first_input_types_tuple>::value,
"Only one function per input type allowed.");

static_assert(
internal::are_same<return_types_tuple>::value,
"All Functions must return the same type.");

static_assert(
internal::type_set_eq<function_first_input_types_tuple, std::tuple<Types...>>::value,
"Functions do not cover all possible types.");

go_effect_one(fs...);
}

template <typename ...Fs>
variant<Types...> transform(Fs ... fs) const
{
Expand Down Expand Up @@ -14406,17 +14472,36 @@ struct variant
return visit(fs...);
}

template <typename T>
maybe<T> get() const
{
return visit_one(identity<T>);
}

private:
template <typename Res, typename F>
std::vector<fplus::maybe<Res>> visit_helper(F f) const
std::vector<fplus::maybe<Res>> go_visit_one(F f) const
{
return {visit_one(f)};
}

template <typename Res, typename F, typename ...Fs>
std::vector<fplus::maybe<Res>> visit_helper(F f, Fs ... fs) const
std::vector<fplus::maybe<Res>> go_visit_one(F f, Fs ... fs) const
{
return fplus::append(go_visit_one<Res>(f), go_visit_one<Res>(fs...));
}

template <typename F>
void go_effect_one(F f) const
{
effect_one(f);
}

template <typename F, typename ...Fs>
void go_effect_one(F f, Fs ... fs) const
{
return fplus::append(visit_helper<Res>(f), visit_helper<Res>(fs...));
go_effect_one(f);
go_effect_one(fs...);
}

typedef typename internal::transform_parameter_pack<
Expand Down
50 changes: 50 additions & 0 deletions test/variant_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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, std::string> int_or_string(3);
//
REQUIRE(int_or_string.is<int>());
REQUIRE_FALSE(int_or_string.is<std::string>());
//
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, std::string> int_or_string_i(3);
fplus::variant<int, std::string> int_or_string_s(std::string("hi"));

REQUIRE_EQ(int_or_string_i.get<int>(), just(3));
REQUIRE_EQ(int_or_string_i.get<std::string>(), nothing<std::string>());

REQUIRE_EQ(int_or_string_s.get<std::string>(), just<std::string>("hi"));
REQUIRE_EQ(int_or_string_s.get<int>(), nothing<int>());

// should not compile (type not in variant)
// REQUIRE_EQ(int_or_string_i.get<char>(), nothing<char>());
// REQUIRE_EQ(int_or_string_s.get<char>(), nothing<char>());
}

0 comments on commit ec95b80

Please sign in to comment.