1 /* Copyright (C) 2016 Murray Cumming
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/
17 #ifndef SIGC_TUPLE_UTILS_TUPLE_TRANSFORM_EACH_H
18 #define SIGC_TUPLE_UTILS_TUPLE_TRANSFORM_EACH_H
20 #include <sigc++/tuple-utils/tuple_cdr.h>
21 #include <sigc++/tuple-utils/tuple_end.h>
22 #include <sigc++/tuple-utils/tuple_start.h>
23 #include <type_traits>
31 template <template <typename> class T_transformer, std::size_t size_from_index>
32 struct tuple_transform_each_impl {
33 // TODO: Avoid the need to pass t_original all the way into the recursion?
34 template <typename T_current, typename T_original>
37 tuple_transform_each(T_current&& t, T_original& t_original) {
38 //We use std::decay_t<> because tuple_size is not defined for references.
39 constexpr auto size = std::tuple_size<std::decay_t<T_current>>::value;
40 static_assert(size > 1, "size must be more than 0.");
42 constexpr auto index = size - size_from_index;
43 static_assert(index >= 0, "unexpected index.");
45 using from_element_type = typename std::tuple_element<index, std::decay_t<T_original>>::type;
46 using to_element_type = typename std::result_of<decltype (
47 &T_transformer<from_element_type>::transform)(from_element_type&)>::type;
48 const auto t_element =
49 std::tuple<to_element_type>(T_transformer<from_element_type>::transform(std::get<index>(t_original)));
51 const auto t_start = tuple_start<index>(std::forward<T_current>(t));
53 // t_end's elements will be copies of the elements in t, so this method's
54 // caller won't see the changes made in the subsequent call of
55 // tuple_transform_each() on those copies. That's why we pass t_original
56 // through too, so we can modify that directly.
57 // the const version (tuple_transform_each_const()) doesn't have to worry
58 // about this, though avoiding copying would be more efficient.
59 const auto t_end = tuple_end<size - index - 1>(t);
61 auto t_with_transformed_element = std::tuple_cat(t_start, t_element, t_end);
62 return tuple_transform_each_impl<T_transformer,
63 size_from_index - 1>::tuple_transform_each(t_with_transformed_element, t_original);
67 template <template <typename> class T_transformer>
68 struct tuple_transform_each_impl<T_transformer, 1> {
69 template <typename T_current, typename T_original>
73 tuple_transform_each(T_current&& t, T_original& t_original) {
74 //We use std::decay_t<> because tuple_size is not defined for references.
75 constexpr auto size = std::tuple_size<std::decay_t<T_current>>::value;
76 static_assert(size > 0, "size must be more than 0.");
78 constexpr auto index = size - 1;
79 static_assert(index >= 0, "unexpected index.");
81 using from_element_type = typename std::tuple_element<index, T_original>::type;
82 using to_element_type = typename std::result_of<decltype (
83 &T_transformer<from_element_type>::transform)(from_element_type&)>::type;
84 const auto tuple_element =
85 std::tuple<to_element_type>(T_transformer<from_element_type>::transform(std::get<index>(t_original)));
87 const auto tuple_rest = tuple_start<size - 1>(std::forward<T_current>(t));
88 return std::tuple_cat(tuple_rest, tuple_element);
92 template <template <typename> class T_transformer>
93 struct tuple_transform_each_impl<T_transformer, 0> {
94 template <typename T_current, typename T_original>
98 tuple_transform_each(T_current&& t, T_original& /* t_original */) {
99 //Do nothing because the tuple has no elements.
100 return std::forward<T_current>(t);
104 } // detail namespace
107 * Get a tuple with each element having the transformed value of the element
108 * in the original tuple.
110 template <template <typename> class T_transformer, typename T>
113 tuple_transform_each(T&& t) {
114 //We use std::decay_t<> because tuple_size is not defined for references.
115 constexpr auto size = std::tuple_size<std::decay_t<T>>::value;
117 return detail::tuple_transform_each_impl<T_transformer,
118 size>::tuple_transform_each(std::forward<T>(t), t);
121 } // namespace internal
125 #endif //SIGC_TUPLE_UTILS_TUPLE_TRANSFORM_EACH_H