From 41cf46e0fca8128abab298f54209d427a5a6c1b9 Mon Sep 17 00:00:00 2001 From: Kjell Ahlstedt Date: Tue, 28 Aug 2012 19:40:24 +0200 Subject: Add SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH for C++11 lambda expressions. * sigc++/functors/macros/functor_trait.h.m4: Add the preprocessor macro SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(C_keyword), which makes it possible to assign C++11 lambda expressions with any return type to slots. Thanks to Chow Loong Jin, who posted similar code on libsigc-list. * sigc++/adaptors/lambda/macros/base.h.m4: * sigc++/adaptors/lambda/macros/group.h.m4: Add information on C++11 lambda expressions to the documentation of lambda expressions and sigc::group(). * tests/Makefile.am: Add test_cpp11_lambda.cc. * tests/test_cpp11_lambda.cc: New test case, showing that most uses of libsigc++'s lambda expressions can be replaced by standard C++11 lambda expressions. Bug #672555. --- ChangeLog | 16 + sigc++/adaptors/lambda/macros/base.h.m4 | 28 +- sigc++/adaptors/lambda/macros/group.h.m4 | 34 ++- sigc++/functors/macros/functor_trait.h.m4 | 110 +++++-- tests/Makefile.am | 2 + tests/test_cpp11_lambda.cc | 483 ++++++++++++++++++++++++++++++ 6 files changed, 635 insertions(+), 38 deletions(-) create mode 100644 tests/test_cpp11_lambda.cc diff --git a/ChangeLog b/ChangeLog index f8d6870..2077c7e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2012-08-28 Kjell Ahlstedt + + Add SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH for C++11 lambda expressions. + + * sigc++/functors/macros/functor_trait.h.m4: Add the preprocessor macro + SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(C_keyword), which makes it possible to + assign C++11 lambda expressions with any return type to slots. + Thanks to Chow Loong Jin, who posted similar code on libsigc-list. + * sigc++/adaptors/lambda/macros/base.h.m4: + * sigc++/adaptors/lambda/macros/group.h.m4: Add information on C++11 lambda + expressions to the documentation of lambda expressions and sigc::group(). + * tests/Makefile.am: Add test_cpp11_lambda.cc. + * tests/test_cpp11_lambda.cc: New test case, showing that most uses of + libsigc++'s lambda expressions can be replaced by standard C++11 lambda + expressions. Bug #672555. + 2012-03-19 Kjell Ahlstedt Enable test_lambda in 'make check'. diff --git a/sigc++/adaptors/lambda/macros/base.h.m4 b/sigc++/adaptors/lambda/macros/base.h.m4 index 7a558dd..649ba47 100644 --- a/sigc++/adaptors/lambda/macros/base.h.m4 +++ b/sigc++/adaptors/lambda/macros/base.h.m4 @@ -57,7 +57,8 @@ divert(0)dnl namespace sigc { /** @defgroup lambdas Lambdas - * libsigc++ ships with basic lambda functionality and the sigc::group adaptor, which uses lambdas to transform a functor's parameter list. + * libsigc++ ships with basic lambda functionality and the sigc::group adaptor, + * which uses lambdas to transform a functor's parameter list. * * The lambda selectors sigc::_1, sigc::_2, ..., sigc::_9 are used to select the * first, second, ..., nineth argument from a list. @@ -66,7 +67,6 @@ namespace sigc { * @code * std::cout << sigc::_1(10,20,30); // returns 10 * std::cout << sigc::_2(10,20,30); // returns 20 - * ... * @endcode * * Operators are defined so that, for example, lambda selectors can be used as @@ -77,6 +77,16 @@ namespace sigc { * std::cout << (sigc::_1 + 5)(3); // returns (3 + 5) * std::cout << (sigc::_1 * sigc::_2)(7,10); // returns (7 * 10) * @endcode + * + * If your compiler supports C++11 lambda expressions, they are often a good + * alternative to libsigc++'s lambda expressions. The following examples are + * equivalent to the previous ones. + * @code + * [[]] (int x, int, int) -> int { return x; }(10,20,30); // returns 10 + * [[]] (int, int y, int) -> int { return y; }(10,20,30); // returns 20 + * [[]] (int x) -> int { return x + 5; }(3); // returns (3 + 5) + * [[]] (int x, int y) -> int { return x * y; }(7,10); // returns (7 * 10) + * @endcode */ /** A hint to the compiler. @@ -268,10 +278,20 @@ dnl { return lambda::type>(v); } * data = 3; * std::cout << readValue() << std::endl; //Prints 3. * - * data = 5; - * std::cout << readValue() << std::endl; //Prints 5. + * data = 5; + * std::cout << readValue() << std::endl; //Prints 5. * } * @endcode + * + * If your compiler supports C++11 lambda expressions, and you use the macro + * SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(), you can replace + * @code + * readValue.connect(sigc::var(data)); + * @endcode + * in the example by + * @code + * readValue.connect([[&data]] () -> int { return data; }); + * @endcode */ template lambda var(T_type& v) diff --git a/sigc++/adaptors/lambda/macros/group.h.m4 b/sigc++/adaptors/lambda/macros/group.h.m4 index 115d755..02b88ae 100644 --- a/sigc++/adaptors/lambda/macros/group.h.m4 +++ b/sigc++/adaptors/lambda/macros/group.h.m4 @@ -114,31 +114,43 @@ __FIREWALL__ * arguments passed into the new functor. Arguments that don't have a placeholder in one * of the lambda expressions are dropped. * + * If you have a C++11 compiler, a C++11 lambda expression and/or std::bind() is + * often a good alternative to sigc::group(). Such alternatives are shown in the + * following examples, marked with the comment //C++11. + * * @par Examples: * @code * void foo(int, int); * int bar(int); * // argument binding ... * sigc::group(&foo,10,sigc::_1)(20); //fixes the first argument and calls foo(10,20) + * std::bind(&foo, 10, std::placeholders::_1)(20); //C++11 * sigc::group(&foo,sigc::_1,30)(40); //fixes the second argument and calls foo(40,30) + * std::bind(&foo, std::placeholders::_1, 30)(40); //C++11 * // argument reordering ... * sigc::group(&foo,sigc::_2,sigc::_1)(1,2); //calls foo(2,1) + * std::bind(&foo, std::placeholders::_2, std::placeholders::_1)(1,2); //C++11 * // argument hiding ... * sigc::group(&foo,sigc::_1,sigc::_2)(1,2,3); //calls foo(1,2) + * std::bind(&foo, std::placeholders::_1, std::placeholders::_2)(1,2,3); //C++11 * // functor composition ... * sigc::group(&foo,sigc::_1,sigc::group(&bar,sigc::_2))(1,2); //calls foo(1,bar(2)) + * std::bind(&foo, std::placeholders::_1, std::bind(&bar, std::placeholders::_2))(1,2); //C++11 * // algebraic expressions ... * sigc::group(&foo,sigc::_1*sigc::_2,sigc::_1/sigc::_2)(6,3); //calls foo(6*3,6/3) + * [[&foo]] (int x, int y) { foo(x*y, x/y); }(6,3); //C++11 * @endcode * - * The functor sigc::group() returns can be passed into - * sigc::signal::connect() directly. + * The functor sigc::group() returns can be passed into sigc::signal::connect() directly. + * A C++11 lambda expression can be passed into sigc::signal::connect() directly, + * if either it returns void, or you use SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(). * * @par Example: * @code * sigc::signal some_signal; * void foo(int); * some_signal.connect(sigc::group(&foo,sigc::_2)); + * some_signal.connect([[&foo]](int, int y) { foo(y); }); //C++11 * @endcode * * Like in sigc::bind(), you can bind references to functors by passing the objects @@ -150,6 +162,7 @@ __FIREWALL__ * sigc::signal some_signal; * void foo(int&); * some_signal.connect(sigc::group(&foo,sigc::ref(some_int))); + * some_signal.connect([[&foo, &some_int]](){ foo(some_int); }); //C++11 * @endcode * * If you bind an object of a sigc::trackable derived type to a functor @@ -165,7 +178,22 @@ __FIREWALL__ * // disconnected automatically if some_bar goes out of scope * @endcode * - * @ingroup adaptors, lambdas + * std::bind() and C++11 lambda expressions fail here. If you store a + * reference to a sigc::trackable derived object in a C++11 lambda expression, + * and assign this expression to a slot or signal, it will not be disconnected + * automatically when the object goes out of scope. The previous example can + * still be rewritten without sigc::group(). + * @code + * struct bar : public sigc::trackable {} some_bar; + * sigc::signal some_signal; + * void foo(bar&); + * some_signal.connect(sigc::bind(&foo, sigc::ref(some_bar))); + * // disconnected automatically if some_bar goes out of scope + * some_signal.connect([[&foo, &some_bar]](){ foo(some_bar); }); //C++11 + * // NOT disconnected automatically if some_bar goes out of scope + * @endcode + * + * @ingroup adaptors lambdas */ namespace sigc { diff --git a/sigc++/functors/macros/functor_trait.h.m4 b/sigc++/functors/macros/functor_trait.h.m4 index f5997da..fd15c69 100644 --- a/sigc++/functors/macros/functor_trait.h.m4 +++ b/sigc++/functors/macros/functor_trait.h.m4 @@ -46,31 +46,6 @@ struct functor_trait ]) divert(0)dnl -/* - Trait functor_trait: - - This trait allows the user to specific what is the return type - of any type. It has been overloaded to detect the return type and - the functor version of function pointers and class methods as well. - - To populate the return type of user defined and third party functors - use the macro SIGC_FUNCTOR_TRAIT(T_functor,T_return) in - namespace sigc. Multi-type functors are only partly supported. - Try specifying the return type of the functor's operator()() overload. - - Alternatively, you can derive your functors from functor_base and - place "typedef T_return result_type;" in the class definition. - - Use SIGC_FUNCTORS_HAVE_RESULT_TYPE if you want sigc++ to assume that - result_type is defined in all user defined or 3rd-party functors - (except those you specify a return type explicitly with SIGC_FUNCTOR_TRAIT()). - -dnl 01.11.2003: Completely removed support for typeof() since it is non-standard! -dnl You might get away without these conventions if your compiler supports -dnl typeof() and if you don't happen to use the operator()() overload of -dnl sigc++'s adaptors in your program. -dnl -*/ __FIREWALL__ #include @@ -98,12 +73,39 @@ struct nil; * Adaptors are functors that alter the signature of a functor's operator()(). * * libsigc++ defines numerous functors, closures and adaptors. - * Since libsigc++ is a callback libaray, most functors are also closures. + * Since libsigc++ is a callback library, most functors are also closures. * The documentation doesn't distinguish between functors and closures. * * The basic functor types libsigc++ provides are created with ptr_fun() and mem_fun() * and can be converted into slots implicitly. * The set of adaptors that ships with libsigc++ is documented in the @ref adaptors module. + * + * If you want to mix user-defined and third party functors with libsigc++, + * and you want them to be implicitly convertible into slots, libsigc++ must know + * the result type of your functors. There are different ways to achieve that. + * + * - Derive your functors from sigc::functor_base and place + * typedef T_return result_type; in the class definition. + * - Use the macro SIGC_FUNCTOR_TRAIT(T_functor,T_return) in namespace sigc. + * Multi-type functors are only partly supported. + * - Use the macro #SIGC_FUNCTORS_HAVE_RESULT_TYPE, if you want sigc++ to assume + * that result_type is defined in all user-defined or third party functors, + * except those for which you specify a return type explicitly with SIGC_FUNCTOR_TRAIT(). + * - Use the macro SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(C_keyword), if your + * compiler makes it possible. Functors with overloaded operator()() are not + * supported. + * + * The last alterative makes it possible to construct a slot from a C++11 lambda + * expression with any return type. Example: + * @code + * namespace sigc { + * SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(decltype) + * } + * sigc::slot slot1 = [[]](int n)-> bool + * { + * return n == 42; + * }; + * @endcode */ /** A hint to the compiler. @@ -113,7 +115,15 @@ struct nil; */ struct functor_base {}; - +/** Trait that specifies the return type of any type. + * Template specializations for functors derived from sigc::functor_base, + * for function pointers and for class methods are provided. + * + * @tparam T_functor Functor type. + * @tparam I_derives_functor_base Whether @p T_functor inherits from sigc::functor_base. + * + * @ingroup sigcfunctors + */ template ::value> struct functor_trait { @@ -128,12 +138,17 @@ struct functor_trait typedef T_functor functor_type; }; -/** If you want to mix functors from a different library with libsigc++ and - * these functors define @p result_type simply use this macro inside namespace sigc like so: +/** Helper macro, if you want to mix user-defined and third party functors with libsigc++. + * + * If you want to mix functors not derived from sigc::functor_base with libsigc++, and + * these functors define @p result_type, use this macro inside namespace sigc like so: * @code * namespace sigc { SIGC_FUNCTORS_HAVE_RESULT_TYPE } * @endcode * + * You can't use both SIGC_FUNCTORS_HAVE_RESULT_TYPE and + * SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH in the same compilation unit. + * * @ingroup sigcfunctors */ #define SIGC_FUNCTORS_HAVE_RESULT_TYPE \ @@ -144,8 +159,10 @@ struct functor_trait \ typedef T_functor functor_type; \ }; -/** If you want to mix functors from a different library with libsigc++ and - * these functors don't define @p result_type use this macro inside namespace sigc +/** Helper macro, if you want to mix user-defined and third party functors with libsigc++. + * + * If you want to mix functors not derived from sigc::functor_base with libsigc++, and + * these functors don't define @p result_type, use this macro inside namespace sigc * to expose the return type of the functors like so: * @code * namespace sigc { @@ -165,6 +182,37 @@ struct functor_trait \ typedef T_functor functor_type; \ }; +/** Helper macro, if you want to mix user-defined and third party functors with libsigc++. + * + * If you want to mix functors not derived from sigc::functor_base with libsigc++, and + * your compiler can deduce the result type of the functor, use this macro inside + * namespace sigc like so: + * @code + * namespace sigc { + * SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(compiler_keyword) + * } + * @endcode + * + * For example, if your compiler understands the C++11 keyword decltype: + * @code + * namespace sigc { + * SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(decltype) + * } + * @endcode + * + * You can't use both SIGC_FUNCTORS_HAVE_RESULT_TYPE and + * SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH in the same compilation unit. + * + * @ingroup sigcfunctors + */ +#define SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(C_keyword) \ +template \ +struct functor_trait \ +{ \ + typedef typename functor_trait::result_type result_type; \ + typedef T_functor functor_type; \ +}; + // detect the return type and the functor version of non-functor types. FOR(0,CALL_SIZE,[[FUNCTOR_PTR_FUN(%1)]]) FOR(0,CALL_SIZE,[[FUNCTOR_MEM_FUN(%1)]]) diff --git a/tests/Makefile.am b/tests/Makefile.am index af20d84..6fd1dcb 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -26,6 +26,7 @@ check_PROGRAMS = \ test_bind_ref \ test_bind_refptr \ test_bind_return \ + test_cpp11_lambda \ test_compose \ test_copy_invalid_slot \ test_custom \ @@ -55,6 +56,7 @@ test_bind_SOURCES = test_bind.cc test_bind_ref_SOURCES = test_bind_ref.cc test_bind_refptr_SOURCES = test_bind_refptr.cc test_bind_return_SOURCES = test_bind_return.cc +test_cpp11_lambda_SOURCES = test_cpp11_lambda.cc test_compose_SOURCES = test_compose.cc test_copy_invalid_slot_SOURCES = test_copy_invalid_slot.cc test_custom_SOURCES = test_custom.cc diff --git a/tests/test_cpp11_lambda.cc b/tests/test_cpp11_lambda.cc new file mode 100644 index 0000000..9e67b22 --- /dev/null +++ b/tests/test_cpp11_lambda.cc @@ -0,0 +1,483 @@ +/* Copyright (C) 2012 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. +// +// To test the C++11 lambda expressions with gcc 4.6.3 (and probably some later +// versions of gcc): +// 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. + +#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define USING_CPP11_LAMBDA_EXPRESSIONS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USING_CPP11_LAMBDA_EXPRESSIONS +namespace sigc +{ + SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH(decltype) +} +#endif + +namespace +{ +bool result_ok = true; +int test_number = 0; +std::ostringstream result_stream; + +void check_result(const std::string& expected_result) +{ + ++test_number; + if (expected_result != result_stream.str()) + { + std::cout << "Test " << test_number << ". Expected \"" << expected_result + << "\", got \"" << result_stream.str() << "\"" << std::endl; + result_ok = false; + } + result_stream.str(""); +} + +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() +{ +#ifdef USING_CPP11_LAMBDA_EXPRESSIONS + + // test lambda operators + int a = 1; + //std::cout << "(_1 + _2) (3,4): " << (_1 + _2) (3,4) << std::endl; + result_stream << ([] (int a, int b) -> int { return a + b; }(3,4)); + check_result("7"); + + //std::cout << "(_1 + 1) (3,4): " << (_1 + 1) (3,4) << std::endl; + result_stream << ([] (int a, int) -> int { return a + 1; }(3,4)); + check_result("4"); + + //std::cout << "(_2 + 1) (3,4): " << (_2 + 1) (3,4) << std::endl; + result_stream << ([] (int, int b) -> int { return b + 1; }(3,4)); + check_result("5"); + + //std::cout << "(2 + _1) (3,4): " << (2 + _1) (3,4) << std::endl; + result_stream << ([] (int a, int) -> int { return 2 + a; }(3,4)); + check_result("5"); + + //std::cout << "(2 + _2) (3,4): " << (2 + _2) (3,4) << std::endl; + result_stream << ([] (int, int b) -> int { return 2 + b; }(3,4)); + check_result("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)); + check_result("7"); + + //std::cout << "((++_1)*2)(1): " << ((++_1)*2)(1) << std::endl; + result_stream << ([] (int a) -> int { return ++a * 2; }(1)); + check_result("4"); + + //std::cout << "((++_1)*2)(a): " << ((++_1)*2)(a); + //std::cout << "; a: " << a << std::endl; + result_stream << ([] (int x) -> int { return ++x * 2; }(a)) << " " << a; + check_result("4 1"); + + // gcc can't compile libsigc++ lambda expressions with sigc::ref() parameters. + // See https://bugzilla.gnome.org/show_bug.cgi?id=669128 + // std::cout << "((++_1)*2)(ref(a)): " << ((++_1)*2)(sigc::ref(a)); + // std::cout << "; a: " << a << std::endl; + result_stream << ([] (std::reference_wrapper x) -> int { return ++x * 2; }(std::ref(a))); + result_stream << " " << a; + check_result("4 2"); + result_stream << ([] (int& x) -> int { return ++x * 2; }(a)); + result_stream << " " << a; + check_result("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)); + result_stream << " " << a; + check_result("8 4"); + + // std::cout << "((--(*(&_1)))*2)(ref(a)): " << ((--(*(&_1)))*2)(sigc::ref(a)); + // std::cout << "; a: " << a << std::endl; + result_stream << ([] (std::reference_wrapper x) -> int { return --(*(&x)) * 2; }(std::ref(a))); + result_stream << " " << a; + check_result("6 3"); + result_stream << ([] (int& x) -> int { return --(*(&x)) * 2; }(a)); + result_stream << " " << a; + check_result("4 2"); + + //std::cout << "(-_1) (-5): " << (-_1) (-5) << std::endl; + result_stream << ([] (int x) -> int { return -x; }(-5)); + check_result("5"); + + //std::cout << "(var(&a)[0])(): " << (sigc::var(&a)[0])() << std::endl; + result_stream << ([&a]() -> int { return a; }()); + check_result("2"); + + //std::cout << "(_1[_2]) (&a,0): " << (_1[_2]) (&a,0) << std::endl; + result_stream << ([] (int* x, int y) -> int { return x[y]; }(&a,0)); + check_result("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,1)); + check_result("1"); + + // 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 + // (sigc::ref(std::cout) << sigc::constant(1) << sigc::var("\n"))(); + [&result_stream](){ result_stream << 1 << "\n"; }(); + check_result("1\n"); + + //(sigc::ref(std::cout) << _1 << std::string("\n"))("hello world"); + [&result_stream](const char* a){ result_stream << a << std::string("\n"); }("hello world"); + check_result("hello world\n"); + + //(sigc::ref(std::cout) << sigc::static_cast_(_1) << std::string("\n"))(1.234); + [&result_stream](double a){ result_stream << static_cast(a) << std::string("\n"); }(1.234); + check_result("1\n"); + + // (sigc::var(std::cout) << 1 << sigc::var("\n"))(); + [&result_stream](){ result_stream << 1 << "\n"; }(); + check_result("1\n"); + + //(sigc::var(std::cout) << _1 << std::string("\n"))("hello world"); + [&result_stream](const char* a){ result_stream << a << std::string("\n"); }("hello world"); + check_result("hello world\n"); + + // auto-disconnect + // Here's an area where the libsigc++ lambda expressions has an advantage. + // It can be more difficult to auto-disconnect slots without them. + sigc::slot sl1; + { + struct printable_book : public book + { + explicit printable_book(const std::string& name) : book(name) {} + void operator()(std::ostringstream& stream) { stream << *this << "\n"; } + }; + //book guest_book("karl"); + printable_book printable_guest_book("karl"); + // sl1 = (sigc::var(std::cout) << sigc::ref(guest_book) << sigc::var("\n")); + // sl1 = [&result_stream, &guest_book](){ result_stream << guest_book << "\n"; }; // no auto-disconnect + // sl1 = printable_guest_book; // no auto-disconnect, no error; a copy is stored in sl1 + sl1 = sigc::mem_fun(printable_guest_book, &printable_book::operator()); + sl1(result_stream); + check_result("karl\n"); + + } // auto-disconnect + + sl1(result_stream); + check_result(""); + + // 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); + check_result("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); + check_result("foo(int 2, int 1)9"); + + //std::cout << (sigc::group(sigc::mem_fun(&bar::test), _1, _2, _3)) (sigc::ref(the_bar), 1, 2) << std::endl; + result_stream << std::bind(std::mem_fn(&bar::test), std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3)(std::ref(the_bar), 1, 2); + check_result("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); + check_result("foo(int 1, int 2)6"); + + //std::cout << (sigc::group(&foo, 1, 2)) () << std::endl; + result_stream << std::bind(&foo, 1, 2)(); + check_result("foo(int 1, int 2)6"); + + //(sigc::group(sigc::ptr_fun(&foo_void), 1)) (); + std::bind(sigc::ptr_fun(&foo_void), 1)(); + check_result("foo_void(int 1)"); + + // auto-disconnect + sigc::slot sl2; + { + book guest_book("karl"); + //sl2 = sigc::group(&egon, sigc::ref(guest_book)); + // sl2 = [&egon, &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::bind(&egon, sigc::ref(guest_book)); + sl2(); + check_result("egon(string 'karl')"); + + //std::cout << static_cast(guest_book) << std::endl; + result_stream << static_cast(guest_book); + check_result("egon was here"); + + } // auto-disconnect + + sl2(); + check_result(""); + + // 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); + check_result("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); + check_result("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); + check_result("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 << ([&foo] (double x) -> int { return foo(static_cast(x), 2); }(1.234)); + check_result("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)); + check_result("10"); + + //std::cout << sigc::_2(10,20,30); // returns 20 + result_stream << ([] (int, int y, int) -> int { return y; }(10,20,30)); + check_result("20"); + + //std::cout << (sigc::_1 + 5)(3); // returns (3 + 5) + result_stream << ([] (int x) -> int { return x + 5; }(3)); + check_result("8"); + + //std::cout << (sigc::_1 * sigc::_2)(7,10); // returns (7 * 10) + result_stream << ([] (int x, int y) -> int { return x * y; }(7,10)); + check_result("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(); + check_result("3"); + + data = 5; + result_stream << readValue(); + check_result("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); + check_result("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); + check_result("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); + check_result("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); + check_result("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); + check_result("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) + [&foo_group1] (int x, int y) { foo_group1(x*y, x/y); }(6,3); + check_result("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([&foo_group2](int, int y) { foo_group2(y); }); + some_signal.emit(1,2); + check_result("foo_group2(int 2)"); + } + + { + int some_int = 0; + sigc::signal some_signal; + //some_signal.connect(sigc::group(&foo,sigc::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, sigc::ref(some_int))); // compiles, but we prefer std::bind() or C++11 lambda + some_signal.connect([&foo_group3, &some_int](){ foo_group3(some_int); }); + some_signal.emit(); + result_stream << some_int; + check_result("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,sigc::ref(some_bar))); + // disconnected automatically if some_bar goes out of scope + //some_signal.connect([&foo_group4, &some_bar](){ foo_group4(some_bar); }); // no auto-disconnect + some_signal.connect(sigc::bind(&foo_group4, sigc::ref(some_bar))); + some_signal.emit(); + check_result("foo_group4(bar_group4&)"); + } + some_signal.emit(); + check_result(""); + } + +#else // not USING_CPP11_LAMBDA_EXPRESSIONS + std::cout << "The compiler capabilities don't allow test of C++11 lambda expressions." << std::endl; +#endif + + return result_ok ? EXIT_SUCCESS : EXIT_FAILURE; +} -- cgit v1.2.1