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 6Print function 7============== 8 9Say, for example, we would like to write a print function. We could start by writing the function that prints using `std::cout`, like this: 10 11 BOOST_HOF_STATIC_LAMBDA_FUNCTION(print) = [](const auto& x) 12 { 13 std::cout << x << std::endl; 14 }; 15 16However, there is lot of things that don't print directly to `std::cout` such as `std::vector` or `std::tuple`. Instead, we want to iterate over these data structures and print each element in them. 17 18Overloading 19----------- 20 21Boost.HigherOrderFunctions provides several ways to do overloading. One of the ways is with the [`first_of`](/include/boost/hof/conditional) adaptor which will pick the first function that is callable. This allows ordering the functions based on which one is more important. So then the first function will print to `std::cout` if possible otherwise we will add an overload to print a range: 22 23 24 BOOST_HOF_STATIC_LAMBDA_FUNCTION(print) = first_of( 25 [](const auto& x) -> decltype(std::cout << x, void()) 26 { 27 std::cout << x << std::endl; 28 }, 29 [](const auto& range) 30 { 31 for(const auto& x:range) std::cout << x << std::endl; 32 } 33 ); 34 35The `-> decltype(std::cout << x, void())` is added to the function to constrain it on whether `std::cout << x` is a valid expression. Then the `void()` is used to return `void` from the function. So, now the function can be called with a vector: 36 37 std::vector<int> v = { 1, 2, 3, 4 }; 38 print(v); 39 40This will print each element in the vector. 41 42We can also constrain the second overload as well, which will be important to add more overloads. So a `for` range loop calls `begin` and `end` to iterated over the range, but we will need some helper function in order to call `std::begin` using ADL lookup: 43 44 namespace adl { 45 46 using std::begin; 47 48 template<class R> 49 auto adl_begin(R&& r) BOOST_HOF_RETURNS(begin(r)); 50 } 51 52Now we can add `-> decltype(std::cout << *adl::adl_begin(range), void())` to the second function to constrain it to ranges: 53 54 BOOST_HOF_STATIC_LAMBDA_FUNCTION(print) = first_of( 55 [](const auto& x) -> decltype(std::cout << x, void()) 56 { 57 std::cout << x << std::endl; 58 }, 59 [](const auto& range) -> decltype(std::cout << *adl::adl_begin(range), void()) 60 { 61 for(const auto& x:range) std::cout << x << std::endl; 62 } 63 ); 64 65So now calling this will work: 66 67 std::vector<int> v = { 1, 2, 3, 4 }; 68 print(v); 69 70And print out: 71 72 1 73 2 74 3 75 4 76 77Tuples 78------ 79 80We could extend this to printing tuples as well. We will need to combine a couple of functions to make a `for_each_tuple`, which lets us call a function for each element. First, the [`proj`](/include/boost/hof/by) adaptor will let us apply a function to each argument passed in, and the [`unpack`](/include/boost/hof/unpack) adaptor will unpack the elements of a tuple and apply them to the function: 81 82 BOOST_HOF_STATIC_LAMBDA_FUNCTION(for_each_tuple) = [](const auto& sequence, auto f) 83 { 84 return unpack(proj(f))(sequence); 85 }; 86 87So now if we call: 88 89 for_each_tuple(std::make_tuple(1, 2, 3), [](auto i) 90 { 91 std::cout << i << std::endl; 92 }); 93 94This will print out: 95 96 1 97 2 98 3 99 100We can integrate this into our `print` function by adding an additional overload: 101 102 BOOST_HOF_STATIC_LAMBDA_FUNCTION(print) = first_of( 103 [](const auto& x) -> decltype(std::cout << x, void()) 104 { 105 std::cout << x << std::endl; 106 }, 107 [](const auto& range) -> decltype(std::cout << *adl::adl_begin(range), void()) 108 { 109 for(const auto& x:range) std::cout << x << std::endl; 110 }, 111 [](const auto& tuple) 112 { 113 for_each_tuple(tuple, [](const auto& x) 114 { 115 std::cout << x << std::endl; 116 }); 117 } 118 ); 119 120So now we can call `print` with a tuple: 121 122 print(std::make_tuple(1, 2, 3)); 123 124And it will print out: 125 126 1 127 2 128 3 129 130Recursive 131--------- 132 133Even though this will print for ranges and tuples, if we were to nest a range into a tuple this would not work. What we need to do is make the function call itself recursively. Even though we are using lambdas, we can easily make this recursive using the [`fix`](/include/boost/hof/fix) adaptor. This implements a fix point combinator, which passes the function(i.e. itself) in as the first argument. 134 135So now we add an additional arguments called `self` which is the `print` function itself. This extra argument is called by the [`fix`](/include/boost/hof/fix) adaptor, and so the user would still call this function with a single argument: 136 137 BOOST_HOF_STATIC_LAMBDA_FUNCTION(print) = fix(first_of( 138 [](auto, const auto& x) -> decltype(std::cout << x, void()) 139 { 140 std::cout << x << std::endl; 141 }, 142 [](auto self, const auto& range) -> decltype(self(*adl::adl_begin(range)), void()) 143 { 144 for(const auto& x:range) self(x); 145 }, 146 [](auto self, const auto& tuple) 147 { 148 return for_each_tuple(tuple, self); 149 } 150 )); 151 152This will let us print nested structures: 153 154 std::vector<int> v = { 1, 2, 3, 4 }; 155 auto t = std::make_tuple(1, 2, 3, 4); 156 auto m = std::make_tuple(3, v, t); 157 print(m); 158 159Which outputs this: 160 161 3 162 1 163 2 164 3 165 4 166 1 167 2 168 3 169 4 170 171Variadic 172-------- 173 174We can also make this `print` function variadic, so it prints every argument passed into it. We can use the [`proj`](/include/boost/hof/by) adaptor, which already calls the function on every argument passed in. First, we just rename our original `print` function to `simple_print`: 175 176 BOOST_HOF_STATIC_LAMBDA_FUNCTION(simple_print) = fix(first_of( 177 [](auto, const auto& x) -> decltype(std::cout << x, void()) 178 { 179 std::cout << x << std::endl; 180 }, 181 [](auto self, const auto& range) -> decltype(self(*adl::adl_begin(range)), void()) 182 { 183 for(const auto& x:range) self(x); 184 }, 185 [](auto self, const auto& tuple) 186 { 187 return for_each_tuple(tuple, self); 188 } 189 )); 190 191And then apply the [`proj`](/include/boost/hof/by) adaptor to `simple_print`: 192 193 BOOST_HOF_STATIC_LAMBDA_FUNCTION(print) = proj(simple_print); 194 195Now we can call `print` with several arguments: 196 197 print(5, "Hello world"); 198 199Which outputs: 200 201 5 202 Hello world 203