112 lines
3.7 KiB
C++
Executable File
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);
|
|
};
|
|
}
|
|
}
|
|
}
|