Imported Upstream version 2.99.2
[platform/upstream/libsigc++.git] / tests / test_cpp11_lambda.cc
1 /* Copyright (C) 2012 The libsigc++ Development Team
2  *
3  * This file is part of libsigc++.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 // The purpose of this test case is threefold.
20 // - Test that C++11 lambda expressions can be used in connection with sigc::slot
21 //   and sigc::signal.
22 // - Show that libsigc++ lambda expressions can be replaced by C++11 lambda
23 //   expressions. It's shown here as a preparation for deprecating and eventually
24 //   deleting the libsigc++ lambda expressions.
25 //   See https://bugzilla.gnome.org/show_bug.cgi?id=672555
26 // - Test the code examples in the documentation in sigc++/adaptors/lambda/base.h
27 //   and sigc++/adaptors/lambda/group.h.
28 //
29 // At present (August 2012) this test case contains approximately the same tests
30 // as test_lambda.cc, but with libsigc++ lambda expressions replaced by C++11
31 // lambda expressions, where possible.
32 // The only real disadvantage of the C++11 lambda expressions is that a slot that
33 // contains an object derived from sigc::trackable is not automatically disconnected
34 // when the object is deleted, if a reference to the object is stored in a C++11
35 // lambda expression, connected to the slot. But if you use sigc::track_obj(),
36 // the slot is automatically disconnected. Thus, the disadvantage is insignificant.
37 //
38 // To test the C++11 lambda expressions with gcc 4.6.3 (and probably some later
39 // versions of gcc; gcc 4.7.x also understands -std=c++11):
40 //   make CXXFLAGS='-g -O2 -std=c++0x' test_cpp11_lambda
41 //   ./test_cpp11_lambda
42 //   echo $?
43 // If test_cpp11_lambda writes nothing and the return code is 0, the test has passed.
44
45
46 #include "testutilities.h"
47 #include <string>
48 #include <iostream>
49 #include <sstream>
50 #include <functional>
51 #include <cstdlib>
52 #include <sigc++/functors/functors.h>
53 #include <sigc++/bind.h>
54 #include <sigc++/adaptors/track_obj.h>
55 #include <sigc++/signal.h>
56
57
58 namespace
59 {
60 std::ostringstream result_stream;
61
62 int foo(int i, int j)
63 {
64   result_stream << "foo(int " << i << ", int " << j << ") ";
65   return 4*i + j;
66 }
67
68 void foo_void(int i)
69 {
70   result_stream << "foo_void(int " << i << ")";
71 }
72
73 struct bar
74 {
75   int test(int i, int j)
76   {
77     result_stream << "bar::test(int " << i << ", int " << j << ") ";
78     return 4*i + j;
79   }
80
81   void test_void(int i)
82   {
83     result_stream << "bar::test_void(int " << i << ")";
84   }
85 };
86
87 void egon(std::string& str)
88 {
89   result_stream << "egon(string '" << str << "')";
90   str = "egon was here";
91 }
92
93 struct book : public sigc::trackable
94 {
95   explicit book(const std::string& name) : name_(name) {}
96   operator std::string& () { return name_; }
97   std::string name_;
98 };
99
100 inline std::ostringstream& operator << (std::ostringstream& s, const book& b)
101 {
102   s << b.name_;
103   return s;
104 }
105
106 void foo_group1(int i, int j)
107 {
108   result_stream << "foo_group1(int " << i << ", int " << j << ")";
109 }
110
111 int bar_group1(int i)
112 {
113   result_stream << "bar_group1(int " << i << ") ";
114   return i + 2;
115 }
116
117 void foo_group2(int i)
118 {
119   result_stream << "foo_group2(int " << i << ")";
120 }
121
122 void foo_group3(int& i)
123 {
124   result_stream << "foo_group3(int " << i << ")";
125   ++i;
126 }
127
128 struct bar_group4 : public sigc::trackable
129 {
130 };
131
132 void foo_group4(bar_group4&)
133 {
134   result_stream << "foo_group4(bar_group4&)";
135 }
136
137 } // end anonymous namespace
138
139
140
141 int main(int argc, char* argv[])
142 {
143   auto util = TestUtilities::get_instance();
144
145   if (!util->check_command_args(argc, argv))
146     return util->get_result_and_delete_instance() ? EXIT_SUCCESS : EXIT_FAILURE;
147
148
149   // test lambda operators
150   //std::cout << "(_1 + _2) (3,4):    " << (_1 + _2) (3,4)      << std::endl;
151   result_stream << ([] (int a, int b) -> int { return a + b; }(3,4));
152   util->check_result(result_stream, "7");
153
154   //std::cout << "(_1 + 1)  (3,4):    " << (_1 + 1)  (3,4)      << std::endl;
155   result_stream << ([] (int a, int) -> int { return a + 1; }(3,4));
156   util->check_result(result_stream, "4");
157
158   //std::cout << "(_2 + 1)  (3,4):    " << (_2 + 1)  (3,4)      << std::endl;
159   result_stream << ([] (int, int b) -> int { return b + 1; }(3,4));
160   util->check_result(result_stream, "5");
161
162   //std::cout << "(2 + _1)  (3,4):    " << (2 + _1)  (3,4)      << std::endl;
163   result_stream << ([] (int a, int) -> int { return 2 + a; }(3,4));
164   util->check_result(result_stream, "5");
165
166   //std::cout << "(2 + _2)  (3,4):    " << (2 + _2)  (3,4)      << std::endl;
167   result_stream << ([] (int, int b) -> int { return 2 + b; }(3,4));
168   util->check_result(result_stream, "6");
169
170   //std::cout << "(_1+_2*_3)(1,2,3):  " << (_1+_2*_3)(1,2,3)    << std::endl;
171   result_stream << ([] (int a, int b, int c) -> int { return a + b*c; }(1,2,3));
172   util->check_result(result_stream, "7");
173
174   //std::cout << "((++_1)*2)(1):      " << ((++_1)*2)(1)        << std::endl;
175   result_stream << ([] (int a) -> int { return ++a * 2; }(1));
176   util->check_result(result_stream, "4");
177
178   //std::cout << "((++_1)*2)(a):      " << ((++_1)*2)(a);
179   //std::cout << "; a: "                << a                    << std::endl;
180   int a_outer = 1;
181   result_stream << ([] (int x) -> int { return ++x * 2; }(a_outer)) << " " << a_outer;
182   util->check_result(result_stream, "4 1");
183
184   // gcc can't compile libsigc++ lambda expressions with std::ref() parameters.
185   // See https://bugzilla.gnome.org/show_bug.cgi?id=669128
186   //  std::cout << "((++_1)*2)(ref(a)): " << ((++_1)*2)(std::ref(a));
187   //  std::cout << "; a: "                << a                    << std::endl;
188   result_stream << ([] (std::reference_wrapper<int> x) -> int { return ++x * 2; }(std::ref(a_outer)));
189   result_stream << " " << a_outer;
190   util->check_result(result_stream, "4 2");
191   result_stream << ([] (int& x) -> int { return ++x * 2; }(a_outer));
192   result_stream << " " << a_outer;
193   util->check_result(result_stream, "6 3");
194
195   //std::cout << "((++(*_1))*2)(&a):  " << ((++(*_1))*2)(&a);
196   //std::cout << "; a: "                << a                    << std::endl;
197   result_stream << ([] (int* x) -> int { return ++(*x) * 2; }(&a_outer));
198   result_stream << " " << a_outer;
199   util->check_result(result_stream, "8 4");
200
201   //  std::cout << "((--(*(&_1)))*2)(ref(a)): " << ((--(*(&_1)))*2)(std::ref(a));
202   //  std::cout << "; a: "                << a                    << std::endl;
203   result_stream << ([] (std::reference_wrapper<int> x) -> int { return --(*(&x)) * 2; }(std::ref(a_outer)));
204   result_stream << " " << a_outer;
205   util->check_result(result_stream, "6 3");
206   result_stream << ([] (int& x) -> int { return --(*(&x)) * 2; }(a_outer));
207   result_stream << " " << a_outer;
208   util->check_result(result_stream, "4 2");
209
210   //std::cout << "(-_1)     (-5):     " << (-_1)     (-5)       << std::endl;
211   result_stream << ([] (int x) -> int { return -x; }(-5));
212   util->check_result(result_stream, "5");
213
214   //std::cout << "(var(&a)[0])():     " << (sigc::var(&a)[0])() << std::endl;
215   result_stream << ([&a_outer]() -> int { return a_outer; }());
216   util->check_result(result_stream, "2");
217
218   //std::cout << "(_1[_2])    (&a,0): " << (_1[_2])    (&a,0)   << std::endl;
219   result_stream << ([] (int* x, int y) -> int { return x[y]; }(&a_outer,0));
220   util->check_result(result_stream, "2");
221
222   //std::cout << "(*_1=_2)    (&a,1): " << (*_1=_2)    (&a,1)   << std::endl;
223   result_stream << ([] (int* x, int y) -> int { *x = y; return *x; }(&a_outer,1));
224   util->check_result(result_stream, "1");
225
226   // Comma operator, https://bugzilla.gnome.org/show_bug.cgi?id=342911
227   a_outer = -1;
228   int b_outer = -1;
229   int c_outer = -1;
230   //std::cout << "(var(c) = (var(a) = _1, var(b) = _2))(2,3): "
231   //          << (sigc::var(c) = (sigc::var(a) = _1, sigc::var(b) = _2))(2,3);
232   //std::cout << "; a: " << a << "; b: " << b << "; c: " << c << std::endl;
233   result_stream << ([&a_outer,&b_outer,&c_outer](int x, int y) -> int { return c_outer = (a_outer = x, b_outer = y); }(2,3));
234   result_stream << " " << a_outer << " " << b_outer << " " << c_outer;
235   util->check_result(result_stream, "3 2 3 3");
236
237   // c++ restrictions:
238   // - ref() must be used to indicate that the value shall not be copied
239   // - constant() is used to create a lambda and delay execution of "std::cout << 1"
240   // - var() is used to create a lambda that holds a reference and is interchangable with ref() in lambda operator expressions
241   // - cannot use std::endl without much hackery because it is defined as a template function
242   // - cannot use "\n" without var() because arrays cannot be copied
243   //  (std::ref(std::cout) << sigc::constant(1) << sigc::var("\n"))();
244   [](){ result_stream << 1 << "\n"; }();
245   util->check_result(result_stream, "1\n");
246
247   //(std::ref(std::cout) << _1 << std::string("\n"))("hello world");
248   [](const char* a){ result_stream << a << std::string("\n"); }("hello world");
249   util->check_result(result_stream, "hello world\n");
250
251   //(std::ref(std::cout) << sigc::static_cast_<int>(_1) << std::string("\n"))(1.234);
252   [](double a){ result_stream << static_cast<int>(a) << std::string("\n"); }(1.234);
253   util->check_result(result_stream, "1\n");
254
255   //  (sigc::var(std::cout) << 1 << sigc::var("\n"))();
256   [](){ result_stream << 1 << "\n"; }();
257   util->check_result(result_stream, "1\n");
258
259   //(sigc::var(std::cout) << _1 << std::string("\n"))("hello world");
260   [](const char* a){ result_stream << a << std::string("\n"); }("hello world");
261   util->check_result(result_stream, "hello world\n");
262
263   // auto-disconnect
264   // Here's an area where the libsigc++ lambda expressions are advantageous.
265   // If you want to auto-disconnect a slot with a C++11 lambda expression
266   // that contains references to sigc::trackable-derived objects, you must use
267   // sigc::track_obj().
268   sigc::slot<void(std::ostringstream&)> sl1;
269   {
270     book guest_book("karl");
271     //sl1 = (sigc::var(std::cout) << std::ref(guest_book) << sigc::var("\n"));
272     // sl1 = [&guest_book](std::ostringstream& stream){ stream << guest_book << "\n"; }; // no auto-disconnect
273     sl1 = sigc::track_obj([&guest_book](std::ostringstream& stream){ stream << guest_book << "\n"; }, guest_book);
274     sl1(result_stream);
275     util->check_result(result_stream, "karl\n");
276
277   } // auto-disconnect
278
279   sl1(result_stream);
280   util->check_result(result_stream, "");
281
282   // test group adaptor, here replaced by std::bind
283   bar the_bar;
284   //std::cout << (sigc::group(&foo, _1, _2)) (1, 2) << std::endl;
285   result_stream << std::bind(&foo, std::placeholders::_1, std::placeholders::_2)(1, 2);
286   util->check_result(result_stream, "foo(int 1, int 2) 6");
287
288   //std::cout << (sigc::group(&foo, _2, _1)) (1, 2) << std::endl;
289   result_stream << std::bind(&foo, std::placeholders::_2, std::placeholders::_1)(1, 2);
290   util->check_result(result_stream, "foo(int 2, int 1) 9");
291
292   //std::cout << (sigc::group(sigc::mem_fun(&bar::test), _1, _2, _3)) (std::ref(the_bar), 1, 2) << std::endl;
293   // std::ref(the_bar) is not necessary. It can make the call ambiguous.
294   // Even without std::ref() the_bar is not copied.
295   result_stream << std::bind(std::mem_fn(&bar::test), std::placeholders::_1,
296     std::placeholders::_2, std::placeholders::_3)(the_bar, 1, 2);
297   util->check_result(result_stream, "bar::test(int 1, int 2) 6");
298
299   // same functionality as bind
300   //std::cout << (sigc::group(&foo, _1, 2))  (1)    << std::endl;
301   result_stream << std::bind(&foo, std::placeholders::_1, 2)(1);
302   util->check_result(result_stream, "foo(int 1, int 2) 6");
303
304   //std::cout << (sigc::group(&foo, 1, 2))   ()     << std::endl;
305   result_stream << std::bind(&foo, 1, 2)();
306   util->check_result(result_stream, "foo(int 1, int 2) 6");
307
308   //(sigc::group(sigc::ptr_fun(&foo_void), 1)) ();
309   std::bind(sigc::ptr_fun(&foo_void), 1)();
310   util->check_result(result_stream, "foo_void(int 1)");
311
312   // std::bind() does not work well together with sigc::slot and sigc::signal::connect().
313   // std::bind() typically creates a functor whose operator()() is a variadic template.
314   // Our functor_trait can't deduce the result type of such a functor.
315   // If the result of std::bind() is assigned to a std::function, the created
316   // functor has an unambiguous operator()().
317
318   // auto-disconnect
319   sigc::slot<void()> sl2;
320   {
321     book guest_book("karl");
322     //sl2 = sigc::group(&egon, std::ref(guest_book));
323     // sl2 = [&guest_book] () { egon(guest_book); }; // no auto-disconnect
324     // sl2 = std::bind(&egon, std::ref(guest_book)); // does not compile (gcc 4.6.3)
325     sl2 = sigc::track_obj([&guest_book] () { egon(guest_book); }, guest_book);
326     sl2();
327     util->check_result(result_stream, "egon(string 'karl')");
328
329     //std::cout << static_cast<std::string&>(guest_book) << std::endl;
330     result_stream << static_cast<std::string&>(guest_book);
331     util->check_result(result_stream, "egon was here");
332
333   } // auto-disconnect
334
335   sl2();
336   util->check_result(result_stream, "");
337
338   // More auto-disconnect
339   {
340     book guest_book("charlie");
341     //sl2 = sigc::group(&egon, std::ref(guest_book));
342     // sl2 = std::bind(&egon, std::ref(guest_book)); // does not compile (gcc 4.6.3)
343     auto fn2 = std::bind(&egon, std::ref(guest_book));
344     //sl2 = fn2; // no auto-disconnect
345     sl2 = sigc::track_obj(fn2, guest_book);
346     sl2();
347     util->check_result(result_stream, "egon(string 'charlie')");
348
349     //std::cout << static_cast<std::string&>(guest_book) << std::endl;
350     result_stream << static_cast<std::string&>(guest_book);
351     util->check_result(result_stream, "egon was here");
352
353   } // auto-disconnect
354
355   sl2();
356   util->check_result(result_stream, "");
357
358   // same functionality as hide
359   //std::cout << (sigc::group(&foo, _1, _2)) (1,2,3) << std::endl;
360   result_stream << std::bind(&foo, std::placeholders::_1, std::placeholders::_2)(1,2,3);
361   util->check_result(result_stream, "foo(int 1, int 2) 6");
362
363   //(sigc::group(sigc::ptr_fun(&foo_void), _2)) (1, 2);
364   std::bind(&foo_void, std::placeholders::_2)(1, 2);
365   util->check_result(result_stream, "foo_void(int 2)");
366
367   // same functionality as compose
368   //std::cout << (sigc::group(&foo, sigc::group(&foo, _1, _2), _3)) (1,2,3) << std::endl;
369   result_stream << std::bind(&foo, std::bind(&foo, std::placeholders::_1, std::placeholders::_2),
370     std::placeholders::_3)(1,2,3);
371   util->check_result(result_stream, "foo(int 1, int 2) foo(int 6, int 3) 27");
372
373   // same functionality as retype
374   //std::cout << (sigc::group(&foo, sigc::static_cast_<int>(_1), 2)) (1.234) << std::endl;
375   result_stream << ([] (double x) -> int { return foo(static_cast<int>(x), 2); }(1.234));
376   util->check_result(result_stream, "foo(int 1, int 2) 6");
377
378   // Code examples with C++11 lambda expressions and std::bind, which can replace
379   // libsigc++ examples in the documentation of libsigc++ lambdas and sigc::group.
380   // -----------------------------------------------------------------------------
381
382   //--- sigc++/adaptors/lambda/macros/base.h.m4
383
384   //std::cout << sigc::_1(10,20,30); // returns 10
385   result_stream << ([] (int x, int, int) -> int { return x; }(10,20,30));
386   util->check_result(result_stream, "10");
387
388   //std::cout << sigc::_2(10,20,30); // returns 20
389   result_stream << ([] (int, int y, int) -> int { return y; }(10,20,30));
390   util->check_result(result_stream, "20");
391
392   //std::cout << (sigc::_1 + 5)(3); // returns (3 + 5)
393   result_stream << ([] (int x) -> int { return x + 5; }(3));
394   util->check_result(result_stream, "8");
395
396   //std::cout << (sigc::_1 * sigc::_2)(7,10); // returns (7 * 10)
397   result_stream << ([] (int x, int y) -> int { return x * y; }(7,10));
398   util->check_result(result_stream, "70");
399
400   //int main(int argc, char* argv[])
401   //{
402   //  int data;
403   //  sigc::signal<int()> readValue;
404   //
405   //  readValue.connect(sigc::var(data));
406   //
407   //  data = 3;
408   //  std::cout << readValue() << std::endl; //Prints 3.
409   //
410   //  data = 5;
411   //  std::cout << readValue() << std::endl; //Prints 5.
412   //}
413   {
414     int data;
415     sigc::signal<int()> readValue;
416
417     readValue.connect([&data] () -> int { return data; });
418
419     data = 3;
420     result_stream << readValue();
421     util->check_result(result_stream, "3");
422
423     data = 5;
424     result_stream << readValue();
425     util->check_result(result_stream, "5");
426   }
427
428   //--- sigc++/adaptors/lambda/macros/group.h.m4
429
430   // argument binding ...
431   //sigc::group(&foo,10,sigc::_1)(20); //fixes the first argument and calls foo(10,20)
432   std::bind(&foo_group1, 10, std::placeholders::_1)(20);
433   util->check_result(result_stream, "foo_group1(int 10, int 20)");
434
435   //sigc::group(&foo,sigc::_1,30)(40); //fixes the second argument and calls foo(40,30)
436   std::bind(&foo_group1, std::placeholders::_1, 30)(40);
437   util->check_result(result_stream, "foo_group1(int 40, int 30)");
438
439   // argument reordering ...
440   //sigc::group(&foo,sigc::_2,sigc::_1)(1,2); //calls foo(2,1)
441   std::bind(&foo_group1, std::placeholders::_2, std::placeholders::_1)(1,2);
442   util->check_result(result_stream, "foo_group1(int 2, int 1)");
443
444   // argument hiding ...
445   //sigc::group(&foo,sigc::_1,sigc::_2)(1,2,3); //calls foo(1,2)
446   std::bind(&foo_group1, std::placeholders::_1, std::placeholders::_2)(1,2,3);
447   util->check_result(result_stream, "foo_group1(int 1, int 2)");
448
449   // functor composition ...
450   //sigc::group(&foo,sigc::_1,sigc::group(&bar,sigc::_2))(1,2); //calls foo(1,bar(2))
451   std::bind(&foo_group1,  std::placeholders::_1, std::bind(&bar_group1, std::placeholders::_2))(1,2);
452   util->check_result(result_stream, "bar_group1(int 2) foo_group1(int 1, int 4)");
453
454   // algebraic expressions ...
455   // sigc::group(&foo,sigc::_1*sigc::_2,sigc::_1/sigc::_2)(6,3); //calls foo(6*3,6/3)
456   [] (int x, int y) { foo_group1(x*y, x/y); }(6,3);
457   util->check_result(result_stream, "foo_group1(int 18, int 2)");
458
459   {
460     sigc::signal<void(int, int)> some_signal;
461     //some_signal.connect(sigc::group(&foo,sigc::_2));
462     //some_signal.connect(std::bind(&foo_group2, std::placeholders::_2)); // does not compile (gcc 4.6.3)
463     some_signal.connect([](int, int y) { foo_group2(y); });
464     some_signal.emit(1,2);
465     util->check_result(result_stream, "foo_group2(int 2)");
466   }
467
468   {
469     int some_int = 0;
470     sigc::signal<void()> some_signal;
471     //some_signal.connect(sigc::group(&foo,std::ref(some_int)));
472     //some_signal.connect(std::bind(&foo_group3, std::ref(some_int))); // does not compile (gcc 4.6.3)
473     //some_signal.connect(sigc::bind(&foo_group3, std::ref(some_int))); // compiles, but we prefer std::bind() or C++11 lambda
474     some_signal.connect([&some_int](){ foo_group3(some_int); });
475     some_signal.emit();
476     result_stream << " " << some_int;
477     util->check_result(result_stream, "foo_group3(int 0) 1");
478   }
479
480   {
481     //struct bar : public sigc::trackable {} some_bar;
482     sigc::signal<void()> some_signal;
483     {
484       bar_group4 some_bar;
485       //some_signal.connect(sigc::group(&foo, std::ref(some_bar)));
486       // disconnected automatically if some_bar goes out of scope
487       //some_signal.connect([&some_bar](){ foo_group4(some_bar); }); // no auto-disconnect
488       //some_signal.connect(sigc::bind(&foo_group4, std::ref(some_bar))); // auto-disconnects, but we prefer C++11 lambda
489       some_signal.connect(sigc::track_obj([&some_bar](){ foo_group4(some_bar); }, some_bar));
490       some_signal.emit();
491       util->check_result(result_stream, "foo_group4(bar_group4&)");
492     }
493     some_signal.emit();
494     util->check_result(result_stream, "");
495   }
496
497   return util->get_result_and_delete_instance() ? EXIT_SUCCESS : EXIT_FAILURE;
498 } // end main()