Lumiera  0.pre.03
»edit your freedom«
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"
20 #include "lib/test/test-helper.hpp"
21 #include "lib/meta/typelist.hpp"
22 #include "lib/meta/function.hpp"
25 
26 #include <tuple>
27 
28 namespace lib {
29 namespace meta {
30 namespace test {
31 
32  using ::test::Test;
33  using lib::test::showType;
34  using lib::meta::_Fun;
35  using func::applyFirst;
36  using func::applyLast;
37  using func::bindLast;
38  using func::PApply;
39  using func::BindToArgument;
40  using std::make_tuple;
41  using std::get;
42 
43 
44  namespace { // test functions
45 
46  Num<1> _1_;
47  Num<2> _2_;
48  Num<3> _3_;
49  Num<4> _4_;
50  Num<5> _5_;
51  Num<6> _6_;
52  Num<7> _7_;
53  Num<8> _8_;
54  Num<9> _9_;
55 
57  template<uint i>
58  Num<i>
59  fun11 ( Num<i> val1
60  )
61  {
62  return val1;
63  }
64 
65  template<uint i, uint ii>
66  Num<i>
67  fun12 ( Num<i> val1
68  , Num<ii> val2
69  )
70  {
71  val1.o_ += val2.o_;
72  return val1;
73  }
74 
75  template<uint i, uint ii, uint iii>
76  Num<i>
77  fun13 ( Num<i> val1
78  , Num<ii> val2
79  , Num<iii> val3
80  )
81  {
82  val1.o_ += val2.o_ + val3.o_;
83  return val1;
84  }
85 
86  template<uint i, uint ii, uint iii, uint iv>
87  Num<i>
88  fun14 ( Num<i> val1
89  , Num<ii> val2
90  , Num<iii> val3
91  , Num<iv> val4
92  )
93  {
94  val1.o_ += val2.o_ + val3.o_ + val4.o_;
95  return val1;
96  }
97 
98  template<uint i, uint ii, uint iii, uint iv, uint v>
99  Num<i>
100  fun15 ( Num<i> val1
101  , Num<ii> val2
102  , Num<iii> val3
103  , Num<iv> val4
104  , Num<v> val5
105  )
106  {
107  val1.o_ += val2.o_ + val3.o_ + val4.o_ + val5.o_;
108  return val1;
109  }
110 
111 
113  template<class II>
114  int
115  fun2 (II val)
116  {
117  return val.o_;
118  }
119 
120  } // (End) test data
121 
122 
123 
124 
125 
126 
127  /**************************************************************************/
133  class FunctionComposition_test : public Test
134  {
135  virtual void
136  run (Arg)
137  {
139  check_partialApplication();
140  check_functionalComposition();
141  check_bindToArbitraryParameter();
142 
144  }
145 
146 
148  void
150  {
151  CHECK (6 == (fun13<1,2,3> (_1_, _2_, _3_)).o_ );
152  CHECK (6 == (fun13<1,1,1> (Num<1>(3), Num<1>(2), Num<1>(1))).o_ );
153 
154  CHECK ( 1 == fun2 (fun11<1> (_1_)) );
155  CHECK ( 3 == fun2 (fun12<1,2> (_1_, _2_)) );
156  CHECK ( 6 == fun2 (fun13<1,2,3> (_1_, _2_, _3_)) );
157  CHECK (10 == fun2 (fun14<1,2,3,4> (_1_, _2_, _3_, _4_)) );
158  CHECK (15 == fun2 (fun15<1,2,3,4,5> (_1_, _2_, _3_, _4_, _5_)) );
159 
160  CHECK ( 9 == fun2 (fun13<2,3,4> (_2_, _3_, _4_)) );
161  CHECK (18 == fun2 (fun13<5,6,7> (_5_, _6_, _7_)) );
162  CHECK (24 == fun2 (fun13<9,8,7> (_9_, _8_, _7_)) );
163  }
164 
165 
166  void
167  check_partialApplication ()
168  {
169  // Because the code of the partial function application is very technical,
170  // the following might serve as explanation what actually happens....
171  // (and actually it's a leftover from initial debugging)
172 
173  typedef Num<1> Sig123(Num<1>, Num<2>, Num<3>); // signature of the original function
174 
175  typedef Num<1> Sig23(Num<2>, Num<3>); // signature after having closed over the first argument
176  typedef function<Sig23> F23; // and a std::function object to hold such a function
177 
178  Sig123& f =fun13<1,2,3>; // the actual input: a reference to the bare function
179 
180 
181  // Version1: do a direct argument binding----------------- //
182 
183  using PH1 = std::_Placeholder<1>; // std::function argument placeholders
184  using PH2 = std::_Placeholder<2>;
185 
186  PH1 ph1; // these empty structs are used to mark the arguments to be kept "open"
187  PH2 ph2;
188  Num<1> num18 (18); // ...and this value is for closing the first function argument
189 
190  F23 fun_23 = std::bind (f, num18 // do the actual binding (i.e. close the first argument with a constant value)
191  , ph1
192  , ph2
193  );
194 
195  int res = 0;
196  res = fun_23 (_2_,_3_).o_; // and invoke the resulting functor ("closure"), providing the remaining arguments
197  CHECK (23 == res);
198 
199 
200 
201  // Version2: extract the binding arguments from a tuple--- //
202 
203  using PartialArg = Tuple<Types<Num<1>, PH1, PH2>>; // Tuple type to hold the binding values. Note the placeholder types
204  PartialArg arg(num18, PH1(), PH2()); // Value for partial application (the placeholders are default constructed)
205 
206  fun_23 = std::bind (f, get<0>(arg) // now extract the values to bind from this tuple
207  , get<1>(arg)
208  , get<2>(arg)
209  );
210  res = 0;
211  res = fun_23 (_2_,_3_).o_; // and invoke the resulting functor....
212  CHECK (23 == res);
213 
214 
215 
216 
217  // Version3: let the PApply-template do the work for us--- //
218 
219  typedef Types<Num<1>> ArgTypes; // now package just the argument(s) to be applied into a tuple
220  Tuple<ArgTypes> args_to_bind (Num<1>(18));
221 
222  fun_23 = PApply<Sig123, ArgTypes>::bindFront (f , args_to_bind);
223  // "bindFront" will close the parameters starting from left....
224  res = 0;
225  res = fun_23 (_2_,_3_).o_; // invoke the resulting functor...
226  CHECK (23 == res);
227 
228 
229 
230 
231 
232 
233  // Version4: as you'd typically do it in real life-------- //
234 
235  fun_23 = func::applyFirst (f, Num<1>(18)); // use the convenience function API to close over a single value
236 
237  res = 0;
238  res = fun_23 (_2_,_3_).o_; // invoke the resulting functor...
239  CHECK (23 == res);
240 
241 
242 
243  // what follows is the real unit test...
244  function<Sig123> func123 (f); // alternatively do it with an std::function object
245  fun_23 = func::applyFirst (func123, Num<1>(19));
246  res = fun_23 (_2_,_3_).o_;
247  CHECK (24 == res);
248 
249  typedef function<Num<1>(Num<1>, Num<2>)> F12;
250  F12 fun_12 = func::applyLast(f, Num<3>(20)); // close the *last* argument of a function
251  res = fun_12 (_1_,_2_).o_;
252  CHECK (23 == res);
253 
254  fun_12 = func::applyLast(func123, Num<3>(21)); // alternatively use a function object
255  res = fun_12 (_1_,_2_).o_;
256  CHECK (24 == res);
257 
258  Sig123 *fP = &f; // a function pointer works too
259  fun_12 = func::applyLast( fP, Num<3>(22));
260  res = fun_12 (_1_,_2_).o_;
261  CHECK (25 == res);
262  // cover more cases....
263 
264  CHECK (1 == (func::applyLast (fun11<1> , _1_ ) ( ) ).o_);
265  CHECK (1+3 == (func::applyLast (fun12<1,3> , _3_ ) (_1_) ).o_);
266  CHECK (1+3+5 == (func::applyLast (fun13<1,3,5> , _5_ ) (_1_,_3_) ).o_);
267  CHECK (1+3+5+7 == (func::applyLast (fun14<1,3,5,7> , _7_ ) (_1_,_3_,_5_) ).o_);
268  CHECK (1+3+5+7+9 == (func::applyLast (fun15<1,3,5,7,9>, _9_ ) (_1_,_3_,_5_,_7_)).o_);
269 
270  CHECK (9+8+7+6+5 == (func::applyFirst(fun15<9,8,7,6,5>, _9_ ) (_8_,_7_,_6_,_5_)).o_);
271  CHECK ( 8+7+6+5 == (func::applyFirst( fun14<8,7,6,5>, _8_ ) (_7_,_6_,_5_)).o_);
272  CHECK ( 7+6+5 == (func::applyFirst( fun13<7,6,5>, _7_ ) (_6_,_5_)).o_);
273  CHECK ( 6+5 == (func::applyFirst( fun12<6,5>, _6_ ) (_5_)).o_);
274  CHECK ( 5 == (func::applyFirst( fun11<5>, _5_ ) ( )).o_);
275 
276 
277 
278  // Finally a more convoluted example
279  // covering the general case of partial function closure:
280  typedef Num<5> Sig54321(Num<5>, Num<4>, Num<3>, Num<2>, Num<1>); // Signature of the 5-argument function
281  typedef Num<5> Sig54 (Num<5>, Num<4>); // ...closing the last 3 arguments should yield this 2-argument function
282  typedef Types<Num<3>,Num<2>,Num<1>> Args2Close; // Tuple type to hold the 3 argument values used for the closure
283 
284  // Close the trailing 3 arguments of the 5-argument function...
285  function<Sig54> fun_54 = PApply<Sig54321, Args2Close>::bindBack(fun15<5,4,3,2,1>,
286  make_tuple(_3_,_2_,_1_)
287  );
288 
289  // apply the remaining argument values
290  Num<5> resN5 = fun_54 (_5_,_4_);
291  CHECK (5+4+3+2+1 == resN5.o_);
292  }
293 
294 
295 
296 
297  void
298  check_functionalComposition ()
299  {
300  typedef int Sig2(Num<1>);
301  typedef Num<1> Sig11(Num<1>);
302  typedef Num<1> Sig12(Num<1>,Num<2>);
303  typedef Num<1> Sig13(Num<1>,Num<2>,Num<3>);
304  typedef Num<1> Sig14(Num<1>,Num<2>,Num<3>,Num<4>);
305  typedef Num<1> Sig15(Num<1>,Num<2>,Num<3>,Num<4>,Num<5>);
306 
307  Sig2 & ff = fun2< Num<1> >;
308  Sig11& f1 = fun11<1>;
309  Sig12& f2 = fun12<1,2>;
310  Sig13& f3 = fun13<1,2,3>;
311  Sig14& f4 = fun14<1,2,3,4>;
312  Sig15& f5 = fun15<1,2,3,4,5>;
313 
314  CHECK (1 == func::chained(f1, ff) (_1_) );
315  CHECK (1+2 == func::chained(f2, ff) (_1_,_2_) );
316  CHECK (1+2+3 == func::chained(f3, ff) (_1_,_2_,_3_) );
317  CHECK (1+2+3+4 == func::chained(f4, ff) (_1_,_2_,_3_,_4_) );
318  CHECK (1+2+3+4+5 == func::chained(f5, ff) (_1_,_2_,_3_,_4_,_5_) );
319 
320 
321  function<Sig15> f5_fun = f5; // also works with function objects...
322  function<Sig2> ff_fun = ff;
323  CHECK (1+2+3+4+5 == func::chained(f5_fun, ff ) (_1_,_2_,_3_,_4_,_5_) );
324  CHECK (1+2+3+4+5 == func::chained(f5, ff_fun) (_1_,_2_,_3_,_4_,_5_) );
325  CHECK (1+2+3+4+5 == func::chained(f5_fun, ff_fun) (_1_,_2_,_3_,_4_,_5_) );
326  }
327 
328 
329 
330  void
331  check_bindToArbitraryParameter ()
332  {
333  typedef Num<1> Sig15(Num<1>,Num<2>,Num<3>,Num<4>,Num<5>);
334 
335  typedef Num<1> SigR1( Num<2>,Num<3>,Num<4>,Num<5>);
336  typedef Num<1> SigR2(Num<1>, Num<3>,Num<4>,Num<5>);
337  typedef Num<1> SigR3(Num<1>,Num<2>, Num<4>,Num<5>);
338  typedef Num<1> SigR4(Num<1>,Num<2>,Num<3>, Num<5>);
339  typedef Num<1> SigR5(Num<1>,Num<2>,Num<3>,Num<4> );
340 
341  typedef Num<5> SigA5(Num<5>);
342 
343  Sig15& f = fun15<1,2,3,4,5>;
344  SigA5& f5 = fun11<5>;
345  Tuple<Types<char>> argT(55);
346 
347  function<SigR1> f_bound_1 = BindToArgument<Sig15,char,0>::reduced (f, argT);
348  function<SigR2> f_bound_2 = BindToArgument<Sig15,char,1>::reduced (f, argT);
349  function<SigR3> f_bound_3 = BindToArgument<Sig15,char,2>::reduced (f, argT);
350  function<SigR4> f_bound_4 = BindToArgument<Sig15,char,3>::reduced (f, argT);
351  function<SigR5> f_bound_5 = BindToArgument<Sig15,char,4>::reduced (f, argT);
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
362  function<Sig15> f_bound_X = BindToArgument<Sig15,char,5>::reduced (f, argT);
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 
376  function<Sig15> asFunctor(f);
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::applyFirst (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  CHECK ( 97 == pappl( ii,33));
419 
420  // can even exchange the actual function, since f1 was passed as reference
421  fun = [](float& f, int& i, size_t s) -> double { return f - i - s; };
422 
423  CHECK (-13 == f1 (ff,ii,33));
424  CHECK (-13 == chain(ff,ii,33));
425  CHECK (-13 == pappl( ii,33));
426  }
427  };
428 
429 
431  LAUNCHER (FunctionComposition_test, "unit common");
432 
433 
434 
435 }}} // namespace lib::meta::test
Bind a specific argument to an arbitrary value.
A template metaprogramming technique for manipulating collections of types.
typename BuildTupleType< TYPES >::Type Tuple
Build a std::tuple from types given as type sequence.
Definition: run.hpp:40
void check_diagnostics()
verify the test input data
Num< i > fun11(Num< i > val1)
"Function-1" will be used at the front side, accepting a tuple of values
Helper for uniform access to function signature types.
Definition: function.hpp:99
Partial function application and building a complete function closure.
static RightReducedFunc bindBack(SIG const &f, Tuple< ValTypes > const &arg)
do a partial function application, closing the last arguments f(a,b,c)->res + (b,c) yields f(a)->res
Implementation namespace for support and library code.
Metaprogramming tools for transforming functor types.
Simplistic test class runner.
A collection of frequently used helper functions to support unit testing.
static LeftReducedFunc bindFront(SIG const &f, Tuple< ValTypes > const &arg)
do a partial function application, closing the first arguments f(a,b,c)->res + (a,b) yields f(c)->res
string showType()
diagnostic type output, including const and similar adornments
Support for writing metaprogramming unit-tests dealing with typelists and flags.
constant-wrapper type for debugging purposes, usable for generating lists of distinguishable types ...