Imported Upstream version 2.99.2
[platform/upstream/libsigc++.git] / sigc++ / tuple-utils / tuple_transform_each.h
1 /* Copyright (C) 2016 Murray Cumming
2  *
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.
7  *
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.
12  *
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/
15  */
16
17 #ifndef SIGC_TUPLE_UTILS_TUPLE_TRANSFORM_EACH_H
18 #define SIGC_TUPLE_UTILS_TUPLE_TRANSFORM_EACH_H
19
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>
24
25 namespace sigc {
26
27 namespace internal {
28
29 namespace detail {
30
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>
35   constexpr
36   static decltype(auto)
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.");
41
42     constexpr auto index = size - size_from_index;
43     static_assert(index >= 0, "unexpected index.");
44
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)));
50
51     const auto t_start = tuple_start<index>(std::forward<T_current>(t));
52
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);
60
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);
64   }
65 };
66
67 template <template <typename> class T_transformer>
68 struct tuple_transform_each_impl<T_transformer, 1> {
69   template <typename T_current, typename T_original>
70   constexpr
71   static
72   decltype(auto)
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.");
77
78     constexpr auto index = size - 1;
79     static_assert(index >= 0, "unexpected index.");
80
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)));
86
87     const auto tuple_rest = tuple_start<size - 1>(std::forward<T_current>(t));
88     return std::tuple_cat(tuple_rest, tuple_element);
89   }
90 };
91
92 template <template <typename> class T_transformer>
93 struct tuple_transform_each_impl<T_transformer, 0> {
94   template <typename T_current, typename T_original>
95   constexpr
96   static
97   decltype(auto)
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);
101   }
102 };
103
104 } // detail namespace
105
106 /**
107  * Get a tuple with each element having the transformed value of the element
108  * in the original tuple.
109  */
110 template <template <typename> class T_transformer, typename T>
111 constexpr
112 decltype(auto)
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;
116
117   return detail::tuple_transform_each_impl<T_transformer,
118     size>::tuple_transform_each(std::forward<T>(t), t);
119 }
120
121 } // namespace internal
122
123 } // namespace sigc
124
125 #endif //SIGC_TUPLE_UTILS_TUPLE_TRANSFORM_EACH_H