[libsigc++2] Add track_obj() and test_track_obj.



commit b0727f4e8cbb289d66b11fc29adf133f6c86847e
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date:   Tue Feb 26 09:29:34 2013 +0100

    Add track_obj() and test_track_obj.
    
    * sigc++/.gitignore: Add adaptors/track_obj.h.
    * sigc++/adaptors/adaptors.h: Add sigc++/adaptors/track_obj.h.
    * sigc++/adaptors/lambda/macros/group.h.m4:
    * sigc++/adaptors/macros/adaptor_trait.h.m4: Mention track_obj() in the
    documentation.
    * sigc++/adaptors/macros/track_obj.h.m4: New file.
    * sigc++/filelist.am: Add track_obj.h.m4 and track_obj.h.
    * tests/.gitignore:
    * tests/Makefile.am: Add test_track_obj.
    * tests/test_cpp11_lambda.cc: Use track_obj() to test auto-disconnection.
    * tests/test_track_obj.cc: New test case.
    Bug #672555.

 ChangeLog                                 |   17 ++
 sigc++/.gitignore                         |    1 +
 sigc++/adaptors/adaptors.h                |    1 +
 sigc++/adaptors/lambda/macros/group.h.m4  |   19 +--
 sigc++/adaptors/macros/adaptor_trait.h.m4 |    5 +-
 sigc++/adaptors/macros/track_obj.h.m4     |  218 +++++++++++++++++++++++++++
 sigc++/filelist.am                        |    6 +-
 tests/.gitignore                          |    1 +
 tests/Makefile.am                         |    4 +-
 tests/test_cpp11_lambda.cc                |   30 ++--
 tests/test_track_obj.cc                   |  230 +++++++++++++++++++++++++++++
 11 files changed, 498 insertions(+), 34 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 4c75b9e..a572270 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2013-02-26  Kjell Ahlstedt  <kjell ahlstedt bredband net>
+
+       Add track_obj() and test_track_obj.
+
+       * sigc++/.gitignore: Add adaptors/track_obj.h.
+       * sigc++/adaptors/adaptors.h: Add sigc++/adaptors/track_obj.h.
+       * sigc++/adaptors/lambda/macros/group.h.m4:
+       * sigc++/adaptors/macros/adaptor_trait.h.m4: Mention track_obj() in the
+       documentation.
+       * sigc++/adaptors/macros/track_obj.h.m4: New file.
+       * sigc++/filelist.am: Add track_obj.h.m4 and track_obj.h.
+       * tests/.gitignore:
+       * tests/Makefile.am: Add test_track_obj.
+       * tests/test_cpp11_lambda.cc: Use track_obj() to test auto-disconnection.
+       * tests/test_track_obj.cc: New test case.
+       Bug #672555.
+
 2013-01-07  Kjell Ahlstedt  <kjell ahlstedt bredband net>
 
        Documentation: Fix many warnings from Doxygen.
diff --git a/sigc++/.gitignore b/sigc++/.gitignore
index 5930a6c..ff73c06 100644
--- a/sigc++/.gitignore
+++ b/sigc++/.gitignore
@@ -15,6 +15,7 @@
 /adaptors/hide.h
 /adaptors/retype.h
 /adaptors/retype_return.h
+/adaptors/track_obj.h
 /adaptors/lambda/base.h
 /adaptors/lambda/group.h
 /adaptors/lambda/lambda.cc
diff --git a/sigc++/adaptors/adaptors.h b/sigc++/adaptors/adaptors.h
index 950063b..ef484a4 100644
--- a/sigc++/adaptors/adaptors.h
+++ b/sigc++/adaptors/adaptors.h
@@ -27,6 +27,7 @@
 #include <sigc++/adaptors/retype.h>
 #include <sigc++/adaptors/compose.h>
 #include <sigc++/adaptors/exception_catch.h>
+#include <sigc++/adaptors/track_obj.h>
 #include <sigc++/adaptors/lambda/lambda.h>
 
 #endif /* _SIGC_ADAPTOR_HPP_ */
diff --git a/sigc++/adaptors/lambda/macros/group.h.m4 b/sigc++/adaptors/lambda/macros/group.h.m4
index 1d4663c..f3b6cb8 100644
--- a/sigc++/adaptors/lambda/macros/group.h.m4
+++ b/sigc++/adaptors/lambda/macros/group.h.m4
@@ -169,6 +169,10 @@ __FIREWALL__
  * by reference, a slot assigned to the group adaptor is cleared automatically
  * when the object goes out of scope.
  *
+ * If you bind an object of a sigc::trackable derived type to a C++11 lambda expression
+ * by reference, a slot assigned to the lambda expression is cleared automatically
+ * when the object goes out of scope only if you use sigc::track_obj().
+ *
  * @par Example:
  * @code
  * struct bar : public sigc::trackable {} some_bar;
@@ -176,21 +180,10 @@ __FIREWALL__
  * void foo(bar&);
  * some_signal.connect(sigc::group(&foo,sigc::ref(some_bar)));
  *   // disconnected automatically if some_bar goes out of scope
- * @endcode
- *
- * 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<void> 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([[&some_bar]](){ foo(some_bar); }); //C++11
  *   // NOT disconnected automatically if some_bar goes out of scope
+ * some_signal.connect(sigc::track_obj([[&some_bar]](){ foo(some_bar); }, some_bar)); //C++11
+ *   // disconnected automatically if some_bar goes out of scope
  * @endcode
  *
  * @ingroup adaptors lambdas
diff --git a/sigc++/adaptors/macros/adaptor_trait.h.m4 b/sigc++/adaptors/macros/adaptor_trait.h.m4
index 2456904..7943a19 100644
--- a/sigc++/adaptors/macros/adaptor_trait.h.m4
+++ b/sigc++/adaptors/macros/adaptor_trait.h.m4
@@ -104,13 +104,14 @@ template <class T_functor> struct adapts;
  *
  * The adaptor types libsigc++ provides
  * are created with bind(), bind_return(), hide(), hide_return(),
- * retype_return(), retype(), compose(), exception_catch() and group().
+ * retype_return(), retype(), compose(), exception_catch(), track_obj()
+ * and group().
  *
  * You can easily derive your own adaptor type from sigc::adapts.
  */
 
 /** Converts an arbitrary functor into an adaptor type.
- * All adaptor tyes in libsigc++ are unnumbered and have
+ * All adaptor types in libsigc++ have
  * a <tt>template operator()</tt> member of every argument count
  * they support. These functions in turn invoke a stored adaptor's
  * <tt>template operator()</tt>, processing the arguments and return
diff --git a/sigc++/adaptors/macros/track_obj.h.m4 b/sigc++/adaptors/macros/track_obj.h.m4
new file mode 100644
index 0000000..d79c6d6
--- /dev/null
+++ b/sigc++/adaptors/macros/track_obj.h.m4
@@ -0,0 +1,218 @@
+dnl Copyright 2013, The libsigc++ Development Team
+dnl
+dnl This file is part of libsigc++.
+dnl
+dnl This library is free software; you can redistribute it and/or
+dnl modify it under the terms of the GNU Lesser General Public
+dnl License as published by the Free Software Foundation; either
+dnl version 2.1 of the License, or (at your option) any later version.
+dnl
+dnl This library is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+dnl Lesser General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU Lesser General Public
+dnl License along with this library. If not, see <http://www.gnu.org/licenses/>.
+dnl
+divert(-1)
+
+include(template.macros.m4)
+
+define([TRACK_OBJECT_OPERATOR],[dnl
+  /** Invokes the wrapped functor passing on the arguments.dnl
+FOR(1, $1,[
+   * @param _A_arg%1 Argument to be passed on to the functor.])
+   * @return The return value of the functor invocation.
+   */
+  template <LOOP([typename T_arg%1], $1)>
+  typename deduce_result_type<LOOP(T_arg%1, $1)>::type
+  operator()(LOOP(T_arg%1 _A_arg%1, $1))
+  {
+    return this->functor_.SIGC_WORKAROUND_OPERATOR_PARENTHESES<LOOP([
+      _P_(T_arg%1)], $1)>
+      (LOOP(_A_arg%1, $1));
+  }
+
+  #ifndef SIGC_TEMPLATE_SPECIALIZATION_OPERATOR_OVERLOAD
+  template <LOOP([typename T_arg%1], $1)>
+  typename deduce_result_type<LOOP(T_arg%1, $1)>::type
+  sun_forte_workaround(LOOP(T_arg%1 _A_arg%1, $1))
+  {
+    return this->functor_.SIGC_WORKAROUND_OPERATOR_PARENTHESES<LOOP([
+      _P_(T_arg%1)], $1)>
+      (LOOP(_A_arg%1, $1));
+  }
+  #endif
+
+])dnl end TRACK_OBJECT_OPERATOR
+
+dnl track_obj_functor[2..CALL_SIZE]. $1 is assumed to be >= 2.
+define([TRACK_OBJECT_FUNCTOR],[dnl
+/** track_obj_functor$1 wraps a functor and stores $1 references to trackable objects.
+ * Use the convenience function track_obj() to create an instance of track_obj_functor$1.
+ *
+ * @tparam T_functor The type of functor to wrap.dnl
+FOR(1,$1,[
+ * @tparam T_obj%1 The type of a trackable object.])
+ *
+ * @newin{2,4}
+ *
+ * @ingroup track_obj
+ */
+template <typename T_functor, LOOP(typename T_obj%1, $1)>
+class track_obj_functor$1 : public track_obj_functor1<T_functor, T_obj1>
+{
+public:
+  /** Constructs a track_obj_functor$1 object that wraps the passed functor and
+   * stores references to the passed trackable objects.
+   * @param _A_func Functor.dnl
+FOR(1,$1,[
+   * @param _A_obj%1 Trackable object.])
+   */
+  track_obj_functor$1(const T_functor& _A_func, LOOP(const T_obj%1& _A_obj%1, $1))
+  : track_obj_functor1<T_functor, T_obj1>(_A_func, _A_obj1)FOR(2,$1,[[, ]obj%1_(_A_obj%1)]) {}
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+//protected:
+  // public, so that visit_each() can access it.dnl
+FOR(2,$1,[
+  const_limit_reference<T_obj%1> obj%1_;])
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+}; // end class track_obj_functor$1
+
+])dnl end TRACK_OBJECT_FUNCTOR
+
+define([TRACK_OBJECT_VISIT_EACH],[dnl
+//template specialization of visit_each<>(action, functor):
+/** Performs a functor on each of the targets of a functor.
+ * The function overload for sigc::track_obj_functor$1 performs a functor
+ * on the functor and on the trackable object instances stored in the
+ * sigc::track_obj_functor$1 object.
+ *
+ * @newin{2,4}
+ *
+ * @ingroup track_obj
+ */
+template <typename T_action, typename T_functor, LOOP(typename T_obj%1, $1)>
+void visit_each(const T_action& _A_action,
+                const track_obj_functor$1<T_functor, LOOP(T_obj%1, $1)>& _A_target)
+{
+  sigc::visit_each(_A_action, _A_target.functor_);dnl
+FOR(1,$1,[
+  sigc::visit_each(_A_action, _A_target.obj%1_);])
+}
+
+])dnl end TRACK_OBJECT_VISIT_EACH
+
+define([TRACK_OBJECT],[dnl
+/** Creates an adaptor of type sigc::track_obj_functor$1 which wraps a functor.
+ * @param _A_func Functor that shall be wrapped.dnl
+FOR(1,$1,[
+ * @param _A_obj%1 Trackable object.])
+ * @return Adaptor that executes _A_func() on invocation.
+ *
+ * @newin{2,4}
+ *
+ * @ingroup track_obj
+ */
+template <typename T_functor, LOOP(typename T_obj%1, $1)>
+inline track_obj_functor$1<T_functor, LOOP(T_obj%1, $1)>
+track_obj(const T_functor& _A_func, LOOP(const T_obj%1& _A_obj%1, $1))
+{
+  return track_obj_functor$1<T_functor, LOOP(T_obj%1, $1)>
+    (_A_func, LOOP(_A_obj%1, $1));
+}
+
+])dnl end TRACK_OBJECT
+
+divert(0)dnl
+__FIREWALL__
+#include <sigc++/adaptors/adaptor_trait.h>
+#include <sigc++/limit_reference.h>
+
+namespace sigc {
+
+/** @defgroup track_obj track_obj()
+ * track_obj() tracks trackable objects, referenced from a functor.
+ * It can be useful when you assign a C++11 lambda expression to a slot, or connect
+ * it to a signal, and the lambda expression contains references to sigc::trackable
+ * derived objects.
+ *
+ * The functor returned by sigc::track_obj() is formally an adaptor, but it does
+ * not alter the signature, return type or behaviour of the supplied functor.
+ * Up to CALL_SIZE objects can be tracked. operator()() can have up to CALL_SIZE arguments.
+ *
+ * @par Example:
+ * @code
+ * namespace sigc { SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE }
+ * struct bar : public sigc::trackable {};
+ * sigc::signal<void> some_signal;
+ * void foo(bar&);
+ * {
+ *   bar some_bar;
+ *   some_signal.connect([[&some_bar]](){ foo(some_bar); });
+ *     // NOT disconnected automatically when some_bar goes out of scope
+ *   some_signal.connect(sigc::track_obj([[&some_bar]](){ foo(some_bar); }, some_bar);
+ *     // disconnected automatically when some_bar goes out of scope
+ * }
+ * @endcode
+ *
+ * @newin{2,4}
+ *
+ * @ingroup adaptors
+ */
+
+/** track_obj_functor1 wraps a functor and stores a reference to a trackable object.
+ * Use the convenience function track_obj() to create an instance of track_obj_functor1.
+ *
+ * @tparam T_functor The type of functor to wrap.
+ * @tparam T_obj1 The type of a trackable object.
+ *
+ * @newin{2,4}
+ *
+ * @ingroup track_obj
+ */
+template <typename T_functor, typename T_obj1>
+class track_obj_functor1 : public adapts<T_functor>
+{
+public:
+  typedef typename adapts<T_functor>::adaptor_type adaptor_type;
+
+  template <LOOP(typename T_arg%1=void, CALL_SIZE)>
+  struct deduce_result_type
+    { typedef typename adaptor_type::template deduce_result_type<LOOP(_P_(T_arg%1), CALL_SIZE)>::type type; 
};
+  typedef typename adaptor_type::result_type result_type;
+
+  /** Constructs a track_obj_functor1 object that wraps the passed functor and
+   * stores a reference to the passed trackable object.
+   * @param _A_func Functor.
+   * @param _A_obj1 Trackable object.
+   */
+  track_obj_functor1(const T_functor& _A_func, const T_obj1& _A_obj1)
+  : adapts<T_functor>(_A_func), obj1_(_A_obj1) {}
+
+  /** Invokes the wrapped functor.
+   * @return The return value of the functor invocation.
+   */
+  result_type operator()()
+  { return this->functor_(); }
+
+FOR(1,CALL_SIZE,[[TRACK_OBJECT_OPERATOR(%1)]])dnl
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+//protected:
+  // public, so that visit_each() can access it.
+  const_limit_reference<T_obj1> obj1_;
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+}; // end class track_obj_functor1
+
+FOR(2,CALL_SIZE,[[TRACK_OBJECT_FUNCTOR(%1)]])dnl
+
+FOR(1,CALL_SIZE,[[TRACK_OBJECT_VISIT_EACH(%1)]])dnl
+
+FOR(1,CALL_SIZE,[[TRACK_OBJECT(%1)]])dnl
+
+} /* namespace sigc */
diff --git a/sigc++/filelist.am b/sigc++/filelist.am
index dc90d3f..b230902 100644
--- a/sigc++/filelist.am
+++ b/sigc++/filelist.am
@@ -30,10 +30,12 @@ functors_built_h = functor_trait.h slot.h ptr_fun.h mem_fun.h
 
 # Adaptors (adaptors/)
 adaptors_m4 = deduce_result_type.h.m4 adaptor_trait.h.m4 bind.h.m4 bind_return.h.m4 \
-             retype_return.h.m4 hide.h.m4 retype.h.m4 compose.h.m4 exception_catch.h.m4
+             retype_return.h.m4 hide.h.m4 retype.h.m4 compose.h.m4 exception_catch.h.m4 \
+             track_obj.h.m4
 adaptors_built_cc =
 adaptors_built_h = deduce_result_type.h adaptor_trait.h bind.h bind_return.h \
-                   retype_return.h hide.h retype.h compose.h exception_catch.h
+                   retype_return.h hide.h retype.h compose.h exception_catch.h \
+                   track_obj.h
 
 # Lambda (adaptors/lambda)
 lambda_m4 = base.h.m4 select.h.m4 operator.h.m4 group.h.m4 lambda.cc.m4
diff --git a/tests/.gitignore b/tests/.gitignore
index 630aeb3..dfc65dd 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -25,3 +25,4 @@
 /test_slot
 /test_slot_disconnect
 /test_trackable
+/test_track_obj
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 067fd71..367cdd4 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -46,7 +46,8 @@ check_PROGRAMS = \
   test_size \
   test_slot \
   test_slot_disconnect \
-  test_trackable
+  test_trackable \
+  test_track_obj
 
 TESTS = $(check_PROGRAMS)
 
@@ -79,6 +80,7 @@ test_size_SOURCES            = test_size.cc $(sigc_test_util)
 test_slot_SOURCES            = test_slot.cc $(sigc_test_util)
 test_slot_disconnect_SOURCES = test_slot_disconnect.cc $(sigc_test_util)
 test_trackable_SOURCES       = test_trackable.cc $(sigc_test_util)
+test_track_obj_SOURCES       = test_track_obj.cc $(sigc_test_util)
 
 # 2005-01-19
 # Disabled: test_lambda - The Tru64 compiler can't build this when not using
diff --git a/tests/test_cpp11_lambda.cc b/tests/test_cpp11_lambda.cc
index ad0271d..463c69e 100644
--- a/tests/test_cpp11_lambda.cc
+++ b/tests/test_cpp11_lambda.cc
@@ -32,7 +32,8 @@
 // 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.
+// 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):
@@ -54,6 +55,7 @@
 #include <sigc++/functors/functors.h>
 #include <sigc++/bind.h>
 #include <sigc++/reference_wrapper.h>
+#include <sigc++/adaptors/track_obj.h>
 #include <sigc++/signal.h>
 
 #ifdef USING_CPP11_LAMBDA_EXPRESSIONS
@@ -270,20 +272,15 @@ int main(int argc, char* argv[])
 
   // auto-disconnect
   // Here's an area where the libsigc++ lambda expressions are advantageous.
-  // It can be more difficult to auto-disconnect slots without them.
+  // 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<void, std::ostringstream&> 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 = [&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());
+    book guest_book("karl");
+    //sl1 = (sigc::var(std::cout) << sigc::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");
 
@@ -327,7 +324,7 @@ int main(int argc, char* argv[])
     //sl2 = sigc::group(&egon, sigc::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::bind(&egon, sigc::ref(guest_book));
+    sl2 = sigc::track_obj([&guest_book] () { egon(guest_book); }, guest_book);
     sl2();
     util->check_result(result_stream, "egon(string 'karl')");
 
@@ -463,14 +460,15 @@ int main(int argc, char* argv[])
   }
 
   {
-    struct bar : public sigc::trackable {} some_bar;
+    //struct bar : public sigc::trackable {} some_bar;
     sigc::signal<void> 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([&some_bar](){ foo_group4(some_bar); }); // no auto-disconnect
-      some_signal.connect(sigc::bind(&foo_group4, sigc::ref(some_bar)));
+      //some_signal.connect(sigc::bind(&foo_group4, sigc::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&)");
     }
diff --git a/tests/test_track_obj.cc b/tests/test_track_obj.cc
new file mode 100644
index 0000000..5e8a824
--- /dev/null
+++ b/tests/test_track_obj.cc
@@ -0,0 +1,230 @@
+/* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
+ */
+
+// The purpose of this test case is threefold.
+// - Test sigc::track_obj().
+// - Show that a slot with a C++11 lambda expression can be automatically
+//   disconnected when an object derived from sigc::trackable is deleted,
+//   provided sigc::track_obj() is used.
+//   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 example in the documentation in sigc++/adaptors/track_obj.h.
+//
+// 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_track_obj
+//   ./test_track_obj
+//   echo $?
+// If test_track_obj 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 "testutilities.h"
+#include <string>
+#include <iostream>
+#include <sstream>
+#include <cstdlib>
+#include <sigc++/adaptors/track_obj.h>
+#include <sigc++/signal.h>
+
+#ifdef USING_CPP11_LAMBDA_EXPRESSIONS
+namespace sigc
+{
+  SIGC_FUNCTORS_DEDUCE_RESULT_TYPE_WITH_DECLTYPE
+}
+#endif
+
+namespace
+{
+std::ostringstream result_stream;
+
+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_; }
+  operator const std::string& () const { return name_; }
+  std::string name_;
+};
+
+inline std::ostringstream& operator << (std::ostringstream& s, const book& b)
+{
+  s << b.name_;
+  return s;
+}
+
+struct bar_group4 : public sigc::trackable
+{
+};
+
+class Functor1 : public sigc::functor_base
+{
+public:
+  typedef std::string result_type;
+
+  Functor1(const bar_group4& bar)
+  : bar_(bar) {}
+
+  std::string operator()(int i)
+  {
+    return (i<0) ? "negative" : ((i>0) ? "positive" : "zero");
+  }
+
+private:
+  const bar_group4& bar_;
+};
+
+class Functor2 : public sigc::functor_base
+{
+public:
+  typedef std::string result_type;
+
+  Functor2(const bar_group4& bar, const book& aBook)
+  : bar_(bar), aBook_(aBook) {}
+
+  std::string operator()(int i, const std::string& str) const
+  {
+    std::string result = (i<0) ? "negative, " : ((i>0) ? "positive, " : "zero, ");
+    result += str;
+    result += aBook_;
+    return result;
+  }
+
+private:
+  const bar_group4& bar_;
+  const book& aBook_;
+};
+
+void foo_group4(bar_group4&)
+{
+  result_stream << "foo_group4(bar_group4&)";
+}
+
+} // end anonymous namespace
+
+
+int main(int argc, char* argv[])
+{
+  TestUtilities* util = TestUtilities::get_instance();
+
+  if (!util->check_command_args(argc, argv))
+    return util->get_result_and_delete_instance() ? EXIT_SUCCESS : EXIT_FAILURE;
+
+  sigc::slot<std::string, int> sl1;
+  {
+    bar_group4 bar4;
+    sl1 = sigc::track_obj(Functor1(bar4), bar4);
+    result_stream << sl1(-2);
+    util->check_result(result_stream, "negative");
+
+  } // auto-disconnect sl1
+
+  result_stream << sl1(-2);
+  util->check_result(result_stream, "");
+
+  // Allocate on the heap. valgrind can then find erroneous memory accesses.
+  // (There should be none, of course.)
+  sigc::slot<std::string, int, std::string>* psl2 = new sigc::slot<std::string, int, std::string>;
+  bar_group4* pbar4 = new bar_group4;
+  book* pbook4 = new book("A Book");
+  *psl2 = sigc::track_obj(Functor2(*pbar4, *pbook4), *pbar4, *pbook4);
+  result_stream << (*psl2)(0, "Book title: ");
+  util->check_result(result_stream, "zero, Book title: A Book");
+
+  delete pbook4; // auto-disconnect *psl2
+  pbook4 = 0;
+  result_stream << (*psl2)(0, "Book title: ");
+  util->check_result(result_stream, "");
+  delete psl2;
+  psl2 = 0;
+  delete pbar4;
+  pbar4 = 0;
+
+#ifdef USING_CPP11_LAMBDA_EXPRESSIONS
+
+  // auto-disconnect
+  // 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<void, std::ostringstream&> sl10;
+  {
+    book guest_book("karl");
+    // sl1 = [&guest_book](std::ostringstream& stream){ stream << guest_book << "\n"; }; // no 
auto-disconnect
+    sl10 = sigc::track_obj([&guest_book](std::ostringstream& stream){ stream << guest_book; }, guest_book);
+    sl10(result_stream);
+    util->check_result(result_stream, "karl");
+
+  } // auto-disconnect sl10
+
+  sl10(result_stream);
+  util->check_result(result_stream, "");
+
+  // auto-disconnect
+  sigc::slot<void> sl20;
+  {
+    book guest_book("karl");
+    // sl2 = [&guest_book] () { egon(guest_book); }; // no auto-disconnect
+    // sl2 = std::bind(&egon, std::ref(guest_book)); // does not compile (gcc 4.6.3)
+    sl20 = sigc::track_obj([&guest_book] () { egon(guest_book); }, guest_book);
+    sl20();
+    util->check_result(result_stream, "egon(string 'karl')");
+
+    result_stream << static_cast<const std::string&>(guest_book);
+    util->check_result(result_stream, "egon was here");
+
+  } // auto-disconnect sl20
+
+  sl20();
+  util->check_result(result_stream, "");
+
+
+  // Code example in the documentation sigc++/adaptors/macros/track_obj.h.m4
+  // -----------------------------------------------------------------------
+  {
+    //struct bar : public sigc::trackable {} some_bar;
+    sigc::signal<void> 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([&some_bar](){ foo_group4(some_bar); }); // no auto-disconnect
+      //some_signal.connect(sigc::bind(&foo_group4, sigc::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&)");
+
+    } // auto-disconnect the lambda expression
+
+    some_signal.emit();
+    util->check_result(result_stream, "");
+  }
+
+#else // not USING_CPP11_LAMBDA_EXPRESSIONS
+  std::cout << "The compiler capabilities don't allow test of C++11 lambda expressions." << std::endl;
+#endif
+
+  return util->get_result_and_delete_instance() ? EXIT_SUCCESS : EXIT_FAILURE;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]