/* Copyright (C) 2012 - 2016, The libsigc++ Development Team * * This file is part of libsigc++. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ // The purpose of this test case is threefold. // - Test that C++11 lambda expressions can be used in connection with sigc::slot // and sigc::signal. // - Show that libsigc++ lambda expressions can be replaced by C++11 lambda // expressions. It's shown here as a preparation for deprecating and eventually // deleting the libsigc++ lambda expressions. // See https://bugzilla.gnome.org/show_bug.cgi?id=672555 // - Test the code examples in the documentation in sigc++/adaptors/lambda/base.h // and sigc++/adaptors/lambda/group.h. // // At present (August 2012) this test case contains approximately the same tests // as test_lambda.cc, but with libsigc++ lambda expressions replaced by C++11 // lambda expressions, where possible. // The only real disadvantage of the C++11 lambda expressions is that a slot that // contains an object derived from sigc::trackable is not automatically disconnected // when the object is deleted, if a reference to the object is stored in a C++11 // lambda expression, connected to the slot. But if you use sigc::track_obj(), // the slot is automatically disconnected. Thus, the disadvantage is insignificant. // // To test the C++11 lambda expressions with gcc 4.6.3 (and probably some later // versions of gcc; gcc 4.7.x also understands -std=c++11): // make CXXFLAGS='-g -O2 -std=c++0x' test_cpp11_lambda // ./test_cpp11_lambda // echo $? // If test_cpp11_lambda writes nothing and the return code is 0, the test has passed. #include "testutilities.h" #include #include #include #include #include #include namespace { std::ostringstream result_stream; int foo(int i, int j) { result_stream << "foo(int " << i << ", int " << j << ") "; return 4 * i + j; } void foo_void(int i) { result_stream << "foo_void(int " << i << ")"; } struct bar { int test(int i, int j) { result_stream << "bar::test(int " << i << ", int " << j << ") "; return 4 * i + j; } void test_void(int i) { result_stream << "bar::test_void(int " << i << ")"; } }; void egon(std::string& str) { result_stream << "egon(string '" << str << "')"; str = "egon was here"; } struct book : public sigc::trackable { explicit book(const std::string& name) : name_(name) {} operator std::string&() { return name_; } std::string name_; }; inline std::ostringstream& operator<<(std::ostringstream& s, const book& b) { s << b.name_; return s; } void foo_group1(int i, int j) { result_stream << "foo_group1(int " << i << ", int " << j << ")"; } int bar_group1(int i) { result_stream << "bar_group1(int " << i << ") "; return i + 2; } void foo_group2(int i) { result_stream << "foo_group2(int " << i << ")"; } void foo_group3(int& i) { result_stream << "foo_group3(int " << i << ")"; ++i; } struct bar_group4 : public sigc::trackable { }; void foo_group4(bar_group4&) { result_stream << "foo_group4(bar_group4&)"; } } // end anonymous namespace int main(int argc, char* argv[]) { auto util = TestUtilities::get_instance(); if (!util->check_command_args(argc, argv)) return util->get_result_and_delete_instance() ? EXIT_SUCCESS : EXIT_FAILURE; // test lambda operators // std::cout << "(_1 + _2) (3,4): " << (_1 + _2) (3,4) << std::endl; result_stream << ([](int a, int b) -> int { return a + b; }(3, 4)); util->check_result(result_stream, "7"); // std::cout << "(_1 + 1) (3,4): " << (_1 + 1) (3,4) << std::endl; result_stream << ([](int a, int) -> int { return a + 1; }(3, 4)); util->check_result(result_stream, "4"); // std::cout << "(_2 + 1) (3,4): " << (_2 + 1) (3,4) << std::endl; result_stream << ([](int, int b) -> int { return b + 1; }(3, 4)); util->check_result(result_stream, "5"); // std::cout << "(2 + _1) (3,4): " << (2 + _1) (3,4) << std::endl; result_stream << ([](int a, int) -> int { return 2 + a; }(3, 4)); util->check_result(result_stream, "5"); // std::cout << "(2 + _2) (3,4): " << (2 + _2) (3,4) << std::endl; result_stream << ([](int, int b) -> int { return 2 + b; }(3, 4)); util->check_result(result_stream, "6"); // std::cout << "(_1+_2*_3)(1,2,3): " << (_1+_2*_3)(1,2,3) << std::endl; result_stream << ([](int a, int b, int c) -> int { return a + b * c; }(1, 2, 3)); util->check_result(result_stream, "7"); // std::cout << "((++_1)*2)(1): " << ((++_1)*2)(1) << std::endl; result_stream << ([](int a) -> int { return ++a * 2; }(1)); util->check_result(result_stream, "4"); // std::cout << "((++_1)*2)(a): " << ((++_1)*2)(a); // std::cout << "; a: " << a << std::endl; int a_outer = 1; result_stream << ([](int x) -> int { return ++x * 2; }(a_outer)) << " " << a_outer; util->check_result(result_stream, "4 1"); // gcc can't compile libsigc++ lambda expressions with std::ref() parameters. // See https://bugzilla.gnome.org/show_bug.cgi?id=669128 // std::cout << "((++_1)*2)(ref(a)): " << ((++_1)*2)(std::ref(a)); // std::cout << "; a: " << a << std::endl; result_stream << ([](std::reference_wrapper x) -> int { return ++x * 2; }(std::ref(a_outer))); result_stream << " " << a_outer; util->check_result(result_stream, "4 2"); result_stream << ([](int& x) -> int { return ++x * 2; }(a_outer)); result_stream << " " << a_outer; util->check_result(result_stream, "6 3"); // std::cout << "((++(*_1))*2)(&a): " << ((++(*_1))*2)(&a); // std::cout << "; a: " << a << std::endl; result_stream << ([](int* x) -> int { return ++(*x) * 2; }(&a_outer)); result_stream << " " << a_outer; util->check_result(result_stream, "8 4"); // std::cout << "((--(*(&_1)))*2)(ref(a)): " << ((--(*(&_1)))*2)(std::ref(a)); // std::cout << "; a: " << a << std::endl; result_stream << ([](std::reference_wrapper x) -> int { return --(*(&x)) * 2; }(std::ref(a_outer))); result_stream << " " << a_outer; util->check_result(result_stream, "6 3"); result_stream << ([](int& x) -> int { return --(*(&x)) * 2; }(a_outer)); result_stream << " " << a_outer; util->check_result(result_stream, "4 2"); // std::cout << "(-_1) (-5): " << (-_1) (-5) << std::endl; result_stream << ([](int x) -> int { return -x; }(-5)); util->check_result(result_stream, "5"); // std::cout << "(var(&a)[0])(): " << (sigc::var(&a)[0])() << std::endl; result_stream << ([&a_outer]() -> int { return a_outer; }()); util->check_result(result_stream, "2"); // std::cout << "(_1[_2]) (&a,0): " << (_1[_2]) (&a,0) << std::endl; result_stream << ([](int* x, int y) -> int { return x[y]; }(&a_outer, 0)); util->check_result(result_stream, "2"); // std::cout << "(*_1=_2) (&a,1): " << (*_1=_2) (&a,1) << std::endl; result_stream << ([](int* x, int y) -> int { *x = y; return *x; }(&a_outer, 1)); util->check_result(result_stream, "1"); // Comma operator, https://bugzilla.gnome.org/show_bug.cgi?id=342911 a_outer = -1; int b_outer = -1; int c_outer = -1; // std::cout << "(var(c) = (var(a) = _1, var(b) = _2))(2,3): " // << (sigc::var(c) = (sigc::var(a) = _1, sigc::var(b) = _2))(2,3); // std::cout << "; a: " << a << "; b: " << b << "; c: " << c << std::endl; result_stream << ([&a_outer, &b_outer, &c_outer](int x, int y) -> int { return c_outer = (a_outer = x, b_outer = y); }(2, 3)); result_stream << " " << a_outer << " " << b_outer << " " << c_outer; util->check_result(result_stream, "3 2 3 3"); // c++ restrictions: // - ref() must be used to indicate that the value shall not be copied // - constant() is used to create a lambda and delay execution of "std::cout << 1" // - var() is used to create a lambda that holds a reference and is interchangable with ref() in // lambda operator expressions // - cannot use std::endl without much hackery because it is defined as a template function // - cannot use "\n" without var() because arrays cannot be copied // (std::ref(std::cout) << sigc::constant(1) << sigc::var("\n"))(); []() { result_stream << 1 << "\n"; }(); util->check_result(result_stream, "1\n"); //(std::ref(std::cout) << _1 << std::string("\n"))("hello world"); [](const char* a) { result_stream << a << std::string("\n"); }("hello world"); util->check_result(result_stream, "hello world\n"); //(std::ref(std::cout) << sigc::static_cast_(_1) << std::string("\n"))(1.234); [](double a) { result_stream << static_cast(a) << std::string("\n"); }(1.234); util->check_result(result_stream, "1\n"); // (sigc::var(std::cout) << 1 << sigc::var("\n"))(); []() { result_stream << 1 << "\n"; }(); util->check_result(result_stream, "1\n"); //(sigc::var(std::cout) << _1 << std::string("\n"))("hello world"); [](const char* a) { result_stream << a << std::string("\n"); }("hello world"); util->check_result(result_stream, "hello world\n"); // auto-disconnect // Here's an area where the libsigc++ lambda expressions are advantageous. // If you want to auto-disconnect a slot with a C++11 lambda expression // that contains references to sigc::trackable-derived objects, you must use // sigc::track_obj(). sigc::slot sl1; { book guest_book("karl"); // sl1 = (sigc::var(std::cout) << std::ref(guest_book) << sigc::var("\n")); // sl1 = [&guest_book](std::ostringstream& stream){ stream << guest_book << "\n"; }; // no // auto-disconnect sl1 = sigc::track_obj( [&guest_book](std::ostringstream& stream) { stream << guest_book << "\n"; }, guest_book); sl1(result_stream); util->check_result(result_stream, "karl\n"); } // auto-disconnect sl1(result_stream); util->check_result(result_stream, ""); // test group adaptor, here replaced by std::bind bar the_bar; // std::cout << (sigc::group(&foo, _1, _2)) (1, 2) << std::endl; result_stream << std::bind(&foo, std::placeholders::_1, std::placeholders::_2)(1, 2); util->check_result(result_stream, "foo(int 1, int 2) 6"); // std::cout << (sigc::group(&foo, _2, _1)) (1, 2) << std::endl; result_stream << std::bind(&foo, std::placeholders::_2, std::placeholders::_1)(1, 2); util->check_result(result_stream, "foo(int 2, int 1) 9"); // std::cout << (sigc::group(sigc::mem_fun(&bar::test), _1, _2, _3)) (std::ref(the_bar), 1, 2) << // std::endl; // std::ref(the_bar) is not necessary. It can make the call ambiguous. // Even without std::ref() the_bar is not copied. result_stream << std::bind( std::mem_fn(&bar::test), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)( the_bar, 1, 2); util->check_result(result_stream, "bar::test(int 1, int 2) 6"); // same functionality as bind // std::cout << (sigc::group(&foo, _1, 2)) (1) << std::endl; result_stream << std::bind(&foo, std::placeholders::_1, 2)(1); util->check_result(result_stream, "foo(int 1, int 2) 6"); // std::cout << (sigc::group(&foo, 1, 2)) () << std::endl; result_stream << std::bind(&foo, 1, 2)(); util->check_result(result_stream, "foo(int 1, int 2) 6"); //(sigc::group(sigc::ptr_fun(&foo_void), 1)) (); std::bind(sigc::ptr_fun(&foo_void), 1)(); util->check_result(result_stream, "foo_void(int 1)"); // std::bind() does not work well together with sigc::slot and sigc::signal::connect(). // std::bind() typically creates a functor whose operator()() is a variadic template. // Our functor_trait can't deduce the result type of such a functor. // If the result of std::bind() is assigned to a std::function, the created // functor has an unambiguous operator()(). // auto-disconnect sigc::slot sl2; { book guest_book("karl"); // sl2 = sigc::group(&egon, std::ref(guest_book)); // sl2 = [&guest_book] () { egon(guest_book); }; // no auto-disconnect // sl2 = std::bind(&egon, std::ref(guest_book)); // does not compile (gcc 4.6.3) sl2 = sigc::track_obj([&guest_book]() { egon(guest_book); }, guest_book); sl2(); util->check_result(result_stream, "egon(string 'karl')"); // std::cout << static_cast(guest_book) << std::endl; result_stream << static_cast(guest_book); util->check_result(result_stream, "egon was here"); } // auto-disconnect sl2(); util->check_result(result_stream, ""); // More auto-disconnect { book guest_book("charlie"); // sl2 = sigc::group(&egon, std::ref(guest_book)); // sl2 = std::bind(&egon, std::ref(guest_book)); // does not compile (gcc 4.6.3) auto fn2 = std::bind(&egon, std::ref(guest_book)); // sl2 = fn2; // no auto-disconnect sl2 = sigc::track_obj(fn2, guest_book); sl2(); util->check_result(result_stream, "egon(string 'charlie')"); // std::cout << static_cast(guest_book) << std::endl; result_stream << static_cast(guest_book); util->check_result(result_stream, "egon was here"); } // auto-disconnect sl2(); util->check_result(result_stream, ""); // same functionality as hide // std::cout << (sigc::group(&foo, _1, _2)) (1,2,3) << std::endl; result_stream << std::bind(&foo, std::placeholders::_1, std::placeholders::_2)(1, 2, 3); util->check_result(result_stream, "foo(int 1, int 2) 6"); //(sigc::group(sigc::ptr_fun(&foo_void), _2)) (1, 2); std::bind(&foo_void, std::placeholders::_2)(1, 2); util->check_result(result_stream, "foo_void(int 2)"); // same functionality as compose // std::cout << (sigc::group(&foo, sigc::group(&foo, _1, _2), _3)) (1,2,3) << std::endl; result_stream << std::bind(&foo, std::bind(&foo, std::placeholders::_1, std::placeholders::_2), std::placeholders::_3)(1, 2, 3); util->check_result(result_stream, "foo(int 1, int 2) foo(int 6, int 3) 27"); // same functionality as retype // std::cout << (sigc::group(&foo, sigc::static_cast_(_1), 2)) (1.234) << std::endl; result_stream << ([](double x) -> int { return foo(static_cast(x), 2); }(1.234)); util->check_result(result_stream, "foo(int 1, int 2) 6"); // Code examples with C++11 lambda expressions and std::bind, which can replace // libsigc++ examples in the documentation of libsigc++ lambdas and sigc::group. // ----------------------------------------------------------------------------- //--- sigc++/adaptors/lambda/macros/base.h.m4 // std::cout << sigc::_1(10,20,30); // returns 10 result_stream << ([](int x, int, int) -> int { return x; }(10, 20, 30)); util->check_result(result_stream, "10"); // std::cout << sigc::_2(10,20,30); // returns 20 result_stream << ([](int, int y, int) -> int { return y; }(10, 20, 30)); util->check_result(result_stream, "20"); // std::cout << (sigc::_1 + 5)(3); // returns (3 + 5) result_stream << ([](int x) -> int { return x + 5; }(3)); util->check_result(result_stream, "8"); // std::cout << (sigc::_1 * sigc::_2)(7,10); // returns (7 * 10) result_stream << ([](int x, int y) -> int { return x * y; }(7, 10)); util->check_result(result_stream, "70"); // int main(int argc, char* argv[]) //{ // int data; // sigc::signal readValue; // // readValue.connect(sigc::var(data)); // // data = 3; // std::cout << readValue() << std::endl; //Prints 3. // // data = 5; // std::cout << readValue() << std::endl; //Prints 5. //} { int data; sigc::signal readValue; readValue.connect([&data]() -> int { return data; }); data = 3; result_stream << readValue(); util->check_result(result_stream, "3"); data = 5; result_stream << readValue(); util->check_result(result_stream, "5"); } //--- sigc++/adaptors/lambda/macros/group.h.m4 // argument binding ... // sigc::group(&foo,10,sigc::_1)(20); //fixes the first argument and calls foo(10,20) std::bind(&foo_group1, 10, std::placeholders::_1)(20); util->check_result(result_stream, "foo_group1(int 10, int 20)"); // sigc::group(&foo,sigc::_1,30)(40); //fixes the second argument and calls foo(40,30) std::bind(&foo_group1, std::placeholders::_1, 30)(40); util->check_result(result_stream, "foo_group1(int 40, int 30)"); // argument reordering ... // sigc::group(&foo,sigc::_2,sigc::_1)(1,2); //calls foo(2,1) std::bind(&foo_group1, std::placeholders::_2, std::placeholders::_1)(1, 2); util->check_result(result_stream, "foo_group1(int 2, int 1)"); // argument hiding ... // sigc::group(&foo,sigc::_1,sigc::_2)(1,2,3); //calls foo(1,2) std::bind(&foo_group1, std::placeholders::_1, std::placeholders::_2)(1, 2, 3); util->check_result(result_stream, "foo_group1(int 1, int 2)"); // functor composition ... // sigc::group(&foo,sigc::_1,sigc::group(&bar,sigc::_2))(1,2); //calls foo(1,bar(2)) std::bind(&foo_group1, std::placeholders::_1, std::bind(&bar_group1, std::placeholders::_2))( 1, 2); util->check_result(result_stream, "bar_group1(int 2) foo_group1(int 1, int 4)"); // algebraic expressions ... // sigc::group(&foo,sigc::_1*sigc::_2,sigc::_1/sigc::_2)(6,3); //calls foo(6*3,6/3) [](int x, int y) { foo_group1(x * y, x / y); }(6, 3); util->check_result(result_stream, "foo_group1(int 18, int 2)"); { sigc::signal some_signal; // some_signal.connect(sigc::group(&foo,sigc::_2)); // some_signal.connect(std::bind(&foo_group2, std::placeholders::_2)); // does not compile (gcc // 4.6.3) some_signal.connect([](int, int y) { foo_group2(y); }); some_signal.emit(1, 2); util->check_result(result_stream, "foo_group2(int 2)"); } { int some_int = 0; sigc::signal some_signal; // some_signal.connect(sigc::group(&foo,std::ref(some_int))); // some_signal.connect(std::bind(&foo_group3, std::ref(some_int))); // does not compile (gcc // 4.6.3) // some_signal.connect(sigc::bind(&foo_group3, std::ref(some_int))); // compiles, but we prefer // std::bind() or C++11 lambda some_signal.connect([&some_int]() { foo_group3(some_int); }); some_signal.emit(); result_stream << " " << some_int; util->check_result(result_stream, "foo_group3(int 0) 1"); } { // struct bar : public sigc::trackable {} some_bar; sigc::signal some_signal; { bar_group4 some_bar; // some_signal.connect(sigc::group(&foo, std::ref(some_bar))); // disconnected automatically if some_bar goes out of scope // some_signal.connect([&some_bar](){ foo_group4(some_bar); }); // no auto-disconnect // some_signal.connect(sigc::bind(&foo_group4, std::ref(some_bar))); // auto-disconnects, but // we prefer C++11 lambda some_signal.connect(sigc::track_obj([&some_bar]() { foo_group4(some_bar); }, some_bar)); some_signal.emit(); util->check_result(result_stream, "foo_group4(bar_group4&)"); } some_signal.emit(); util->check_result(result_stream, ""); } return util->get_result_and_delete_instance() ? EXIT_SUCCESS : EXIT_FAILURE; } // end main()