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) Lumiera.org
5  2009, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
28 #include "lib/test/run.hpp"
29 #include "lib/test/test-helper.hpp"
30 #include "lib/meta/typelist.hpp"
31 #include "lib/meta/function.hpp"
34 
35 #include <tuple>
36 
37 namespace lib {
38 namespace meta {
39 namespace test {
40 
41  using ::test::Test;
42  using lib::test::showType;
43  using lib::meta::_Fun;
44  using func::applyFirst;
45  using func::applyLast;
46  using func::bindLast;
47  using func::PApply;
48  using func::BindToArgument;
49  using std::make_tuple;
50  using std::get;
51 
52 
53  namespace { // test functions
54 
55  Num<1> _1_;
56  Num<2> _2_;
57  Num<3> _3_;
58  Num<4> _4_;
59  Num<5> _5_;
60  Num<6> _6_;
61  Num<7> _7_;
62  Num<8> _8_;
63  Num<9> _9_;
64 
66  template<uint i>
67  Num<i>
68  fun11 ( Num<i> val1
69  )
70  {
71  return val1;
72  }
73 
74  template<uint i, uint ii>
75  Num<i>
76  fun12 ( Num<i> val1
77  , Num<ii> val2
78  )
79  {
80  val1.o_ += val2.o_;
81  return val1;
82  }
83 
84  template<uint i, uint ii, uint iii>
85  Num<i>
86  fun13 ( Num<i> val1
87  , Num<ii> val2
88  , Num<iii> val3
89  )
90  {
91  val1.o_ += val2.o_ + val3.o_;
92  return val1;
93  }
94 
95  template<uint i, uint ii, uint iii, uint iv>
96  Num<i>
97  fun14 ( Num<i> val1
98  , Num<ii> val2
99  , Num<iii> val3
100  , Num<iv> val4
101  )
102  {
103  val1.o_ += val2.o_ + val3.o_ + val4.o_;
104  return val1;
105  }
106 
107  template<uint i, uint ii, uint iii, uint iv, uint v>
108  Num<i>
109  fun15 ( Num<i> val1
110  , Num<ii> val2
111  , Num<iii> val3
112  , Num<iv> val4
113  , Num<v> val5
114  )
115  {
116  val1.o_ += val2.o_ + val3.o_ + val4.o_ + val5.o_;
117  return val1;
118  }
119 
120 
122  template<class II>
123  int
124  fun2 (II val)
125  {
126  return val.o_;
127  }
128 
129  } // (End) test data
130 
131 
132 
133 
134 
135 
136  /**************************************************************************/
142  class FunctionComposition_test : public Test
143  {
144  virtual void
145  run (Arg)
146  {
148  check_partialApplication();
149  check_functionalComposition();
150  check_bindToArbitraryParameter();
151 
153  }
154 
155 
157  void
159  {
160  CHECK (6 == (fun13<1,2,3> (_1_, _2_, _3_)).o_ );
161  CHECK (6 == (fun13<1,1,1> (Num<1>(3), Num<1>(2), Num<1>(1))).o_ );
162 
163  CHECK ( 1 == fun2 (fun11<1> (_1_)) );
164  CHECK ( 3 == fun2 (fun12<1,2> (_1_, _2_)) );
165  CHECK ( 6 == fun2 (fun13<1,2,3> (_1_, _2_, _3_)) );
166  CHECK (10 == fun2 (fun14<1,2,3,4> (_1_, _2_, _3_, _4_)) );
167  CHECK (15 == fun2 (fun15<1,2,3,4,5> (_1_, _2_, _3_, _4_, _5_)) );
168 
169  CHECK ( 9 == fun2 (fun13<2,3,4> (_2_, _3_, _4_)) );
170  CHECK (18 == fun2 (fun13<5,6,7> (_5_, _6_, _7_)) );
171  CHECK (24 == fun2 (fun13<9,8,7> (_9_, _8_, _7_)) );
172  }
173 
174 
175  void
176  check_partialApplication ()
177  {
178  // Because the code of the partial function application is very technical,
179  // the following might serve as explanation what actually happens....
180  // (and actually it's a leftover from initial debugging)
181 
182  typedef Num<1> Sig123(Num<1>, Num<2>, Num<3>); // signature of the original function
183 
184  typedef Num<1> Sig23(Num<2>, Num<3>); // signature after having closed over the first argument
185  typedef function<Sig23> F23; // and a std::function object to hold such a function
186 
187  Sig123& f =fun13<1,2,3>; // the actual input: a reference to the bare function
188 
189 
190  // Version1: do a direct argument binding----------------- //
191 
192  using PH1 = std::_Placeholder<1>; // std::function argument placeholders
193  using PH2 = std::_Placeholder<2>;
194 
195  PH1 ph1; // these empty structs are used to mark the arguments to be kept "open"
196  PH2 ph2;
197  Num<1> num18 (18); // ...and this value is for closing the first function argument
198 
199  F23 fun_23 = std::bind (f, num18 // do the actual binding (i.e. close the first argument with a constant value)
200  , ph1
201  , ph2
202  );
203 
204  int res = 0;
205  res = fun_23 (_2_,_3_).o_; // and invoke the resulting functor ("closure"), providing the remaining arguments
206  CHECK (23 == res);
207 
208 
209 
210  // Version2: extract the binding arguments from a tuple--- //
211 
212  using PartialArg = Tuple<Types<Num<1>, PH1, PH2>>; // Tuple type to hold the binding values. Note the placeholder types
213  PartialArg arg(num18, PH1(), PH2()); // Value for partial application (the placeholders are default constructed)
214 
215  fun_23 = std::bind (f, get<0>(arg) // now extract the values to bind from this tuple
216  , get<1>(arg)
217  , get<2>(arg)
218  );
219  res = 0;
220  res = fun_23 (_2_,_3_).o_; // and invoke the resulting functor....
221  CHECK (23 == res);
222 
223 
224 
225 
226  // Version3: let the PApply-template do the work for us--- //
227 
228  typedef Types<Num<1>> ArgTypes; // now package just the argument(s) to be applied into a tuple
229  Tuple<ArgTypes> args_to_bind (Num<1>(18));
230 
231  fun_23 = PApply<Sig123, ArgTypes>::bindFront (f , args_to_bind);
232  // "bindFront" will close the parameters starting from left....
233  res = 0;
234  res = fun_23 (_2_,_3_).o_; // invoke the resulting functor...
235  CHECK (23 == res);
236 
237 
238 
239 
240 
241 
242  // Version4: as you'd typically do it in real life-------- //
243 
244  fun_23 = func::applyFirst (f, Num<1>(18)); // use the convenience function API to close over a single value
245 
246  res = 0;
247  res = fun_23 (_2_,_3_).o_; // invoke the resulting functor...
248  CHECK (23 == res);
249 
250 
251 
252  // what follows is the real unit test...
253  function<Sig123> func123 (f); // alternatively do it with an std::function object
254  fun_23 = func::applyFirst (func123, Num<1>(19));
255  res = fun_23 (_2_,_3_).o_;
256  CHECK (24 == res);
257 
258  typedef function<Num<1>(Num<1>, Num<2>)> F12;
259  F12 fun_12 = func::applyLast(f, Num<3>(20)); // close the *last* argument of a function
260  res = fun_12 (_1_,_2_).o_;
261  CHECK (23 == res);
262 
263  fun_12 = func::applyLast(func123, Num<3>(21)); // alternatively use a function object
264  res = fun_12 (_1_,_2_).o_;
265  CHECK (24 == res);
266 
267  Sig123 *fP = &f; // a function pointer works too
268  fun_12 = func::applyLast( fP, Num<3>(22));
269  res = fun_12 (_1_,_2_).o_;
270  CHECK (25 == res);
271  // cover more cases....
272 
273  CHECK (1 == (func::applyLast (fun11<1> , _1_ ) ( ) ).o_);
274  CHECK (1+3 == (func::applyLast (fun12<1,3> , _3_ ) (_1_) ).o_);
275  CHECK (1+3+5 == (func::applyLast (fun13<1,3,5> , _5_ ) (_1_,_3_) ).o_);
276  CHECK (1+3+5+7 == (func::applyLast (fun14<1,3,5,7> , _7_ ) (_1_,_3_,_5_) ).o_);
277  CHECK (1+3+5+7+9 == (func::applyLast (fun15<1,3,5,7,9>, _9_ ) (_1_,_3_,_5_,_7_)).o_);
278 
279  CHECK (9+8+7+6+5 == (func::applyFirst(fun15<9,8,7,6,5>, _9_ ) (_8_,_7_,_6_,_5_)).o_);
280  CHECK ( 8+7+6+5 == (func::applyFirst( fun14<8,7,6,5>, _8_ ) (_7_,_6_,_5_)).o_);
281  CHECK ( 7+6+5 == (func::applyFirst( fun13<7,6,5>, _7_ ) (_6_,_5_)).o_);
282  CHECK ( 6+5 == (func::applyFirst( fun12<6,5>, _6_ ) (_5_)).o_);
283  CHECK ( 5 == (func::applyFirst( fun11<5>, _5_ ) ( )).o_);
284 
285 
286 
287  // Finally a more convoluted example
288  // covering the general case of partial function closure:
289  typedef Num<5> Sig54321(Num<5>, Num<4>, Num<3>, Num<2>, Num<1>); // Signature of the 5-argument function
290  typedef Num<5> Sig54 (Num<5>, Num<4>); // ...closing the last 3 arguments should yield this 2-argument function
291  typedef Types<Num<3>,Num<2>,Num<1>> Args2Close; // Tuple type to hold the 3 argument values used for the closure
292 
293  // Close the trailing 3 arguments of the 5-argument function...
294  function<Sig54> fun_54 = PApply<Sig54321, Args2Close>::bindBack(fun15<5,4,3,2,1>,
295  make_tuple(_3_,_2_,_1_)
296  );
297 
298  // apply the remaining argument values
299  Num<5> resN5 = fun_54 (_5_,_4_);
300  CHECK (5+4+3+2+1 == resN5.o_);
301  }
302 
303 
304 
305 
306  void
307  check_functionalComposition ()
308  {
309  typedef int Sig2(Num<1>);
310  typedef Num<1> Sig11(Num<1>);
311  typedef Num<1> Sig12(Num<1>,Num<2>);
312  typedef Num<1> Sig13(Num<1>,Num<2>,Num<3>);
313  typedef Num<1> Sig14(Num<1>,Num<2>,Num<3>,Num<4>);
314  typedef Num<1> Sig15(Num<1>,Num<2>,Num<3>,Num<4>,Num<5>);
315 
316  Sig2 & ff = fun2< Num<1> >;
317  Sig11& f1 = fun11<1>;
318  Sig12& f2 = fun12<1,2>;
319  Sig13& f3 = fun13<1,2,3>;
320  Sig14& f4 = fun14<1,2,3,4>;
321  Sig15& f5 = fun15<1,2,3,4,5>;
322 
323  CHECK (1 == func::chained(f1, ff) (_1_) );
324  CHECK (1+2 == func::chained(f2, ff) (_1_,_2_) );
325  CHECK (1+2+3 == func::chained(f3, ff) (_1_,_2_,_3_) );
326  CHECK (1+2+3+4 == func::chained(f4, ff) (_1_,_2_,_3_,_4_) );
327  CHECK (1+2+3+4+5 == func::chained(f5, ff) (_1_,_2_,_3_,_4_,_5_) );
328 
329 
330  function<Sig15> f5_fun = f5; // also works with function objects...
331  function<Sig2> ff_fun = ff;
332  CHECK (1+2+3+4+5 == func::chained(f5_fun, ff ) (_1_,_2_,_3_,_4_,_5_) );
333  CHECK (1+2+3+4+5 == func::chained(f5, ff_fun) (_1_,_2_,_3_,_4_,_5_) );
334  CHECK (1+2+3+4+5 == func::chained(f5_fun, ff_fun) (_1_,_2_,_3_,_4_,_5_) );
335  }
336 
337 
338 
339  void
340  check_bindToArbitraryParameter ()
341  {
342  typedef Num<1> Sig15(Num<1>,Num<2>,Num<3>,Num<4>,Num<5>);
343 
344  typedef Num<1> SigR1( Num<2>,Num<3>,Num<4>,Num<5>);
345  typedef Num<1> SigR2(Num<1>, Num<3>,Num<4>,Num<5>);
346  typedef Num<1> SigR3(Num<1>,Num<2>, Num<4>,Num<5>);
347  typedef Num<1> SigR4(Num<1>,Num<2>,Num<3>, Num<5>);
348  typedef Num<1> SigR5(Num<1>,Num<2>,Num<3>,Num<4> );
349 
350  typedef Num<5> SigA5(Num<5>);
351 
352  Sig15& f = fun15<1,2,3,4,5>;
353  SigA5& f5 = fun11<5>;
354  Tuple<Types<char>> argT(55);
355 
356  function<SigR1> f_bound_1 = BindToArgument<Sig15,char,0>::reduced (f, argT);
357  function<SigR2> f_bound_2 = BindToArgument<Sig15,char,1>::reduced (f, argT);
358  function<SigR3> f_bound_3 = BindToArgument<Sig15,char,2>::reduced (f, argT);
359  function<SigR4> f_bound_4 = BindToArgument<Sig15,char,3>::reduced (f, argT);
360  function<SigR5> f_bound_5 = BindToArgument<Sig15,char,4>::reduced (f, argT);
361 
362  CHECK (55+2+3+4+5 == f_bound_1 ( _2_,_3_,_4_,_5_) );
363  CHECK (1+55+3+4+5 == f_bound_2 (_1_, _3_,_4_,_5_) );
364  CHECK (1+2+55+4+5 == f_bound_3 (_1_,_2_, _4_,_5_) );
365  CHECK (1+2+3+55+5 == f_bound_4 (_1_,_2_,_3_, _5_) );
366  CHECK (1+2+3+4+55 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
367 
368 
369  // degenerate case: specify wrong argument position (behind end of argument list)
370  // causes the argument to be simply ignored and no binding to happen
371  function<Sig15> f_bound_X = BindToArgument<Sig15,char,5>::reduced (f, argT);
372  CHECK (1+2+3+4+5 == f_bound_X (_1_,_2_,_3_,_4_,_5_) );
373 
374 
375  /* check the convenient function-style API */
376 
377  using std::bind;
378 
379  f_bound_5 = bindLast (f, bind(f5, Num<5>(99)));
380  CHECK (1+2+3+4+99 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
381 
382  f_bound_5 = bindLast (f, bind(&f5, Num<5>(99))); // can bind function pointer
383  CHECK (1+2+3+4+99 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
384 
385  function<Sig15> asFunctor(f);
386  f_bound_5 = bindLast (asFunctor, bind(f5, Num<5>(88))); // use functor instead of direct ref
387  CHECK (1+2+3+4+88 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
388  }
389 
390 
392  static long floorIt (float it) { return long(floor (it)); }
393 
394 
396  void
398  {
399  int ii = 99;
400  float ff = 88;
401  auto fun = std::function{[](float& f, int& i, long l) -> double { return f + i + l; }};
402  auto& f1 = fun;
403 
404  // build chained and a partially applied functors
405  auto chain = func::chained(f1,floorIt);
406  auto pappl = func::applyFirst (f1, ff);
407 
408  using Sig1 = _Fun<decltype(f1)>::Sig;
409  using SigC = _Fun<decltype(chain)>::Sig;
410  using SigP = _Fun<decltype(pappl)>::Sig;
411 
412  CHECK (showType<Sig1>() == "double (float&, int&, long)"_expect);
413  CHECK (showType<SigC>() == "long (float&, int&, long)"_expect);
414  CHECK (showType<SigP>() == "double (int&, long)"_expect);
415 
416  CHECK (220 == f1 (ff,ii,33));
417  CHECK (220 == chain(ff,ii,33));
418  CHECK (220 == pappl( ii,33));
419 
420  // change original values to prove that references were
421  // passed and stored properly in the adapted functors
422  ii = 22;
423  ff = 42;
424 
425  CHECK ( 97 == f1 (ff,ii,33));
426  CHECK ( 97 == chain(ff,ii,33));
427  CHECK ( 97 == pappl( ii,33));
428 
429  // can even exchange the actual function, since f1 was passed as reference
430  fun = [](float& f, int& i, size_t s) -> double { return f - i - s; };
431 
432  CHECK (-13 == f1 (ff,ii,33));
433  CHECK (-13 == chain(ff,ii,33));
434  CHECK (-13 == pappl( ii,33));
435  }
436  };
437 
438 
440  LAUNCHER (FunctionComposition_test, "unit common");
441 
442 
443 
444 }}} // 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:49
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:108
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.
Simple 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 ...