rec/fplus/internal/composition.hpp
2020-03-18 14:42:46 +08:00

112 lines
3.7 KiB
C++
Executable File

// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
// https://github.com/Dobiasd/FunctionalPlus
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#include <tuple>
#include <utility>
#include <fplus/internal/invoke.hpp>
namespace fplus
{
namespace internal
{
// source: https://codereview.stackexchange.com/a/63893
// note: the code in the link above is called with the arguments in reverse order
template <typename... Fs>
class compose_impl
{
static constexpr std::size_t size = sizeof...(Fs);
static_assert(size > 1,
"Invalid number of functions to compose, minimum is two.");
public:
compose_impl(Fs&&... fs) : _functionTuple(std::forward<Fs>(fs)...)
{
}
template <typename... Ts>
auto operator()(Ts&&... ts) const
{
return _apply(std::integral_constant<std::size_t, 0>{},
std::forward<Ts>(ts)...);
}
private:
template <std::size_t N, typename... Ts>
auto _apply(std::integral_constant<std::size_t, N>, Ts&&... ts) const
{
return _apply(std::integral_constant<std::size_t, N + 1>{},
std::get<N>(_functionTuple)(std::forward<Ts>(ts)...));
}
template <typename... Ts>
auto _apply(std::integral_constant<std::size_t, size - 1>, Ts&&... ts) const
{
return internal::invoke(std::get<size - 1>(_functionTuple),
std::forward<Ts>(ts)...);
}
std::tuple<Fs...> _functionTuple;
};
// Is BinaryLift really correct?
template <typename Tuple, typename BinaryLift>
auto compose_binary_lift_impl(std::integral_constant<std::size_t, 1>,
const Tuple& tup,
const BinaryLift& lifter)
{
return lifter(std::get<0>(tup), std::get<1>(tup));
}
template <std::size_t N, typename Tuple, typename BinaryLift>
auto compose_binary_lift_impl(std::integral_constant<std::size_t, N>,
const Tuple& tup,
const BinaryLift& lifter)
{
return lifter(
compose_binary_lift_impl(
std::integral_constant<std::size_t, N - 1>{}, tup, lifter),
std::get<N>(tup));
}
template <typename BinaryLift, typename... Callables>
auto compose_binary_lift(const BinaryLift& lifter, Callables&&... args)
{
static_assert(sizeof...(Callables) > 1,
"Invalid number of functions to compose, minimum is two.");
const auto tup = std::forward_as_tuple(std::forward<Callables>(args)...);
return compose_binary_lift_impl(
std::integral_constant<std::size_t, sizeof...(Callables) - 1>{},
tup,
lifter);
}
// concentrate asserts in this method. Lambda is provided by the library.
template <typename Lambda, typename F, typename G>
auto logical_binary_op(Lambda op, F f, G g)
{
// Perfect-forwarding might move twice, if we add a requirement on F and G,
// that might not be an issue.
return [op, f, g](auto x) {
internal::trigger_static_asserts<internal::unary_function_tag,
F,
decltype(x)>();
internal::trigger_static_asserts<internal::unary_function_tag,
G,
decltype(x)>();
using FRes = std::decay_t<internal::invoke_result_t<F, decltype(x)>>;
using GRes = std::decay_t<internal::invoke_result_t<G, decltype(x)>>;
static_assert(std::is_same<FRes, bool>::value, "Must return bool.");
static_assert(std::is_same<GRes, bool>::value, "Must return bool.");
return op(f, g, x);
};
}
}
}