Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add get and non-returning effect/effect_one for variant #290

Merged
merged 1 commit into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
Dobiasd marked this conversation as resolved.
Show resolved Hide resolved
"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
Dobiasd marked this conversation as resolved.
Show resolved Hide resolved
{
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>());
}