Lumiera 0.pre.04
»edit your freedom«
Loading...
Searching...
No Matches
function-composition-test.cpp
Go to the documentation of this file.
1/*
2 FunctionComposition(Test) - functional composition and partial application
3
4 Copyright (C)
5 2009, Hermann Vosseler <Ichthyostega@web.de>
6
7  **Lumiera** is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License as published by the
9  Free Software Foundation; either version 2 of the License, or (at your
10  option) any later version. See the file COPYING for further details.
11
12* *****************************************************************/
13
19#include "lib/test/run.hpp"
21#include "lib/meta/typelist.hpp"
22#include "lib/meta/function.hpp"
25
26#include <tuple>
27
28namespace lib {
29namespace meta {
30namespace test {
31
32 using ::test::Test;
34 using lib::meta::_Fun;
35 using func::bindFirst;
36 using func::bindLast;
37 using func::PApply;
38 using func::BindToArgument;
39 using std::make_tuple;
40 using std::get;
41
42
43 namespace { // test functions
44
54
56 template<uint i>
57 Num<i>
59 )
60 {
61 return val1;
62 }
63
64 template<uint i, uint ii>
65 Num<i>
68 )
69 {
70 val1.o_ += val2.o_;
71 return val1;
72 }
73
74 template<uint i, uint ii, uint iii>
75 Num<i>
79 )
80 {
81 val1.o_ += val2.o_ + val3.o_;
82 return val1;
83 }
84
85 template<uint i, uint ii, uint iii, uint iv>
86 Num<i>
91 )
92 {
93 val1.o_ += val2.o_ + val3.o_ + val4.o_;
94 return val1;
95 }
96
97 template<uint i, uint ii, uint iii, uint iv, uint v>
98 Num<i>
100 , Num<ii> val2
101 , Num<iii> val3
102 , Num<iv> val4
103 , Num<v> val5
104 )
105 {
106 val1.o_ += val2.o_ + val3.o_ + val4.o_ + val5.o_;
107 return val1;
108 }
109
110
112 template<class II>
113 int
114 fun2 (II val)
115 {
116 return val.o_;
117 }
118
119 } // (End) test data
120
121
122
123
124
125
126 /**************************************************************************/
132 class FunctionComposition_test : public Test
133 {
134 virtual void
144
145
147 void
149 {
150 CHECK (6 == (fun13<1,2,3> (_1_, _2_, _3_)).o_ );
151 CHECK (6 == (fun13<1,1,1> (Num<1>(3), Num<1>(2), Num<1>(1))).o_ );
152
153 CHECK ( 1 == fun2 (fun11<1> (_1_)) );
154 CHECK ( 3 == fun2 (fun12<1,2> (_1_, _2_)) );
155 CHECK ( 6 == fun2 (fun13<1,2,3> (_1_, _2_, _3_)) );
156 CHECK (10 == fun2 (fun14<1,2,3,4> (_1_, _2_, _3_, _4_)) );
157 CHECK (15 == fun2 (fun15<1,2,3,4,5> (_1_, _2_, _3_, _4_, _5_)) );
158
159 CHECK ( 9 == fun2 (fun13<2,3,4> (_2_, _3_, _4_)) );
160 CHECK (18 == fun2 (fun13<5,6,7> (_5_, _6_, _7_)) );
161 CHECK (24 == fun2 (fun13<9,8,7> (_9_, _8_, _7_)) );
162 }
163
164
165 void
167 {
168 // Because the code of the partial function application is very technical,
169 // the following might serve as explanation what actually happens....
170 // (and actually it's a leftover from initial debugging)
171
172 typedef Num<1> Sig123(Num<1>, Num<2>, Num<3>); // signature of the original function
173
174 typedef Num<1> Sig23(Num<2>, Num<3>); // signature after having closed over the first argument
175 using F23 = function<Sig23>; // and a std::function object to hold such a function
176
177 Sig123& f = fun13<1,2,3>; // the actual input: a reference to the bare function
178
179
180 // Version1: do a direct argument binding----------------- //
181
182 using PH1 = std::_Placeholder<1>; // std::function argument placeholders
183 using PH2 = std::_Placeholder<2>;
184
185 PH1 ph1; // these empty structs are used to mark the arguments to be kept "open"
186 PH2 ph2;
187 Num<1> num18 (18); // ...and this value is for closing the first function argument
188
189 F23 fun_23 = std::bind (f, num18 // do the actual binding (i.e. close the first argument with a constant value)
190 , ph1
191 , ph2
192 );
193
194 int r1 = fun_23 (_2_,_3_).o_; // and invoke the resulting functor ("closure"), providing the remaining arguments
195 CHECK (23 == r1); // result ≡ num18 + _2_ + _3_ ≙ 18 + 2 + 3
196
197
198
199 // Version2: extract the binding arguments from a tuple--- //
200
201 using PartialArg = Tuple<Types<Num<1>, PH1, PH2>>; // Tuple type to hold the binding values. Note the placeholder types
202 PartialArg arg{num18, PH1(), PH2()}; // Value for partial application (the placeholders are default constructed)
203
204 fun_23 = std::bind (f, get<0>(arg) // now extract the values to bind from this tuple
205 , get<1>(arg)
206 , get<2>(arg)
207 );
208
209 int r2 = fun_23 (_2_,_3_).o_; // and invoke the resulting functor....
210 CHECK (23 == r2);
211
212 // function-closure.hpp defines a shorthand for this operation
213 fun_23 = func::bindArgTuple (f, arg);
214
215 int r3 = fun_23 (_2_,_3_).o_;
216 CHECK (23 == r3);
217
218
219
220 // Version3: let the PApply-template do the work for us--- //
221
222 using ArgTypes = Types<Num<1>>; // now package just the argument(s) to be applied into a tuple
224
226 // "bindFront" will close the parameters starting from left....
227
228 int r4 = fun_23 (_2_,_3_).o_; // invoke the resulting functor...
229 CHECK (23 == r4);
230
231
232
233
234
235
236 // Version4: as you'd typically do it in real life-------- //
237
238 fun_23 = func::bindFirst (f, Num<1>(18)); // use the convenience function API to close over a single value
239
240 int r5 = fun_23(_2_,_3_).o_; // invoke the resulting functor...
241 CHECK (23 == r5);
242
243
244
245 // what follows is the real unit test...
246 function<Sig123> func123{f}; // alternatively do it with an std::function object
248 int r6 = fun_23(_2_,_3_).o_;
249 CHECK (24 == r6);
250
251 using F12 = function<Num<1>(Num<1>, Num<2>)>;
252 F12 fun_12 = func::bindLast (f, Num<3>(20)); // close the *last* argument of a function
253 int r7 = fun_12(_1_,_2_).o_;
254 CHECK (23 == r7);
255
256 fun_12 = func::bindLast (func123, Num<3>(21)); // alternatively use a function object
257 int r8 = fun_12(_1_,_2_).o_;
258 CHECK (24 == r8);
259
260 Sig123* fP = &f; // a function pointer works too
262 int r9 = fun_12(_1_,_2_).o_;
263 CHECK (25 == r9);
264 // cover more cases....
265
266 CHECK (1 == (func::bindLast (fun11<1> , _1_ ) ( ) ).o_);
267 CHECK (1+3 == (func::bindLast (fun12<1,3> , _3_ ) (_1_) ).o_);
268 CHECK (1+3+5 == (func::bindLast (fun13<1,3,5> , _5_ ) (_1_,_3_) ).o_);
269 CHECK (1+3+5+7 == (func::bindLast (fun14<1,3,5,7> , _7_ ) (_1_,_3_,_5_) ).o_);
270 CHECK (1+3+5+7+9 == (func::bindLast (fun15<1,3,5,7,9>, _9_ ) (_1_,_3_,_5_,_7_)).o_);
271
272 CHECK (9+8+7+6+5 == (func::bindFirst(fun15<9,8,7,6,5>, _9_ ) (_8_,_7_,_6_,_5_)).o_);
273 CHECK ( 8+7+6+5 == (func::bindFirst( fun14<8,7,6,5>, _8_ ) (_7_,_6_,_5_)).o_);
274 CHECK ( 7+6+5 == (func::bindFirst( fun13<7,6,5>, _7_ ) (_6_,_5_)).o_);
275 CHECK ( 6+5 == (func::bindFirst( fun12<6,5>, _6_ ) (_5_)).o_);
276 CHECK ( 5 == (func::bindFirst( fun11<5>, _5_ ) ( )).o_);
277
278
279
280 // Finally a more convoluted example
281 // covering the general case of partial function closure:
282 typedef Num<5> Sig54321 (Num<5>, Num<4>, Num<3>, Num<2>, Num<1>); // Signature of the 5-argument function
283 typedef Num<5> Sig54 (Num<5>, Num<4>); // ...closing the last 3 arguments should yield this 2-argument function
284 using Args2Close = Types<Num<3>, Num<2>, Num<1>>; // Tuple type to hold the 3 argument values used for the closure
285
286 // Close the trailing 3 arguments of the 5-argument function...
288 ,make_tuple (_3_,_2_,_1_)
289 );
290
291 // apply the remaining argument values
292 Num<5> resN5 = fun_54(_5_,_4_);
293 CHECK (5+4+3+2+1 == resN5.o_);
294 }
295
296
297
298 void
300 {
301 typedef int Sig2(Num<1>);
302 typedef Num<1> Sig11(Num<1>);
303 typedef Num<1> Sig12(Num<1>,Num<2>);
307
308 Sig2 & ff = fun2< Num<1> >;
309 Sig11& f1 = fun11<1>;
314
315 CHECK (1 == func::chained(f1, ff) (_1_) );
316 CHECK (1+2 == func::chained(f2, ff) (_1_,_2_) );
317 CHECK (1+2+3 == func::chained(f3, ff) (_1_,_2_,_3_) );
318 CHECK (1+2+3+4 == func::chained(f4, ff) (_1_,_2_,_3_,_4_) );
319 CHECK (1+2+3+4+5 == func::chained(f5, ff) (_1_,_2_,_3_,_4_,_5_) );
320
321
322 function<Sig15> f5_fun = f5; // also works with function objects...
324 CHECK (1+2+3+4+5 == func::chained(f5_fun, ff ) (_1_,_2_,_3_,_4_,_5_) );
325 CHECK (1+2+3+4+5 == func::chained(f5, ff_fun) (_1_,_2_,_3_,_4_,_5_) );
326 CHECK (1+2+3+4+5 == func::chained(f5_fun, ff_fun) (_1_,_2_,_3_,_4_,_5_) );
327 }
328
329
330
331 void
333 {
335
341
342 typedef Num<5> SigA5(Num<5>);
343
345 SigA5& f5 = fun11<5>;
346
352
353 CHECK (55+2+3+4+5 == f_bound_1 ( _2_,_3_,_4_,_5_) );
354 CHECK (1+55+3+4+5 == f_bound_2 (_1_, _3_,_4_,_5_) );
355 CHECK (1+2+55+4+5 == f_bound_3 (_1_,_2_, _4_,_5_) );
356 CHECK (1+2+3+55+5 == f_bound_4 (_1_,_2_,_3_, _5_) );
357 CHECK (1+2+3+4+55 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
358
359
360 // degenerate case: specify wrong argument position (behind end of argument list)
361 // causes the argument to be simply ignored and no binding to happen
363 CHECK (1+2+3+4+5 == f_bound_X (_1_,_2_,_3_,_4_,_5_) );
364
365
366 /* check the convenient function-style API */
367
368 using std::bind;
369
370 f_bound_5 = bindLast (f, bind(f5, Num<5>(99)));
371 CHECK (1+2+3+4+99 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
372
373 f_bound_5 = bindLast (f, bind(&f5, Num<5>(99))); // can bind function pointer
374 CHECK (1+2+3+4+99 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
375
377 f_bound_5 = bindLast (asFunctor, bind(f5, Num<5>(88))); // use functor instead of direct ref
378 CHECK (1+2+3+4+88 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
379 }
380
381
383 static long floorIt (float it) { return long(floor (it)); }
384
385
387 void
389 {
390 int ii = 99;
391 float ff = 88;
392 auto fun = std::function{[](float& f, int& i, long l) -> double { return f + i + l; }};
393 auto& f1 = fun;
394
395 // build chained and a partially applied functors
396 auto chain = func::chained(f1,floorIt);
397 auto pappl = func::bindFirst (f1, ff);
398
399 using Sig1 = _Fun<decltype(f1)>::Sig;
400 using SigC = _Fun<decltype(chain)>::Sig;
401 using SigP = _Fun<decltype(pappl)>::Sig;
402
403 CHECK (showType<Sig1>() == "double (float&, int&, long)"_expect);
404 CHECK (showType<SigC>() == "long (float&, int&, long)"_expect);
405 CHECK (showType<SigP>() == "double (int&, long)"_expect);
406
407 CHECK (220 == f1 (ff,ii,33));
408 CHECK (220 == chain(ff,ii,33));
409 CHECK (220 == pappl( ii,33));
410
411 // change original values to prove that references were
412 // passed and stored properly in the adapted functors
413 ii = 22;
414 ff = 42;
415
416 CHECK ( 97 == f1 (ff,ii,33));
417 CHECK ( 97 == chain(ff,ii,33));
418
419 // NOTE: the partial-application generates a std::bind (Binder object),
420 // which deliberately _decays_ arguments to values.
421 CHECK (143 == pappl( ii,33)); // --> uses original *value* for f, but the int-ref (88+22+33)
422
423 // can even exchange the actual function, since f1 was passed as reference
424 fun = [](float& f, int& i, size_t s) -> double { return f - i - s; };
425
426 CHECK (-13 == f1 (ff,ii,33));
427 CHECK (-13 == chain(ff,ii,33));
428
429 CHECK (143 == pappl( ii,33)); // Note again: uses original value for the function and the float
430 }
431 };
432
433
436
437
438
439}}} // namespace lib::meta::test
static auto reduced(FUN &&f, VAL &&val)
static LeftReducedFunc bindFront(SIG const &f, Tuple< ValTypes > arg)
do a partial function application, closing the first arguments f(a,b,c)->res + (a,...
static RightReducedFunc bindBack(SIG const &f, Tuple< ValTypes > arg)
do a partial function application, closing the last arguments f(a,b,c)->res + (b,...
Partial function application and building a complete function closure.
Metaprogramming tools for detecting and transforming function types.
auto bindArgTuple(FUN &&fun, TUP &&tuple)
Base technique: build a binder with arguments from a tuple.
auto bindFirst(FUN &&f, TERM &&arg)
bind (close) the first function argument to an arbitrary term.
auto bindLast(FUN &&f, TERM &&arg)
auto chained(FUN1 &&f1, FUN2 &&f2)
build a functor chaining the given functions: feed the result of f1 into f2.
Num< i > fun14(Num< i > val1, Num< ii > val2, Num< iii > val3, Num< iv > val4)
Num< i > fun11(Num< i > val1)
"Function-1" will be used at the front side, accepting a tuple of values
Num< i > fun13(Num< i > val1, Num< ii > val2, Num< iii > val3)
Num< i > fun15(Num< i > val1, Num< ii > val2, Num< iii > val3, Num< iv > val4, Num< v > val5)
enable_if_c< Cond::value, T >::type enable_if
SFINAE helper to control the visibility of specialisations and overloads.
Definition meta/util.hpp:87
string showType()
diagnostic type output, including const and similar adornments
Implementation namespace for support and library code.
Test runner and basic definitions for tests.
Simplistic test class runner.
#define LAUNCHER(_TEST_CLASS_, _GROUPS_)
Definition run.hpp:116
Trait template for uniform access to function signature types.
Definition function.hpp:144
A collection of frequently used helper functions to support unit testing.
Support for writing metaprogramming unit-tests dealing with typelists and flags.
A template metaprogramming technique for manipulating collections of types.