[libsigc++2/variadic] track_obj.h.m4: Make this variadic.



commit 4abd4d6a559671173a48eb4cbf6a1e5ca631cdc0
Author: Murray Cumming <murrayc murrayc com>
Date:   Thu Jan 14 21:32:10 2016 +0100

    track_obj.h.m4: Make this variadic.
    
    This uses a tuple_for_each_const<>() utility taken from here:
    https://github.com/murraycu/murrayc-tuple-utils/tree/master/tuple-utils
    for the visit_each() specialization.

 sigc++/adaptors/macros/track_obj.h.m4 |  165 +++++++++++++-------------------
 sigc++/filelist.am                    |    1 +
 sigc++/tuple_for_each_const.h         |   76 +++++++++++++++
 tests/Makefile.am                     |    2 +
 tests/test_tuple_for_each.cc          |  166 +++++++++++++++++++++++++++++++++
 5 files changed, 312 insertions(+), 98 deletions(-)
---
diff --git a/sigc++/adaptors/macros/track_obj.h.m4 b/sigc++/adaptors/macros/track_obj.h.m4
index b957eae..38a831f 100644
--- a/sigc++/adaptors/macros/track_obj.h.m4
+++ b/sigc++/adaptors/macros/track_obj.h.m4
@@ -19,94 +19,12 @@ divert(-1)
 
 include(template.macros.m4)
 
-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 visitor<>::do_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_functor, LOOP(typename T_obj%1, $1)>
-struct visitor<track_obj_functor$1<T_functor, LOOP(T_obj%1, $1)> >
-{
-  template <typename T_action>
-  static void do_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([ADAPTORS_TRACK_OBJ])
 #include <sigc++/adaptors/adaptor_trait.h>
 #include <sigc++/limit_reference.h>
+#include <sigc++/tuple_for_each_const.h>
 
 namespace sigc {
 
@@ -139,30 +57,30 @@ namespace sigc {
  * @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.
+/** track_obj_functor wraps a functor and stores a reference to a trackable object.
+ * Use the convenience function track_obj() to create an instance of track_obj_functor.
  *
  * @tparam T_functor The type of functor to wrap.
- * @tparam T_obj1 The type of a trackable object.
+ * @tparam T_obj The types of the trackable objects.
  *
  * @newin{2,4}
  *
  * @ingroup track_obj
  */
-template <typename T_functor, typename T_obj1>
-class track_obj_functor1 : public adapts<T_functor>
+template <typename T_functor, typename... T_obj>
+class track_obj_functor : public adapts<T_functor>
 {
 public:
   typedef typename adapts<T_functor>::adaptor_type adaptor_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.
+  /** Constructs a track_obj_functor object that wraps the passed functor and
+   * stores a reference to the passed trackable objects.
    * @param _A_func Functor.
-   * @param _A_obj1 Trackable object.
+   * @param _A_obj Trackable objects.
    */
-  track_obj_functor1(const T_functor& _A_func, const T_obj1& _A_obj1)
-  : adapts<T_functor>(_A_func), obj1_(_A_obj1) {}
+  track_obj_functor(const T_functor& _A_func, const T_obj&... _A_obj)
+  : adapts<T_functor>(_A_func), obj_(_A_obj...) {}
 
   /** Invokes the wrapped functor.
    * @return The return value of the functor invocation.
@@ -186,17 +104,68 @@ public:
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
 //protected:
   // public, so that visit_each() can access it.
-  const_limit_reference<T_obj1> obj1_;
+  std::tuple<const_limit_reference<T_obj>...> obj_;
 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
 
-}; // end class track_obj_functor1
+}; // end class track_obj_functor
 
-FOR(2,CALL_SIZE,[[TRACK_OBJECT_FUNCTOR(%1)]])dnl
 
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
-FOR(1,CALL_SIZE,[[TRACK_OBJECT_VISIT_EACH(%1)]])dnl
+//template specialization of visitor<>::do_visit_each<>(action, functor):
+/** Performs a functor on each of the targets of a functor.
+ * The function overload for sigc::track_obj_functor performs a functor
+ * on the functor and on the trackable object instances stored in the
+ * sigc::track_obj_functor object.
+ *
+ * @newin{2,4}
+ *
+ * @ingroup track_obj
+ */
+template <typename T_functor, typename... T_obj>
+struct visitor<track_obj_functor<T_functor, T_obj...>>
+{
+  template <typename T_action>
+  static void do_visit_each(const T_action& _A_action,
+                            const track_obj_functor<T_functor, T_obj...>& _A_target)
+  {
+    sigc::visit_each(_A_action, _A_target.functor_);
+
+    //Call sigc::visit_each(_A_action, element) on each element in the 
+    //_A_target.obj_ tuple:
+    sigc::tuple_for_each_const<TrackObjVisitForEach>(_A_target.obj_, _A_action);
+  }
+
+private:
+  template<typename T_element, typename T_action>
+  struct TrackObjVisitForEach
+  {
+    static
+    void
+    visit(const T_element& element, const T_action& action)
+    {
+       sigc::visit_each(action, element);
+    }
+  };
+};
 #endif // DOXYGEN_SHOULD_SKIP_THIS
 
-FOR(1,CALL_SIZE,[[TRACK_OBJECT(%1)]])dnl
+
+/** Creates an adaptor of type sigc::track_obj_functor which wraps a functor.
+ * @param _A_func Functor that shall be wrapped.
+ * @param _A_obj Trackable objects.
+ * @return Adaptor that executes _A_func() on invocation.
+ *
+ * @newin{2,4}
+ *
+ * @ingroup track_obj
+ */
+template <typename T_functor, typename... T_obj>
+inline decltype(auto)
+track_obj(const T_functor& _A_func, const T_obj&... _A_obj)
+{
+  return track_obj_functor<T_functor, T_obj...>
+    (_A_func, _A_obj...);
+}
+
 
 } /* namespace sigc */
diff --git a/sigc++/filelist.am b/sigc++/filelist.am
index 5aaea9e..267cc90 100644
--- a/sigc++/filelist.am
+++ b/sigc++/filelist.am
@@ -58,6 +58,7 @@ sigc_public_h =                               \
        tuple_cat.h \
        tuple_cdr.h \
        tuple_end.h \
+       tuple_for_each_const.h \
        tuple_start.h \
        type_traits.h                   \
        visit_each.h                    \
diff --git a/sigc++/tuple_for_each_const.h b/sigc++/tuple_for_each_const.h
new file mode 100644
index 0000000..cac485e
--- /dev/null
+++ b/sigc++/tuple_for_each_const.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 2016 Murray Cumming
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/
+ */
+
+#ifndef _SIGC_TUPLE_FOR_EACH_CONST_H_
+#define _SIGC_TUPLE_FOR_EACH_CONST_H_
+
+#include <tuple>
+
+namespace sigc
+{
+
+namespace {
+
+template<template<typename> class T_visitor, std::size_t index, typename... T_extras>
+struct tuple_for_each_const_impl
+{
+  template<typename T>
+  static
+  void
+  tuple_for_each_const(const T& t, T_extras... extras) {
+    using element_type = typename std::tuple_element<index, T>::type;
+    T_visitor<element_type>::visit(std::get<index>(t), extras...);
+
+    tuple_for_each_const_impl<T_visitor, index - 1, T_extras...>::tuple_for_each_const(t, extras...);
+  }
+};
+
+template<template<typename> class T_visitor, typename... T_extras>
+struct tuple_for_each_const_impl<T_visitor, 0, T_extras...>
+{
+  template<typename T>
+  static
+  void
+  tuple_for_each_const(const T& t, T_extras... extras) {
+    constexpr std::size_t index = 0;
+
+    using element_type = typename std::tuple_element<index, T>::type;
+    T_visitor<element_type>::visit(std::get<index>(t), extras...);
+  }
+};
+
+} //anonymous namespace
+
+/**
+ * Get a tuple with each element having the transformed value of the element
+ * in the original tuple.
+ *
+ * @tparam T_visitor should be a template that has a static visit() method.
+ * @tparam T the tuple type.
+ * @tparam T_extras the types of any extra arguments to pass to @e T_Visitor's visit() method.
+ * @param t The tuple whose elements should be visited.
+ * @param extras Any extra arguments to pass to @e T_Visitor's visit() method. 
+ */
+template<template<typename> class T_visitor, typename T, typename... T_extras>
+void
+tuple_for_each_const(const T& t, T_extras... extras) {
+  constexpr auto size = std::tuple_size<T>::value; 
+  tuple_for_each_const_impl<T_visitor, size - 1, T_extras...>::tuple_for_each_const(t, extras...);
+}
+
+} //namespace sigc
+
+#endif //_SIGC_TUPLE_FOR_EACH_CONST_H_
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 1061ca8..bbf641b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -52,6 +52,7 @@ check_PROGRAMS = \
   test_tuple_cat \
   test_tuple_cdr \
   test_tuple_end \
+  test_tuple_for_each \
   test_tuple_start \
   test_visit_each
 
@@ -91,5 +92,6 @@ test_track_obj_SOURCES       = test_track_obj.cc $(sigc_test_util)
 test_tuple_cat_SOURCES       = test_tuple_cat.cc $(sigc_test_util)
 test_tuple_cdr_SOURCES       = test_tuple_cdr.cc $(sigc_test_util)
 test_tuple_end_SOURCES       = test_tuple_end.cc $(sigc_test_util)
+test_tuple_for_each_SOURCES  = test_tuple_for_each.cc $(sigc_test_util)
 test_tuple_start_SOURCES     = test_tuple_start.cc $(sigc_test_util)
 test_visit_each_SOURCES      = test_visit_each.cc $(sigc_test_util)
diff --git a/tests/test_tuple_for_each.cc b/tests/test_tuple_for_each.cc
new file mode 100644
index 0000000..e2558d6
--- /dev/null
+++ b/tests/test_tuple_for_each.cc
@@ -0,0 +1,166 @@
+/* Copyright (C) 2016 Murray Cumming
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/
+ */
+
+//#include <sigc++/tuple_for_each.h>
+#include <sigc++/tuple_for_each_const.h>
+#include <utility>
+#include <cstdlib>
+#include <cassert>
+//#include <typeinfo>
+#include <iostream>
+
+template <class T_element_from>
+class for_each_simple
+{
+public:
+  static
+  void
+  visit(const T_element_from& from) {
+    std::cout << "for_each_simple(): " << std::to_string(from) << std::endl;
+  } 
+};
+
+void test_tuple_for_each_same_types()
+{
+  {
+    auto t_original = std::make_tuple(1, 2, 3);
+    sigc::tuple_for_each_const<for_each_simple>(t_original);
+  }
+
+  {
+    auto t_original = std::make_tuple(1, (double)2.1f, 3);
+    sigc::tuple_for_each_const<for_each_simple>(t_original);
+  }
+}
+
+template <class T_element_from>
+class for_each_simple_with_extras
+{
+public:
+  static
+  void
+  visit(const T_element_from& from, int extra1, const std::string& extra2) {
+    std::cout << "for_each_simple_with_extras(): from=" <<
+      std::to_string(from) <<
+      ", extra1: " << extra1 <<
+      ", extra2: " << extra2 << std::endl;
+  }
+};
+
+void test_tuple_for_each_same_types_with_extras()
+{
+  {
+    auto t_original = std::make_tuple(1, (double)2.1f, 3);
+    sigc::tuple_for_each_const<for_each_simple_with_extras>(t_original, 89, "eightynine");
+  }
+}
+
+template <class T_element_from>
+class for_each_simple_with_nonconst_extras
+{
+public:
+  static
+  void
+  visit(const T_element_from& from, int& extra) {
+    extra += (int)from;
+  }
+};
+
+void test_tuple_for_each_same_types_with_nonconst_extras()
+{
+  {
+    auto t_original = std::make_tuple(1, (double)2.1f, 3);
+    int extra = 0;
+
+    //TODO: avoid the need to specify the tuple type (decltype(t_original).
+    //  It can't be at the end (or can't it?) because we have T_extras... at the end.
+    //TODO: avoid the need to specify that the int should be passed by reference?
+    sigc::tuple_for_each_const<for_each_simple_with_nonconst_extras, decltype(t_original), int&>(t_original, 
extra);
+    //std::cout << "extra: " << extra << std::endl;
+    assert(extra == 6);
+  }
+}
+
+//The general template declaration.
+//We then provide specializations for each type,
+//so we can test having a different return value for each T_element_from type.
+template <class T_element_from>
+class visitor_with_specializations;
+
+//An int will be converted to a std::string:
+template <>
+class visitor_with_specializations<int>
+{
+public:
+  static
+  void
+  visit(const int& from) {
+    std::cout << "visitor_with_specializations::visit(): " << std::to_string(from) << std::endl;
+  } 
+};
+
+//A double will be converted to a char:
+template <>
+class visitor_with_specializations<double>
+{
+public:
+  static
+  void
+  visit(const double& from) {
+    std::cout << "visitor_with_specializations::visit(): " << std::to_string(from)[0] << std::endl;
+  } 
+};
+
+//A std::string will be converted to an int:
+template <>
+class visitor_with_specializations<std::string>
+{
+public:
+  static
+  void
+  visit(const std::string& from) {
+    std::cout << "visitor_with_specializations::visit(): " << std::stoi(from) << std::endl;
+  } 
+};
+
+void test_tuple_for_each_multiple_types()
+{
+  auto t_original = std::make_tuple(1, (double)2.1f, std::string("3"));
+  sigc::tuple_for_each_const<visitor_with_specializations>(t_original);
+}
+
+template <class T_element_from>
+class for_each_nonconst
+{
+public:
+  static
+  void
+  visit(T_element_from& from) {
+    from *= 2;
+    //Or, for instance, call a non-const method on from.
+  } 
+};
+
+int main()
+{
+  test_tuple_for_each_same_types();
+  test_tuple_for_each_same_types_with_extras();
+  test_tuple_for_each_same_types_with_nonconst_extras();
+
+  test_tuple_for_each_multiple_types();
+      
+  return EXIT_SUCCESS;
+}


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