• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<!-- Copyright 2018 Paul Fultz II
2     Distributed under the Boost Software License, Version 1.0.
3     (http://www.boost.org/LICENSE_1_0.txt)
4-->
5
6Getting started
7===============
8
9Higher-order functions
10----------------------
11
12A core part of this library is higher-order functions. A higher-order function is a function that either takes a function as its argument or returns a function. To be able to define higher-order functions, we must be able to refer functions as first-class objects. One example of a higher-order function is `std::accumulate`. It takes a custom binary operator as a parameter.
13
14One way to refer to a function is to use a function pointer(or a member function pointer). So if we had our own custom `sum` function, we could pass it directly to `std::accumulate`:
15
16    int sum(int x, int y)
17    {
18        return x + y;
19    }
20    // Pass sum to accumulate
21    std::vector<int> v = { 1, 2, 3 };
22    int total = std::accumulate(v.begin(), v.end(), 0, &sum);
23
24
25However, a function pointer can only refer to one function in an overload set of functions, and it requires explicit casting to select that overload.
26
27For example, if we had a templated `sum` function that we want to pass to `std::accumulate`, we would need an explicit cast:
28
29    template<class T, class U>
30    auto sum(T x, U y)
31    {
32        return x + y;
33    }
34
35    auto sum_int = (int (*)(int, int))&sum;
36    // Call integer overload
37    int i = sum_int(1, 2);
38    // Or pass to an algorithm
39    std::vector<int> v = { 1, 2, 3 };
40    int total = std::accumulate(v.begin(), v.end(), 0, sum_int);
41
42
43Function Objects
44----------------
45
46A function object allows the ability to encapsulate an entire overload set into one object. This can be done by defining a class that overrides the call operator like this:
47
48    // A sum function object
49    struct sum_f
50    {
51        template<class T, class U>
52        auto operator()(T x, U y) const
53        {
54            return x + y;
55        }
56    };
57
58There are few things to note about this. First, the call operator member function is always declared `const`, which is generally required to be used with Boost.HigherOrderFunctions.(Note: The [`mutable_`](/include/boost/hof/mutable) adaptor can be used to make a mutable function object have a `const` call operator, but this should generally be avoided). Secondly, the `sum_f` class must be constructed first before it can be called:
59
60    auto sum = sum_f();
61    // Call sum function
62    auto three = sum(1, 2);
63    // Or pass to an algorithm
64    std::vector<int> v = { 1, 2, 3 };
65    int total = std::accumulate(v.begin(), v.end(), 0, sum);
66
67Because the function is templated, it can be called on any type that has the plus `+` operator, not just integers. Futhermore, the `sum` variable can be used to refer to the entire overload set.
68
69Lifting functions
70-----------------
71
72Another alternative to defining a function object, is to lift the templated function using [`BOOST_HOF_LIFT`](/include/boost/hof/lift). This will turn the entire overload set into one object like a function object:
73
74    template<class T, class U>
75    auto sum(T x, U y)
76    {
77        return x + y;
78    }
79
80    // Pass sum to an algorithm
81    std::vector<int> v = { 1, 2, 3 };
82    int total = std::accumulate(v.begin(), v.end(), 0, BOOST_HOF_LIFT(sum));
83
84However, due to limitations in C++14 this will not preserve `constexpr`. In those cases, its better to use a function object.
85
86Declaring functions
87-------------------
88
89Now, this is useful for local functions. However, many times we want to write functions and make them available for others to use. Boost.HigherOrderFunctions provides [`BOOST_HOF_STATIC_FUNCTION`](/include/boost/hof/function) to declare the function object at the global or namespace scope:
90
91    BOOST_HOF_STATIC_FUNCTION(sum) = sum_f();
92
93The [`BOOST_HOF_STATIC_FUNCTION`](/include/boost/hof/function) declares a global variable following the best practices as outlined in [N4381](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html). This includes using `const` to avoid global state, compile-time initialization of the function object to avoid the [static initialization order fiasco](https://isocpp.org/wiki/faq/ctors#static-init-order), and an external address of the function object that is the same across translation units to avoid possible One-Definition-Rule(ODR) violations. In C++17, this can be achieved using an `inline` variable:
94
95    inline const constexpr auto sum = sum_f{};
96
97The [`BOOST_HOF_STATIC_FUNCTION`](/include/boost/hof/function) macro provides a portable way to do this that supports pre-C++17 compilers and MSVC.
98
99Adaptors
100--------
101
102Now we have defined the function as a function object, we can add new "enhancements" to the function. One enhancement is to write "extension" methods. The proposal [N4165](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4165.pdf) for Unified Call Syntax(UFCS) would have allowed a function call of `x.f(y)` to become `f(x, y)`. Without UFCS in C++, we can instead use pipable function which would transform `x | f(y)` into `f(x, y)`. To make `sum_f` function pipable using the [`pipable`](/include/boost/hof/pipable) adaptor, we can simply write:
103
104    BOOST_HOF_STATIC_FUNCTION(sum) = pipable(sum_f());
105
106Then the parameters can be piped into it, like this:
107
108    auto three = 1 | sum(2);
109
110Pipable function can be chained mutliple times just like the `.` operator:
111
112    auto four = 1 | sum(2) | sum(1);
113
114Alternatively, instead of using the `|` operator, pipable functions can be chained together using the [`flow`](/include/boost/hof/flow) adaptor:
115
116    auto four = flow(sum(2), sum(1))(1);
117
118Another enhancement that can be done to functions is defining named infix operators using the [`infix`](/include/boost/hof/infix) adaptor:
119
120    BOOST_HOF_STATIC_FUNCTION(sum) = infix(sum_f());
121
122And it could be called like this:
123
124    auto three = 1 <sum> 2;
125
126In addition, adaptors are provided that support simple functional operations such as [partial application](https://en.wikipedia.org/wiki/Partial_application) and [function composition](https://en.wikipedia.org/wiki/Function_composition):
127
128    auto add_1 = partial(sum)(1);
129    auto add_2 = compose(add_1, add_1);
130    auto three = add_2(1);
131
132Lambdas
133-------
134
135Writing function objects can be a little verbose. C++ provides lambdas which have a much terser syntax for defining functions. Of course, lambdas can work with all the adaptors in the library, however, if we want to declare a function using lambdas, [`BOOST_HOF_STATIC_FUNCTION`](/include/boost/hof/function) won't work. Instead, [`BOOST_HOF_STATIC_LAMBDA_FUNCTION`](BOOST_HOF_STATIC_LAMBDA_FUNCTION) can be used to the declare the lambda as a function instead, this will initialize the function at compile-time and avoid possible ODR violations:
136
137    BOOST_HOF_STATIC_LAMBDA_FUNCTION(sum) = [](auto x, auto y)
138    {
139        return x + y;
140    };
141
142Additionally, adaptors can be used, so the pipable version of `sum` can be written like this:
143
144    // Pipable sum
145    BOOST_HOF_STATIC_LAMBDA_FUNCTION(sum) = pipable([](auto x, auto y)
146    {
147        return x + y;
148    });
149
150