[libsigcplusplus/variadic_mem_fun3: 66/148] Add tuple utils from murrayc-tuple-utils.



commit 6bf76ddbf248eec51eb7104600cadaf163c7c3fb
Author: Murray Cumming <murrayc murrayc com>
Date:   Wed Mar 2 10:46:01 2016 +0100

    Add tuple utils from murrayc-tuple-utils.

 sigc++/filelist.am                 |    6 +
 sigc++/tuple_cat.h                 |   54 +++++++
 sigc++/tuple_cdr.h                 |   68 +++++++++
 sigc++/tuple_end.h                 |   93 ++++++++++++
 sigc++/tuple_for_each.h            |  103 +++++++++++++
 sigc++/tuple_start.h               |   80 ++++++++++
 sigc++/tuple_transform_each.h      |  157 ++++++++++++++++++++
 tests/.gitignore                   |    4 +
 tests/Makefile.am                  |   10 ++
 tests/test_tuple_cat.cc            |   71 +++++++++
 tests/test_tuple_cdr.cc            |   76 ++++++++++
 tests/test_tuple_end.cc            |  132 +++++++++++++++++
 tests/test_tuple_for_each.cc       |  219 ++++++++++++++++++++++++++++
 tests/test_tuple_start.cc          |  123 ++++++++++++++++
 tests/test_tuple_transform_each.cc |  278 ++++++++++++++++++++++++++++++++++++
 15 files changed, 1474 insertions(+), 0 deletions(-)
---
diff --git a/sigc++/filelist.am b/sigc++/filelist.am
index 3c57660..49bbb55 100644
--- a/sigc++/filelist.am
+++ b/sigc++/filelist.am
@@ -55,6 +55,12 @@ sigc_public_h =                              \
        signal_base.h                   \
        slot.h                  \
        trackable.h                     \
+       tuple_cat.h \
+       tuple_cdr.h \
+       tuple_end.h \
+       tuple_for_each.h \
+       tuple_start.h \
+       tuple_transform_each.h \
        type_traits.h                   \
        visit_each.h                    \
        adaptors/adaptor_base.h \
diff --git a/sigc++/tuple_cat.h b/sigc++/tuple_cat.h
new file mode 100644
index 0000000..8cb8d25
--- /dev/null
+++ b/sigc++/tuple_cat.h
@@ -0,0 +1,54 @@
+/* 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_UTILS_TUPLE_CAT_H
+#define SIGC_TUPLE_UTILS_TUPLE_CAT_H
+
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+namespace sigc {
+
+namespace detail {
+
+template <typename T1, typename T2, typename Seq1, typename Seq2>
+struct tuple_type_cat_impl;
+
+template <typename T1, typename T2, std::size_t... I1, std::size_t... I2>
+struct tuple_type_cat_impl<T1, T2, std::index_sequence<I1...>,
+  std::index_sequence<I2...>> {
+  using type = std::tuple<typename std::tuple_element<I1, T1>::type...,
+    typename std::tuple_element<I2, T2>::type...>;
+};
+
+} // detail namespace
+
+/**
+ * Get the type of a tuple without the first item.
+ */
+template <typename T1, typename T2>
+struct tuple_type_cat
+  : detail::tuple_type_cat_impl<T1, T2,
+      std::make_index_sequence<std::tuple_size<T1>::value>,
+      std::make_index_sequence<std::tuple_size<T2>::value>> {};
+
+// There is no tuple_cat() here because std::tuple_cat() exists already in
+// the C++ standard library.
+
+} // namespace sigc
+
+#endif //SIGC_TUPLE_UTILS_TUPLE_CAT_H
diff --git a/sigc++/tuple_cdr.h b/sigc++/tuple_cdr.h
new file mode 100644
index 0000000..580296b
--- /dev/null
+++ b/sigc++/tuple_cdr.h
@@ -0,0 +1,68 @@
+/* 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_UTILS_TUPLE_CDR_H
+#define SIGC_TUPLE_UTILS_TUPLE_CDR_H
+
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+namespace sigc {
+
+/**
+ * Get the type of a tuple without the first item.
+ */
+template <typename T>
+struct tuple_type_cdr; // primary template is not defined
+
+// Partial specialization for tuples of at least one element:
+template <typename H, typename... T>
+struct tuple_type_cdr<std::tuple<H, T...>>
+{
+  using type = std::tuple<T...>;
+};
+
+namespace detail {
+
+template <typename T, std::size_t... I>
+decltype(auto)
+tuple_cdr_impl(T&& t, std::index_sequence<0, I...>)
+{
+  using cdr = typename tuple_type_cdr<std::decay_t<T>>::type;
+  return cdr(std::get<I>(std::forward<T>(t))...);
+}
+
+} // detail namespace
+
+/**
+ * Get the a tuple without the first item.
+ * This is analogous to std::tuple_cat().
+ */
+template <typename T>
+decltype(auto)
+tuple_cdr(T&& t) {
+  //We use std::decay_t<> because tuple_size is not defined for references.
+  constexpr auto size = std::tuple_size<std::decay_t<T>>::value;
+
+  static_assert(size != 0, "tuple size must be non-zero");
+  using seq = std::make_index_sequence<size>;
+  return detail::tuple_cdr_impl(std::forward<T>(t), seq{});
+}
+
+} // namespace sigc
+
+#endif //SIGC_TUPLE_UTILS_TUPLE_CDR_H
diff --git a/sigc++/tuple_end.h b/sigc++/tuple_end.h
new file mode 100644
index 0000000..d36afdd
--- /dev/null
+++ b/sigc++/tuple_end.h
@@ -0,0 +1,93 @@
+/* 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_UTILS_TUPLE_END_H
+#define SIGC_TUPLE_UTILS_TUPLE_END_H
+
+#include <sigc++/tuple_cdr.h>
+
+namespace sigc {
+
+namespace detail {
+
+template <typename T, std::size_t remove_from_start>
+struct tuple_type_end_impl {
+  using type = typename tuple_type_end_impl<typename tuple_type_cdr<std::decay_t<T>>::type,
+    remove_from_start - 1>::type;
+};
+
+template <typename T>
+struct tuple_type_end_impl<T, 0> {
+  using type = T;
+};
+
+} // detail namespace
+
+/**
+ * Get the type of a tuple with the last @a len types of the original.
+ */
+template <typename T, std::size_t len>
+struct tuple_type_end
+  : detail::tuple_type_end_impl<T, std::tuple_size<T>::value - len> {};
+
+namespace detail {
+
+template <typename T, std::size_t remove_from_start>
+struct tuple_end_impl {
+  static decltype(auto) // typename tuple_type_end<T, size - remove_from_start>::type
+  tuple_end(T&& t) {
+    static_assert(remove_from_start > 0, "remove_from_start must be more than zero.");
+
+    using cdr = typename tuple_type_cdr<std::decay_t<T>>::type;
+    return tuple_end_impl<cdr, remove_from_start - 1>::tuple_end(
+      tuple_cdr(std::forward<T>(t)));
+  }
+};
+
+template <typename T>
+struct tuple_end_impl<T, 1> {
+  static decltype(auto)
+  tuple_end(T&& t) {
+    return tuple_cdr(std::forward<T>(t));
+  }
+};
+
+template <typename T>
+struct tuple_end_impl<T, 0> {
+  static decltype(auto)
+  tuple_end(T&& t) {
+    return std::forward<T>(t);
+  }
+};
+
+} // detail namespace
+
+/**
+ * Get the tuple with the last @a len items of the original.
+ */
+template <std::size_t len, typename T>
+decltype(auto) // typename tuple_type_end<T, len>::type
+  tuple_end(T&& t) {
+  //We use std::decay_t<> because tuple_size is not defined for references.
+  constexpr auto size = std::tuple_size<std::decay_t<T>>::value;
+  static_assert(len <= size, "The tuple size must be less than or equal to the length.");
+  constexpr auto size_start = size - len;
+  return detail::tuple_end_impl<T, size_start>::tuple_end(std::forward<T>(t));
+}
+
+} // namespace sigc;
+
+#endif //SIGC_TUPLE_UTILS_TUPLE_END_H
diff --git a/sigc++/tuple_for_each.h b/sigc++/tuple_for_each.h
new file mode 100644
index 0000000..1bf98bd
--- /dev/null
+++ b/sigc++/tuple_for_each.h
@@ -0,0 +1,103 @@
+/* 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_UTILS_TUPLE_FOR_EACH_H
+#define SIGC_TUPLE_UTILS_TUPLE_FOR_EACH_H
+
+#include <tuple>
+
+namespace sigc {
+
+namespace detail {
+
+template <template <typename> class T_visitor, std::size_t size_from_index,
+  typename... T_extras>
+struct tuple_for_each_impl {
+  template <typename T>
+  static void
+  tuple_for_each(T&& t, T_extras&&... extras) {
+    //We use std::decay_t<> because tuple_size is not defined for references.
+    constexpr auto size = std::tuple_size<std::decay_t<T>>::value;
+    static_assert(size > 1, "size must be more than 0.");
+
+    constexpr auto index = size - size_from_index;
+    static_assert(index >= 0, "unexpected index.");
+
+    using element_type = typename std::tuple_element<index, std::decay_t<T>>::type;
+    T_visitor<element_type>::visit(std::get<index>(t), std::forward<T_extras>(extras)...);
+
+    tuple_for_each_impl<T_visitor, size_from_index - 1, T_extras...>::tuple_for_each(
+      std::forward<T>(t), std::forward<T_extras>(extras)...);
+  }
+};
+
+template <template <typename> class T_visitor, typename... T_extras>
+struct tuple_for_each_impl<T_visitor, 1, T_extras...> {
+  template <typename T>
+  static void
+  tuple_for_each(T&& t, T_extras&&... extras) {
+    //We use std::decay_t<> because tuple_size is not defined for references.
+    constexpr auto size = std::tuple_size<std::decay_t<T>>::value;
+    static_assert(size > 0, "size must be more than 0.");
+
+    constexpr auto index = size - 1;
+    static_assert(index >= 0, "unexpected index.");
+
+    using element_type = typename std::tuple_element<index, std::decay_t<T>>::type;
+    T_visitor<element_type>::visit(std::get<index>(std::forward<T>(t)), std::forward<T_extras>(extras)...);
+  }
+};
+
+template <template <typename> class T_visitor, typename... T_extras>
+struct tuple_for_each_impl<T_visitor, 0, T_extras...> {
+  template <typename T>
+  static void
+  tuple_for_each(T&& /* t */, T_extras&&... /* extras */) {
+    //Do nothing because the tuple has no elements.
+  }
+};
+
+} // detail namespace
+
+
+/**
+ * Call the @e T_Visitor functors visit() method for each element,
+ * from the first to the last.
+ *
+ * @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(T&& t, T_extras&&... extras) {
+  //We use std::decay_t<> because tuple_size is not defined for references.
+  constexpr auto size = std::tuple_size<std::decay_t<T>>::value;
+
+  if(size == 0) {
+    return;
+  }
+
+  detail::tuple_for_each_impl<T_visitor, size, T_extras...>::tuple_for_each(
+    std::forward<T>(t), std::forward<T_extras>(extras)...);
+}
+
+} // namespace sigc
+
+#endif //SIGC_TUPLE_UTILS_TUPLE_FOR_EACH_H
diff --git a/sigc++/tuple_start.h b/sigc++/tuple_start.h
new file mode 100644
index 0000000..95fda2e
--- /dev/null
+++ b/sigc++/tuple_start.h
@@ -0,0 +1,80 @@
+/* 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_UTILS_TUPLE_START_H
+#define SIGC_TUPLE_UTILS_TUPLE_START_H
+
+#include <tuple>
+#include <utility>
+
+namespace sigc {
+
+namespace detail {
+
+template <typename T, typename Seq>
+struct tuple_type_start_impl;
+
+template <typename T, std::size_t... I>
+struct tuple_type_start_impl<T, std::index_sequence<I...>> {
+  using type = std::tuple<typename std::tuple_element<I, T>::type...>;
+};
+
+} // detail namespace
+
+/**
+ * Get the type of a tuple with just the first @len items.
+ */
+template <typename T, std::size_t len>
+struct tuple_type_start
+  : detail::tuple_type_start_impl<T, std::make_index_sequence<len>> {};
+
+namespace detail {
+
+template <typename T, typename Seq>
+struct tuple_start_impl;
+
+template <typename T, std::size_t... I>
+struct tuple_start_impl<T, std::index_sequence<I...>> {
+  static decltype(auto)
+  tuple_start(T&& t) {
+    constexpr auto size = std::tuple_size<std::decay_t<T>>::value;
+    constexpr auto len = sizeof...(I);
+    static_assert(len <= size, "The tuple size must be less than or equal to the length.");
+
+    using start = typename tuple_type_start<std::decay_t<T>, len>::type;
+    return start(std::get<I>(std::forward<T>(t))...);
+  }
+};
+
+} // detail namespace
+
+/**
+ * Get the tuple with the last @a len items of the original.
+ */
+template <std::size_t len, typename T>
+decltype(auto) // typename tuple_type_end<T, len>::type
+  tuple_start(T&& t) {
+  //We use std::decay_t<> because tuple_size is not defined for references.
+  constexpr auto size = std::tuple_size<std::decay_t<T>>::value;
+  static_assert(len <= size, "The tuple size must be less than or equal to the length.");
+
+  return detail::tuple_start_impl<T, std::make_index_sequence<len>>::tuple_start(
+    std::forward<T>(t));
+}
+
+} // namespace sigc;
+
+#endif //SIGC_TUPLE_UTILS_TUPLE_START_H
diff --git a/sigc++/tuple_transform_each.h b/sigc++/tuple_transform_each.h
new file mode 100644
index 0000000..ec95aab
--- /dev/null
+++ b/sigc++/tuple_transform_each.h
@@ -0,0 +1,157 @@
+/* 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_UTILS_TUPLE_TRANSFORM_EACH_H
+#define SIGC_TUPLE_UTILS_TUPLE_TRANSFORM_EACH_H
+
+#include <sigc++/tuple_cat.h>
+#include <sigc++/tuple_cdr.h>
+#include <sigc++/tuple_end.h>
+#include <sigc++/tuple_start.h>
+#include <type_traits>
+
+namespace sigc {
+
+namespace detail {
+
+template <typename T, template <typename> class T_transformer,
+  std::size_t index>
+struct tuple_type_transform_each_impl {
+private:
+  using from_element_type = typename std::tuple_element<index, T>::type;
+
+  using to_element_type = typename std::result_of<decltype (
+    &T_transformer<from_element_type>::transform)(from_element_type&)>::type;
+
+  using t_element_type = std::tuple<to_element_type>;
+
+  using t_type_start = typename tuple_type_start<T, index>::type;
+
+  using t_type_end =
+    typename tuple_type_end<T, std::tuple_size<T>::value - index - 1>::type;
+
+  using t_type_with_transformed_element = typename tuple_type_cat<
+    typename tuple_type_cat<t_type_start, t_element_type>::type,
+    t_type_end>::type;
+
+public:
+  using type =
+    typename tuple_type_transform_each_impl<t_type_with_transformed_element,
+      T_transformer, index - 1>::type;
+};
+
+template <typename T, template <typename> class T_transformer>
+struct tuple_type_transform_each_impl<T, T_transformer, 0> {
+private:
+  static constexpr std::size_t index = 0;
+  using from_element_type = typename std::tuple_element<index, T>::type;
+  using to_element_type = typename std::result_of<decltype (
+    &T_transformer<from_element_type>::transform)(from_element_type&)>::type;
+  using t_element_type = std::tuple<to_element_type>;
+
+  using t_type_end =
+    typename tuple_type_end<T, std::tuple_size<std::decay_t<T>>::value - index - 1>::type;
+
+  using t_type_with_transformed_element =
+    typename tuple_type_cat<t_element_type, t_type_end>::type;
+
+public:
+  using type = t_type_with_transformed_element;
+};
+
+} // detail namespace
+
+/**
+ * Get a tuple with each element having the transformed value of the element
+ * in the original tuple.
+ */
+
+template <typename T, template <typename> class T_transformer>
+struct tuple_type_transform_each {
+  using type = typename detail::tuple_type_transform_each_impl<T, T_transformer,
+    std::tuple_size<T>::value - 1>::type;
+};
+
+namespace detail {
+
+template <template <typename> class T_transformer, std::size_t index>
+struct tuple_transform_each_impl {
+  // TODO: Avoid the need to pass t_original all the way into the recursion?
+  template <typename T_current, typename T_original>
+  static decltype(auto)
+  tuple_transform_each(T_current&& t, T_original& t_original) {
+    using from_element_type = typename std::tuple_element<index, std::decay_t<T_current>>::type;
+    using to_element_type = typename std::result_of<decltype (
+      &T_transformer<from_element_type>::transform)(from_element_type&)>::type;
+    const auto t_element =
+      std::tuple<to_element_type>(T_transformer<from_element_type>::transform(std::get<index>(t_original)));
+
+    const auto t_start = tuple_start<index>(std::forward<T_current>(t));
+
+    constexpr auto size = std::tuple_size<std::decay_t<T_current>>::value;
+
+    // t_end's elements will be copies of the elements in t, so this method's
+    // caller won't see the changes made in the subsequent call of
+    // tuple_transform_each() on those copies. That's why we pass t_original
+    // through too, so we can modify that directly.
+    // the const version (tuple_transform_each_const()) doesn't have to worry
+    // about this, though avoiding copying would be more efficient.
+    const auto t_end = tuple_end<size - index - 1>(t);
+
+    auto t_with_transformed_element = std::tuple_cat(t_start, t_element, t_end);
+    return tuple_transform_each_impl<T_transformer,
+      index - 1>::tuple_transform_each(t_with_transformed_element, t_original);
+  }
+};
+
+template <template <typename> class T_transformer>
+struct tuple_transform_each_impl<T_transformer, 0> {
+  template <typename T_current, typename T_original>
+  static decltype(auto)
+  tuple_transform_each(T_current&& t, T_original& t_original) {
+    constexpr std::size_t index = 0;
+
+    using from_element_type = typename std::tuple_element<index, T_original>::type;
+    using to_element_type = typename std::result_of<decltype (
+      &T_transformer<from_element_type>::transform)(from_element_type&)>::type;
+    const auto tuple_element =
+      std::tuple<to_element_type>(T_transformer<from_element_type>::transform(std::get<index>(t_original)));
+
+    const auto tuple_rest = tuple_cdr(std::forward<T_current>(t));
+    return std::tuple_cat(tuple_element, tuple_rest);
+  }
+};
+
+} // detail namespace
+
+/**
+ * Get a tuple with each element having the transformed value of the element
+ * in the original tuple.
+ */
+template <template <typename> class T_transformer, typename T>
+decltype(auto)
+tuple_transform_each(T&& t) {
+  //We use std::decay_t<> because tuple_size is not defined for references.
+  constexpr auto size = std::tuple_size<std::decay_t<T>>::value;
+  static_assert(size > 0, "The tuple size must be more than zero.");
+
+  return detail::tuple_transform_each_impl<T_transformer,
+    size - 1>::tuple_transform_each(std::forward<T>(t), t);
+}
+
+} // namespace sigc
+
+#endif //SIGC_TUPLE_UTILS_TUPLE_TRANSFORM_EACH_H
diff --git a/tests/.gitignore b/tests/.gitignore
index be803fc..ff35316 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -30,4 +30,8 @@
 /test_trackable
 /test_trackable_move
 /test_track_obj
+/test_tuple_cat
+/test_tuple_cdr
+/test_tuple_end
+/test_tuple_start
 /test_visit_each
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a429c40..d00e1be 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -50,6 +50,11 @@ check_PROGRAMS = \
   test_trackable \
   test_trackable_move \
   test_track_obj \
+  test_tuple_cat \
+  test_tuple_cdr \
+  test_tuple_end \
+  test_tuple_start \
+  test_tuple_transform_each \
   test_visit_each
 
 TESTS = $(check_PROGRAMS)
@@ -86,4 +91,9 @@ test_slot_move_SOURCES       = test_slot_move.cc $(sigc_test_util)
 test_trackable_SOURCES       = test_trackable.cc $(sigc_test_util)
 test_trackable_move_SOURCES  = test_trackable_move.cc $(sigc_test_util)
 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_start_SOURCES     = test_tuple_start.cc $(sigc_test_util)
+test_tuple_transform_each_SOURCES = test_tuple_transform_each.cc $(sigc_test_util)
 test_visit_each_SOURCES      = test_visit_each.cc $(sigc_test_util)
diff --git a/tests/test_tuple_cat.cc b/tests/test_tuple_cat.cc
new file mode 100644
index 0000000..d266483
--- /dev/null
+++ b/tests/test_tuple_cat.cc
@@ -0,0 +1,71 @@
+/* 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 <cassert>
+#include <cstdlib>
+#include <sigc++/tuple_cat.h>
+#include <utility>
+//#include <functional>
+
+void
+test_tuple_type_cat() {
+  using type_tuple_is = std::tuple<int, short>;
+  using type_tuple_dc = std::tuple<double, char>;
+  using type_tuple_cat =
+    sigc::tuple_type_cat<type_tuple_is, type_tuple_dc>::type;
+  using type_tuple_expected = std::tuple<int, short, double, char>;
+
+  static_assert(std::tuple_size<type_tuple_cat>::value == 4,
+    "unexpected tuple_cat()ed tuple size.");
+  static_assert(std::is_same<type_tuple_cat, type_tuple_expected>::value,
+    "unexpected tuple_cat()ed tuple type");
+}
+
+/** We don't want to test std::tuple_cat() here,
+ * but this a demonstration that std::ref() works with std::tuple_cat().
+void
+test_tuple_cat_stdref() {
+  std::string a = "yadda1";
+  std::string b = "yaddayadda1";
+  auto t_one =
+    std::make_tuple(std::ref(a), std::ref(b));
+  int c = 2;
+  char d = 'a';
+  auto t_two =
+    std::make_tuple(std::ref(c), std::ref(d));
+  auto t_both = std::tuple_cat(t_one, t_two);
+  a = "hello";
+  b = "world";
+  c = 3;
+  d = 'b';
+
+  assert(std::get<0>(t_both) == "hello");
+  assert(std::get<1>(t_both) == "world");
+  assert(std::get<2>(t_both) == 3);
+  assert(std::get<3>(t_both) == 'b');
+}
+*/
+
+int
+main() {
+  test_tuple_type_cat();
+  // There is no typeutils::tuple_cat() because std::tuple_cat() exists:
+  // test_tuple_cat();
+
+  //test_tuple_cat_stdref();
+
+  return EXIT_SUCCESS;
+}
diff --git a/tests/test_tuple_cdr.cc b/tests/test_tuple_cdr.cc
new file mode 100644
index 0000000..b0d74f3
--- /dev/null
+++ b/tests/test_tuple_cdr.cc
@@ -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/
+ */
+
+#include <cassert>
+#include <cstdlib>
+#include <sigc++/tuple_cdr.h>
+#include <utility>
+#include <functional>
+
+void
+test_tuple_type_cdr() {
+  using type_tuple_isd = std::tuple<int, short, double>;
+  using type_tuple_sd = std::tuple<short, double>;
+  using type_tuple_suffix = sigc::tuple_type_cdr<type_tuple_isd>::type;
+
+  static_assert(std::tuple_size<type_tuple_suffix>::value == 2,
+    "unexpected tuple_cdr()ed tuple size.");
+  static_assert(std::is_same<type_tuple_suffix, type_tuple_sd>::value,
+    "unexpected tuple_cdr()ed tuple type");
+}
+
+void
+test_tuple_cdr() {
+  auto t_larger =
+    std::make_tuple(nullptr, std::string("hello"), std::string("world"));
+  auto t_suffix = sigc::tuple_cdr(t_larger);
+  assert(std::get<0>(t_suffix) == "hello");
+  assert(std::get<1>(t_suffix) == "world");
+
+  using type_tuple_suffix = std::tuple<std::string, std::string>;
+
+  static_assert(std::tuple_size<decltype(t_suffix)>::value == 2,
+    "unexpected cdr()ed tuple size.");
+  static_assert(std::is_same<decltype(t_suffix), type_tuple_suffix>::value,
+    "unexpected cdr()ed tuple type");
+}
+
+void
+test_tuple_cdr_stdref() {
+  std::string b = "yadda";
+  std::string c = "yaddayadda";
+  auto t_larger = std::make_tuple(1, std::ref(b), std::ref(c));
+
+  //std::cout << "debug: " << type(std::get<1>(t_larger)) << std::endl;
+
+  auto t_suffix = sigc::tuple_cdr(t_larger);
+  b = "hello";
+  c = "world";
+  //This works, but it's not what we are testing here:
+  //assert(std::get<1>(t_larger) == "hello");
+
+  assert(std::get<0>(t_suffix) == "hello");
+  assert(std::get<1>(t_suffix) == "world");
+}
+
+int
+main() {
+  test_tuple_type_cdr();
+  test_tuple_cdr();
+  test_tuple_cdr_stdref();
+
+  return EXIT_SUCCESS;
+}
diff --git a/tests/test_tuple_end.cc b/tests/test_tuple_end.cc
new file mode 100644
index 0000000..ad4b063
--- /dev/null
+++ b/tests/test_tuple_end.cc
@@ -0,0 +1,132 @@
+/* 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 <cassert>
+#include <cstdlib>
+#include <sigc++/tuple_end.h>
+#include <functional>
+
+void
+test_tuple_type_end() {
+  {
+    using type_tuple = std::tuple<int, short, double>;
+    using type_tuple_end = sigc::tuple_type_end<type_tuple, 1>::type;
+    using type_tuple_expected = std::tuple<double>;
+
+    static_assert(std::tuple_size<type_tuple_end>::value == 1,
+      "unexpected tuple_end()ed tuple size.");
+    static_assert(std::is_same<type_tuple_end, type_tuple_expected>::value,
+      "unexpected type_tuple_end type");
+  }
+
+  {
+    using type_tuple = std::tuple<int, short, double>;
+    using type_tuple_end = sigc::tuple_type_end<type_tuple, 2>::type;
+    using type_tuple_expected = std::tuple<short, double>;
+
+    static_assert(std::tuple_size<type_tuple_end>::value == 2,
+      "unexpected tuple_end()ed tuple size.");
+    static_assert(std::is_same<type_tuple_end, type_tuple_expected>::value,
+      "unexpected type_tuple_end type");
+  }
+
+  {
+    using type_tuple = std::tuple<int, short, double>;
+    using type_tuple_end = sigc::tuple_type_end<type_tuple, 3>::type;
+    using type_tuple_expected = std::tuple<int, short, double>;
+
+    static_assert(std::tuple_size<type_tuple_end>::value == 3,
+      "unexpected tuple_end()ed tuple size.");
+    static_assert(std::is_same<type_tuple_end, type_tuple_expected>::value,
+      "unexpected type_tuple_end type");
+  }
+}
+
+void
+test_tuple_end() {
+  {
+    auto t_original =
+      std::make_tuple(nullptr, std::string("hello"), std::string("world"));
+    auto t_suffix = sigc::tuple_end<3>(t_original);
+
+    static_assert(std::tuple_size<decltype(t_suffix)>::value == 3,
+      "unexpected tuple_end()ed tuple size.");
+
+    assert(std::get<0>(t_suffix) == nullptr);
+    assert(std::get<1>(t_suffix) == "hello");
+    assert(std::get<2>(t_suffix) == "world");
+
+    static_assert(std::is_same<decltype(t_suffix), decltype(t_original)>::value,
+      "unexpected end()ed tuple type");
+  }
+
+  {
+    auto t_original =
+      std::make_tuple(nullptr, std::string("hello"), std::string("world"));
+    auto t_suffix = sigc::tuple_end<2>(t_original);
+
+    static_assert(std::tuple_size<decltype(t_suffix)>::value == 2,
+      "unexpected tuple_end()ed tuple size.");
+
+    assert(std::get<0>(t_suffix) == "hello");
+    assert(std::get<1>(t_suffix) == "world");
+
+    using type_tuple_suffix = std::tuple<std::string, std::string>;
+    static_assert(std::is_same<decltype(t_suffix), type_tuple_suffix>::value,
+      "unexpected end()ed tuple type");
+  }
+
+  {
+    auto t_original =
+      std::make_tuple(nullptr, std::string("hello"), std::string("world"));
+    auto t_suffix = sigc::tuple_end<1>(t_original);
+
+    static_assert(std::tuple_size<decltype(t_suffix)>::value == 1,
+      "unexpected tuple_end()ed tuple size.");
+
+    assert(std::get<0>(t_suffix) == "world");
+
+    using type_tuple_suffix = std::tuple<std::string>;
+    static_assert(std::is_same<decltype(t_suffix), type_tuple_suffix>::value,
+      "unexpected end()ed tuple type");
+  }
+}
+
+void
+test_tuple_end_stdref() {
+  std::string c = "yadda";
+  std::string d = "yaddayadda";
+  auto t_larger = std::make_tuple(1, 2, std::ref(c), std::ref(d));
+
+  auto t_suffix = sigc::tuple_end<2>(t_larger);
+  c = "hello";
+  d = "world";
+  //This works, but it's not what we are testing here:
+  //assert(std::get<0>(t_larger) == "hello");
+
+  assert(std::get<0>(t_suffix) == "hello");
+  assert(std::get<1>(t_suffix) == "world");
+}
+
+
+int
+main() {
+  test_tuple_type_end();
+  test_tuple_end();
+  test_tuple_end_stdref();
+
+  return EXIT_SUCCESS;
+}
diff --git a/tests/test_tuple_for_each.cc b/tests/test_tuple_for_each.cc
new file mode 100644
index 0000000..b621c57
--- /dev/null
+++ b/tests/test_tuple_for_each.cc
@@ -0,0 +1,219 @@
+/* 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 <cassert>
+#include <cstdlib>
+#include <sigc++/tuple_for_each.h>
+#include <utility>
+//#include <typeinfo>
+#include <iostream>
+#include <functional>
+
+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<for_each_simple>(t_original);
+  }
+
+  {
+    auto t_original = std::make_tuple(1, (double)2.1f, 3);
+    sigc::tuple_for_each<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<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;
+
+    sigc::tuple_for_each<for_each_simple_with_nonconst_extras>(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<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.
+  }
+};
+
+void
+test_tuple_for_each_nonconst() {
+  auto t = std::make_tuple(1, 2, 3);
+  sigc::tuple_for_each<for_each_nonconst, decltype(t)&>(t);
+  std::cout << std::get<0>(t) << std::endl;
+  assert(std::get<0>(t) == 2);
+  assert(std::get<1>(t) == 4);
+  assert(std::get<2>(t) == 6);
+}
+
+void
+test_tuple_for_each_stdref() {
+  {
+    int a = 1;
+    int b = 2;
+    int c = 3;
+    auto t_original = std::make_tuple(std::ref(a), std::ref(b), std::ref(c));
+    sigc::tuple_for_each<for_each_simple>(t_original);
+  }
+
+  {
+    int a = 1;
+    int b = 2;
+    int c = 3;
+    auto t_original = std::make_tuple(std::ref(a), std::ref(b), std::ref(c));
+    sigc::tuple_for_each<for_each_nonconst>(t_original);
+    assert(a == 2);
+    assert(b == 4);
+    assert(c == 6);
+  }
+}
+
+static std::string correct_sequence_output;
+
+template <class T_element_from>
+class for_each_correct_sequence {
+public:
+  static void
+  visit(const T_element_from& from) {
+    //std::cout << "from: " << from << std::endl;
+    correct_sequence_output += std::to_string(from);
+  }
+};
+
+void
+test_tuple_for_each_correct_sequence() {
+  correct_sequence_output.clear();
+  auto t = std::make_tuple(1, 2, 3);
+  sigc::tuple_for_each<for_each_correct_sequence>(t);
+  //std::cout << "correct_sequence_output: " << correct_sequence_output << std::endl;
+  assert(correct_sequence_output == "123");
+}
+
+void
+test_tuple_for_each_empty_tuple() {
+  auto t = std::tuple<>();
+  sigc::tuple_for_each<for_each_simple>(t);
+}
+
+
+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();
+
+  test_tuple_for_each_nonconst();
+  
+  test_tuple_for_each_stdref();
+
+  test_tuple_for_each_correct_sequence();
+
+  test_tuple_for_each_empty_tuple();
+
+  return EXIT_SUCCESS;
+}
diff --git a/tests/test_tuple_start.cc b/tests/test_tuple_start.cc
new file mode 100644
index 0000000..00e4f30
--- /dev/null
+++ b/tests/test_tuple_start.cc
@@ -0,0 +1,123 @@
+/* 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 <cassert>
+#include <cstdlib>
+#include <sigc++/tuple_start.h>
+#include <functional>
+
+void
+test_tuple_type_start() {
+  {
+    using type_tuple = std::tuple<int, short, double>;
+    using type_tuple_start = sigc::tuple_type_start<type_tuple, 1>::type;
+    using type_tuple_expected = std::tuple<int>;
+
+    static_assert(std::is_same<type_tuple_start, type_tuple_expected>::value,
+      "unexpected type_tuple_start type");
+  }
+
+  {
+    using type_tuple = std::tuple<int, short, double>;
+    using type_tuple_start = sigc::tuple_type_start<type_tuple, 2>::type;
+    using type_tuple_expected = std::tuple<int, short>;
+
+    static_assert(std::is_same<type_tuple_start, type_tuple_expected>::value,
+      "unexpected type_tuple_start type");
+  }
+
+  {
+    using type_tuple = std::tuple<int, short, double>;
+    using type_tuple_start = sigc::tuple_type_start<type_tuple, 3>::type;
+    using type_tuple_expected = std::tuple<int, short, double>;
+
+    static_assert(std::is_same<type_tuple_start, type_tuple_expected>::value,
+      "unexpected type_tuple_start type");
+  }
+}
+
+void
+test_tuple_start() {
+  {
+    auto t_original =
+      std::make_tuple(nullptr, std::string("hello"), std::string("world"));
+    auto t_prefix = sigc::tuple_start<3>(t_original);
+
+    static_assert(std::tuple_size<decltype(t_prefix)>::value == 3,
+      "unexpected tuple_start()ed tuple size.");
+
+    assert(std::get<0>(t_prefix) == nullptr);
+    assert(std::get<1>(t_prefix) == "hello");
+    assert(std::get<2>(t_prefix) == "world");
+
+    static_assert(std::is_same<decltype(t_prefix), decltype(t_original)>::value,
+      "unexpected start()ed tuple type");
+  }
+
+  {
+    auto t_original =
+      std::make_tuple(nullptr, std::string("hello"), std::string("world"));
+    auto t_prefix = sigc::tuple_start<2>(t_original);
+
+    static_assert(std::tuple_size<decltype(t_prefix)>::value == 2,
+      "unexpected tuple_start()ed tuple size.");
+
+    assert(std::get<0>(t_prefix) == nullptr);
+    assert(std::get<1>(t_prefix) == "hello");
+
+    using type_tuple_prefix = std::tuple<std::nullptr_t, std::string>;
+    static_assert(std::is_same<decltype(t_prefix), type_tuple_prefix>::value,
+      "unexpected start()ed tuple type");
+  }
+
+  {
+    auto t_original =
+      std::make_tuple(nullptr, std::string("hello"), std::string("world"));
+    auto t_prefix = sigc::tuple_start<1>(t_original);
+
+    static_assert(std::tuple_size<decltype(t_prefix)>::value == 1,
+      "unexpected tuple_start()ed tuple size.");
+
+    assert(std::get<0>(t_prefix) == nullptr);
+
+    using type_tuple_prefix = std::tuple<std::nullptr_t>;
+    static_assert(std::is_same<decltype(t_prefix), type_tuple_prefix>::value,
+      "unexpected start()ed tuple type");
+  }
+}
+
+void
+test_tuple_start_stdref() {
+  std::string a = "yadda";
+  std::string b = "yaddayadda";
+  auto t_larger = std::make_tuple(std::ref(a), std::ref(b), 1);
+
+  auto t_prefix = sigc::tuple_start<2>(t_larger);
+  a = "hello";
+  b = "world";
+  //This works, but it's not what we are testing here:
+  //assert(std::get<0>(t_larger) == "hello");
+
+  assert(std::get<0>(t_prefix) == "hello");
+  assert(std::get<1>(t_prefix) == "world");
+}
+
+int
+main() {
+  test_tuple_type_start();
+  test_tuple_start();
+  test_tuple_start_stdref();
+}
diff --git a/tests/test_tuple_transform_each.cc b/tests/test_tuple_transform_each.cc
new file mode 100644
index 0000000..18b95d6
--- /dev/null
+++ b/tests/test_tuple_transform_each.cc
@@ -0,0 +1,278 @@
+/* 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 <cassert>
+#include <cstdlib>
+#include <sigc++/tuple_transform_each.h>
+#include <utility>
+#include <functional>
+
+template <class T_element_from>
+class transform_to_string {
+public:
+  static decltype(auto)
+  transform(T_element_from& from) {
+    return std::to_string(from);
+  }
+};
+
+void
+test_tuple_type_transform_each_same_types() {
+  using type_tuple_original = std::tuple<int, int>;
+  using type_tuple_transformed =
+    sigc::tuple_type_transform_each<type_tuple_original,
+      transform_to_string>::type;
+  using type_tuple_expected = std::tuple<std::string, std::string>;
+
+  static_assert(
+    std::is_same<type_tuple_transformed, type_tuple_expected>::value,
+    "unexpected tuple_transform_each()ed tuple type");
+}
+
+// In these tests, t_expected has elements all of the same type.
+void
+test_tuple_transform_each_same_types() {
+  {
+    auto t_original = std::make_tuple(1, 2, 3);
+    auto t_transformed =
+      sigc::tuple_transform_each<transform_to_string>(t_original);
+    auto t_expected =
+      std::make_tuple(std::string("1"), std::string("2"), std::string("3"));
+
+    static_assert(std::tuple_size<decltype(t_transformed)>::value == 3,
+      "unexpected tuple_transform_each()ed tuple size.");
+
+    assert(std::get<0>(t_transformed) == "1");
+    assert(std::get<1>(t_transformed) == "2");
+    assert(std::get<2>(t_transformed) == "3");
+
+    static_assert(
+      std::is_same<decltype(t_transformed), decltype(t_expected)>::value,
+      "unexpected transform_each()ed tuple type");
+  }
+
+  {
+    auto t_original = std::make_tuple(1, (double)2.1f, 3);
+    auto t_transformed =
+      sigc::tuple_transform_each<transform_to_string>(t_original);
+    auto t_expected =
+      std::make_tuple(std::string("1"), std::string("2"), std::string("3"));
+
+    static_assert(std::tuple_size<decltype(t_transformed)>::value == 3,
+      "unexpected tuple_transform_each()ed tuple size.");
+
+    assert(std::get<0>(t_transformed) == "1");
+    assert(std::get<1>(t_transformed).substr(0, 3) == "2.1");
+    assert(std::get<2>(t_transformed) == "3");
+
+    static_assert(
+      std::is_same<decltype(t_transformed), decltype(t_expected)>::value,
+      "unexpected transform_each()ed tuple type");
+  }
+}
+
+// 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 transform_to_something;
+
+// An int will be converted to a std::string:
+template <>
+class transform_to_something<int> {
+public:
+  static std::string
+  transform(int& from) {
+    return std::to_string(from);
+  }
+};
+
+// A double will be converted to a char:
+template <>
+class transform_to_something<double> {
+public:
+  static char
+  transform(double& from) {
+    return std::to_string(from)[0];
+  }
+};
+
+// A std::string will be converted to an int:
+template <>
+class transform_to_something<std::string> {
+public:
+  static int
+  transform(std::string& from) {
+    return std::stoi(from);
+  }
+};
+
+void
+test_tuple_type_transform_each_multiple_types() {
+  using type_tuple_original = std::tuple<int, double, std::string>;
+  using type_tuple_transformed =
+    sigc::tuple_type_transform_each<type_tuple_original,
+      transform_to_something>::type;
+  using type_tuple_expected = std::tuple<std::string, char, int>;
+
+  static_assert(
+    std::is_same<type_tuple_transformed, type_tuple_expected>::value,
+    "unexpected tuple_transform_each()ed tuple type");
+}
+
+// In these tests, t_expected has elements of different types.
+void
+test_tuple_transform_each_multiple_types() {
+  auto t_original = std::make_tuple(1, (double)2.1f, std::string("3"));
+  auto t_transformed =
+    sigc::tuple_transform_each<transform_to_something>(t_original);
+  auto t_expected = std::make_tuple(std::string("1"), '2', 3);
+
+  static_assert(std::tuple_size<decltype(t_transformed)>::value == 3,
+    "unexpected tuple_transform_each()ed tuple size.");
+
+  assert(std::get<0>(t_transformed) == "1");
+  assert(std::get<1>(t_transformed) == '2');
+  assert(std::get<2>(t_transformed) == 3);
+
+  static_assert(
+    std::is_same<decltype(t_transformed), decltype(t_expected)>::value,
+    "unexpected transform_each()ed tuple type");
+}
+
+template <class T_element_from>
+class transform_each_nonconst {
+public:
+  static int
+  transform(T_element_from& from) {
+    from *= 2;
+    // Or, for instance, call a non-const method on from.
+
+    return from * 10;
+  }
+};
+
+void
+test_tuple_transform_each_nonconst() {
+  auto t = std::make_tuple(1, 2, 3);
+  auto t_transformed =
+    sigc::tuple_transform_each<transform_each_nonconst>(t);
+
+  // Check that t was changed (from * 2):
+  assert(std::get<0>(t) == 2);
+  assert(std::get<1>(t) == 4);
+  assert(std::get<2>(t) == 6);
+
+  // Check that t_transformed has the expected values ( from * 2 * 10):
+  assert(std::get<0>(t_transformed) == 20);
+  assert(std::get<1>(t_transformed) == 40);
+  assert(std::get<2>(t_transformed) == 60);
+}
+
+void
+test_tuple_transform_each_stdref() {
+  int a = 1;
+  int b = 2;
+  int c = 3;
+  auto t_original = std::make_tuple(std::ref(a), std::ref(b), std::ref(c));
+  auto t_transformed =
+    sigc::tuple_transform_each<transform_to_string>(t_original);
+  auto t_expected =
+    std::make_tuple(std::string("1"), std::string("2"), std::string("3"));
+
+  static_assert(std::tuple_size<decltype(t_transformed)>::value == 3,
+    "unexpected tuple_transform_each()ed tuple size.");
+
+  assert(std::get<0>(t_transformed) == "1");
+  assert(std::get<1>(t_transformed) == "2");
+  assert(std::get<2>(t_transformed) == "3");
+
+  static_assert(
+    std::is_same<decltype(t_transformed), decltype(t_expected)>::value,
+    "unexpected transform_each()ed tuple type");
+}
+
+
+//This can only be used via std::ref(), for instance.
+//Any attempt to copy or move it, should cause a compiler error.
+class NonCopyable {
+public:
+  explicit NonCopyable(int val)
+  : m_val(val)
+  {}
+  
+  int get_val() const {
+    return m_val;
+  }
+
+  NonCopyable(const NonCopyable& src) = delete;
+  NonCopyable& operator=(const NonCopyable& src) = delete;
+  
+  NonCopyable(NonCopyable&& src) = delete;
+  NonCopyable& operator=(NonCopyable&& src) = delete;
+
+private:
+  int m_val;
+};
+
+
+template <class T_element_from>
+class transform_noncopyable_to_string {
+public:
+  static decltype(auto)
+  transform(T_element_from&& from) {
+    return std::to_string(from.get_val());
+  }
+};
+
+void
+test_tuple_transform_each_stdref_non_copyable() {
+  NonCopyable a(1);
+  NonCopyable b(2);
+  NonCopyable c(3);
+  auto t_original = std::make_tuple(std::ref(a), std::ref(b), std::ref(c));
+  auto t_transformed =
+    sigc::tuple_transform_each<transform_noncopyable_to_string>(t_original);
+  auto t_expected =
+    std::make_tuple(std::string("1"), std::string("2"), std::string("3"));
+
+  static_assert(std::tuple_size<decltype(t_transformed)>::value == 3,
+    "unexpected tuple_transform_each()ed tuple size.");
+
+  assert(std::get<0>(t_transformed) == "1");
+  assert(std::get<1>(t_transformed) == "2");
+  assert(std::get<2>(t_transformed) == "3");
+
+  static_assert(
+    std::is_same<decltype(t_transformed), decltype(t_expected)>::value,
+    "unexpected transform_each()ed tuple type");
+}
+
+int
+main() {
+  test_tuple_type_transform_each_same_types();
+  test_tuple_type_transform_each_multiple_types();
+
+  test_tuple_transform_each_same_types();
+  test_tuple_transform_each_multiple_types();
+
+  test_tuple_transform_each_nonconst();
+
+  test_tuple_transform_each_stdref();
+  test_tuple_transform_each_stdref_non_copyable();
+
+  return EXIT_SUCCESS;
+}


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