File: composition.hpp

package info (click to toggle)
libfplus 0.2.13-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,904 kB
  • sloc: cpp: 27,543; javascript: 634; sh: 105; python: 103; makefile: 6
file content (111 lines) | stat: -rw-r--r-- 3,822 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// 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);
    };
}
}
}