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 func::applyFirst;
43  using func::applyLast;
44  using func::bindLast;
45  using func::PApply;
46  using func::BindToArgument;
47  using std::make_tuple;
48  using std::get;
49 
50 
51  namespace { // test functions
52 
53  Num<1> _1_;
54  Num<2> _2_;
55  Num<3> _3_;
56  Num<4> _4_;
57  Num<5> _5_;
58  Num<6> _6_;
59  Num<7> _7_;
60  Num<8> _8_;
61  Num<9> _9_;
62 
64  template<uint i>
65  Num<i>
66  fun11 ( Num<i> val1
67  )
68  {
69  return val1;
70  }
71 
72  template<uint i, uint ii>
73  Num<i>
74  fun12 ( Num<i> val1
75  , Num<ii> val2
76  )
77  {
78  val1.o_ += val2.o_;
79  return val1;
80  }
81 
82  template<uint i, uint ii, uint iii>
83  Num<i>
84  fun13 ( Num<i> val1
85  , Num<ii> val2
86  , Num<iii> val3
87  )
88  {
89  val1.o_ += val2.o_ + val3.o_;
90  return val1;
91  }
92 
93  template<uint i, uint ii, uint iii, uint iv>
94  Num<i>
95  fun14 ( Num<i> val1
96  , Num<ii> val2
97  , Num<iii> val3
98  , Num<iv> val4
99  )
100  {
101  val1.o_ += val2.o_ + val3.o_ + val4.o_;
102  return val1;
103  }
104 
105  template<uint i, uint ii, uint iii, uint iv, uint v>
106  Num<i>
107  fun15 ( Num<i> val1
108  , Num<ii> val2
109  , Num<iii> val3
110  , Num<iv> val4
111  , Num<v> val5
112  )
113  {
114  val1.o_ += val2.o_ + val3.o_ + val4.o_ + val5.o_;
115  return val1;
116  }
117 
118 
120  template<class II>
121  int
122  fun2 (II val)
123  {
124  return val.o_;
125  }
126 
127  } // (End) test data
128 
129 
130 
131 
132 
133 
134  /**************************************************************************/
140  class FunctionComposition_test : public Test
141  {
142  virtual void
143  run (Arg)
144  {
146  check_partialApplication ();
147  check_functionalComposition ();
148  check_bindToArbitraryParameter ();
149  }
150 
151 
153  void
155  {
156  CHECK (6 == (fun13<1,2,3> (_1_, _2_, _3_)).o_ );
157  CHECK (6 == (fun13<1,1,1> (Num<1>(3), Num<1>(2), Num<1>(1))).o_ );
158 
159  CHECK ( 1 == fun2 (fun11<1> (_1_)) );
160  CHECK ( 3 == fun2 (fun12<1,2> (_1_, _2_)) );
161  CHECK ( 6 == fun2 (fun13<1,2,3> (_1_, _2_, _3_)) );
162  CHECK (10 == fun2 (fun14<1,2,3,4> (_1_, _2_, _3_, _4_)) );
163  CHECK (15 == fun2 (fun15<1,2,3,4,5> (_1_, _2_, _3_, _4_, _5_)) );
164 
165  CHECK ( 9 == fun2 (fun13<2,3,4> (_2_, _3_, _4_)) );
166  CHECK (18 == fun2 (fun13<5,6,7> (_5_, _6_, _7_)) );
167  CHECK (24 == fun2 (fun13<9,8,7> (_9_, _8_, _7_)) );
168  }
169 
170 
171  void
172  check_partialApplication ()
173  {
174  // Because the code of the partial function application is very technical,
175  // the following might serve as explanation what actually happens....
176  // (and actually it's a leftover from initial debugging)
177 
178  typedef Num<1> Sig123(Num<1>, Num<2>, Num<3>); // signature of the original function
179 
180  typedef Num<1> Sig23(Num<2>, Num<3>); // signature after having closed over the first argument
181  typedef function<Sig23> F23; // and a std::function object to hold such a function
182 
183  Sig123& f =fun13<1,2,3>; // the actual input: a reference to the bare function
184 
185 
186  // Version1: do a direct argument binding----------------- //
187 
188  using PH1 = std::_Placeholder<1>; // std::function argument placeholders
189  using PH2 = std::_Placeholder<2>;
190 
191  PH1 ph1; // these empty structs are used to mark the arguments to be kept "open"
192  PH2 ph2;
193  Num<1> num18 (18); // ...and this value is for closing the first function argument
194 
195  F23 fun_23 = std::bind (f, num18 // do the actual binding (i.e. close the first argument with a constant value)
196  , ph1
197  , ph2
198  );
199 
200  int res = 0;
201  res = fun_23 (_2_,_3_).o_; // and invoke the resulting functor ("closure"), providing the remaining arguments
202  CHECK (23 == res);
203 
204 
205 
206  // Version2: extract the binding arguments from a tuple--- //
207 
208  using PartialArg = Tuple<Types<Num<1>, PH1, PH2>>; // Tuple type to hold the binding values. Note the placeholder types
209  PartialArg arg(num18, PH1(), PH2()); // Value for partial application (the placeholders are default constructed)
210 
211  fun_23 = std::bind (f, get<0>(arg) // now extract the values to bind from this tuple
212  , get<1>(arg)
213  , get<2>(arg)
214  );
215  res = 0;
216  res = fun_23 (_2_,_3_).o_; // and invoke the resulting functor....
217  CHECK (23 == res);
218 
219 
220 
221 
222  // Version3: let the PApply-template do the work for us--- //
223 
224  typedef Types<Num<1>> ArgTypes; // now package just the argument(s) to be applied into a tuple
225  Tuple<ArgTypes> args_to_bind (Num<1>(18));
226 
227  fun_23 = PApply<Sig123, ArgTypes>::bindFront (f , args_to_bind);
228  // "bindFront" will close the parameters starting from left....
229  res = 0;
230  res = fun_23 (_2_,_3_).o_; // invoke the resulting functor...
231  CHECK (23 == res);
232 
233 
234 
235 
236 
237 
238  // Version4: as you'd typically do it in real life-------- //
239 
240  fun_23 = func::applyFirst (f, Num<1>(18)); // use the convenience function API to close over a single value
241 
242  res = 0;
243  res = fun_23 (_2_,_3_).o_; // invoke the resulting functor...
244  CHECK (23 == res);
245 
246 
247 
248  // what follows is the real unit test...
249  function<Sig123> func123 (f); // alternatively do it with an std::function object
250  fun_23 = func::applyFirst (func123, Num<1>(19));
251  res = fun_23 (_2_,_3_).o_;
252  CHECK (24 == res);
253 
254  typedef function<Num<1>(Num<1>, Num<2>)> F12;
255  F12 fun_12 = func::applyLast(f, Num<3>(20)); // close the *last* argument of a function
256  res = fun_12 (_1_,_2_).o_;
257  CHECK (23 == res);
258 
259  fun_12 = func::applyLast(func123, Num<3>(21)); // alternatively use a function object
260  res = fun_12 (_1_,_2_).o_;
261  CHECK (24 == res);
262 
263  Sig123 *fP = &f; // a function pointer works too
264  fun_12 = func::applyLast( fP, Num<3>(22));
265  res = fun_12 (_1_,_2_).o_;
266  CHECK (25 == res);
267  // cover more cases....
268 
269  CHECK (1 == (func::applyLast (fun11<1> , _1_ ) ( ) ).o_);
270  CHECK (1+3 == (func::applyLast (fun12<1,3> , _3_ ) (_1_) ).o_);
271  CHECK (1+3+5 == (func::applyLast (fun13<1,3,5> , _5_ ) (_1_,_3_) ).o_);
272  CHECK (1+3+5+7 == (func::applyLast (fun14<1,3,5,7> , _7_ ) (_1_,_3_,_5_) ).o_);
273  CHECK (1+3+5+7+9 == (func::applyLast (fun15<1,3,5,7,9>, _9_ ) (_1_,_3_,_5_,_7_)).o_);
274 
275  CHECK (9+8+7+6+5 == (func::applyFirst(fun15<9,8,7,6,5>, _9_ ) (_8_,_7_,_6_,_5_)).o_);
276  CHECK ( 8+7+6+5 == (func::applyFirst( fun14<8,7,6,5>, _8_ ) (_7_,_6_,_5_)).o_);
277  CHECK ( 7+6+5 == (func::applyFirst( fun13<7,6,5>, _7_ ) (_6_,_5_)).o_);
278  CHECK ( 6+5 == (func::applyFirst( fun12<6,5>, _6_ ) (_5_)).o_);
279  CHECK ( 5 == (func::applyFirst( fun11<5>, _5_ ) ( )).o_);
280 
281 
282 
283  // Finally a more convoluted example
284  // covering the general case of partial function closure:
285  typedef Num<5> Sig54321(Num<5>, Num<4>, Num<3>, Num<2>, Num<1>); // Signature of the 5-argument function
286  typedef Num<5> Sig54 (Num<5>, Num<4>); // ...closing the last 3 arguments should yield this 2-argument function
287  typedef Types<Num<3>,Num<2>,Num<1>> Args2Close; // Tuple type to hold the 3 argument values used for the closure
288 
289  // Close the trailing 3 arguments of the 5-argument function...
290  function<Sig54> fun_54 = PApply<Sig54321, Args2Close>::bindBack(fun15<5,4,3,2,1>,
291  make_tuple(_3_,_2_,_1_)
292  );
293 
294  // apply the remaining argument values
295  Num<5> resN5 = fun_54 (_5_,_4_);
296  CHECK (5+4+3+2+1 == resN5.o_);
297  }
298 
299 
300 
301 
302  void
303  check_functionalComposition ()
304  {
305  typedef int Sig2(Num<1>);
306  typedef Num<1> Sig11(Num<1>);
307  typedef Num<1> Sig12(Num<1>,Num<2>);
308  typedef Num<1> Sig13(Num<1>,Num<2>,Num<3>);
309  typedef Num<1> Sig14(Num<1>,Num<2>,Num<3>,Num<4>);
310  typedef Num<1> Sig15(Num<1>,Num<2>,Num<3>,Num<4>,Num<5>);
311 
312  Sig2 & ff = fun2< Num<1> >;
313  Sig11& f1 = fun11<1>;
314  Sig12& f2 = fun12<1,2>;
315  Sig13& f3 = fun13<1,2,3>;
316  Sig14& f4 = fun14<1,2,3,4>;
317  Sig15& f5 = fun15<1,2,3,4,5>;
318 
319  CHECK (1 == func::chained(f1, ff) (_1_) );
320  CHECK (1+2 == func::chained(f2, ff) (_1_,_2_) );
321  CHECK (1+2+3 == func::chained(f3, ff) (_1_,_2_,_3_) );
322  CHECK (1+2+3+4 == func::chained(f4, ff) (_1_,_2_,_3_,_4_) );
323  CHECK (1+2+3+4+5 == func::chained(f5, ff) (_1_,_2_,_3_,_4_,_5_) );
324 
325 
326  function<Sig15> f5_fun = f5; // also works with function objects...
327  function<Sig2> ff_fun = ff;
328  CHECK (1+2+3+4+5 == func::chained(f5_fun, ff ) (_1_,_2_,_3_,_4_,_5_) );
329  CHECK (1+2+3+4+5 == func::chained(f5, ff_fun) (_1_,_2_,_3_,_4_,_5_) );
330  CHECK (1+2+3+4+5 == func::chained(f5_fun, ff_fun) (_1_,_2_,_3_,_4_,_5_) );
331  }
332 
333 
334 
335  void
336  check_bindToArbitraryParameter ()
337  {
338  typedef Num<1> Sig15(Num<1>,Num<2>,Num<3>,Num<4>,Num<5>);
339 
340  typedef Num<1> SigR1( Num<2>,Num<3>,Num<4>,Num<5>);
341  typedef Num<1> SigR2(Num<1>, Num<3>,Num<4>,Num<5>);
342  typedef Num<1> SigR3(Num<1>,Num<2>, Num<4>,Num<5>);
343  typedef Num<1> SigR4(Num<1>,Num<2>,Num<3>, Num<5>);
344  typedef Num<1> SigR5(Num<1>,Num<2>,Num<3>,Num<4> );
345 
346  typedef Num<5> SigA5(Num<5>);
347 
348  Sig15& f = fun15<1,2,3,4,5>;
349  SigA5& f5 = fun11<5>;
350  Tuple<Types<char>> argT(55);
351 
352  function<SigR1> f_bound_1 = BindToArgument<Sig15,char,0>::reduced (f, argT);
353  function<SigR2> f_bound_2 = BindToArgument<Sig15,char,1>::reduced (f, argT);
354  function<SigR3> f_bound_3 = BindToArgument<Sig15,char,2>::reduced (f, argT);
355  function<SigR4> f_bound_4 = BindToArgument<Sig15,char,3>::reduced (f, argT);
356  function<SigR5> f_bound_5 = BindToArgument<Sig15,char,4>::reduced (f, argT);
357 
358  CHECK (55+2+3+4+5 == f_bound_1 ( _2_,_3_,_4_,_5_) );
359  CHECK (1+55+3+4+5 == f_bound_2 (_1_, _3_,_4_,_5_) );
360  CHECK (1+2+55+4+5 == f_bound_3 (_1_,_2_, _4_,_5_) );
361  CHECK (1+2+3+55+5 == f_bound_4 (_1_,_2_,_3_, _5_) );
362  CHECK (1+2+3+4+55 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
363 
364 
365  // degenerate case: specify wrong argument position (behind end of argument list)
366  // causes the argument to be simply ignored and no binding to happen
367  function<Sig15> f_bound_X = BindToArgument<Sig15,char,5>::reduced (f, argT);
368  CHECK (1+2+3+4+5 == f_bound_X (_1_,_2_,_3_,_4_,_5_) );
369 
370 
371  /* check the convenient function-style API */
372 
373  using std::bind;
374 
375  f_bound_5 = bindLast (f, bind(f5, Num<5>(99)));
376  CHECK (1+2+3+4+99 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
377 
378  f_bound_5 = bindLast (f, bind(&f5, Num<5>(99))); // can bind function pointer
379  CHECK (1+2+3+4+99 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
380 
381  function<Sig15> asFunctor(f);
382  f_bound_5 = bindLast (asFunctor, bind(f5, Num<5>(88))); // use functor instead of direct ref
383  CHECK (1+2+3+4+88 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
384  }
385 
386  };
387 
388 
390  LAUNCHER (FunctionComposition_test, "unit common");
391 
392 
393 
394 }}} // 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
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
Support for writing metaprogramming unit-tests dealing with typelists and flags.
constant-wrapper type for debugging purposes, usable for generating lists of distinguishable types ...