Loading...
Searching...
No Matches
named.h
Go to the documentation of this file.
1#ifndef _BBM_NAMED_H_
2#define _BBM_NAMED_H_
3
4#include <tuple>
5
6#include "concepts/util.h"
7
8#include "util/constfor.h"
10
11/************************************************************************/
12/*! \file named.h
13
14 \brief A wrapper for STL containers such as tuple, pair, and array. These
15 containers force the programmer to remember the function/meaning of each
16 element based on its position. This is error-prone. To mitigate this, the
17 named wrapper allows to assign a name, via a template parameters, to each
18 element so that subsequently it can be used to query elements by name, and
19 to ensure that the assignment takes in account the order of the elements.
20
21 **named container elements vs structs** A struct cannot be defined in a
22 function signature, and thus requires an external struct definition. This
23 is cumbersome for single use structures. Structured bindings introduced in
24 C++17 do not completely solve this problem, since the definition is still
25 annonymous. Example:
26
27 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
28 struct foo { float a; float b; char c; };
29
30 foo bar1(void);
31 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32
33 Alternatively, a single use tuple can be used:
34
35 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
36 std::tuple<float, float, char> bar2(void);
37 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
38
39 but now the meaning of each element is implicit (is 'a' the first or second
40 float?). Named containers provide an alternative:
41
42 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
43 named<std::tuple<float,float,char>, "a", "b", "c"> bar3(void);
44 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
45
46 Equivallent correspondling look ups for the above 3 stratgies are:
47
48 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
49 auto b1 = bar1();
50 auto b2 = bar2();
51 auto b3 = bar3();
52
53 std::cout << b1.a << std::endl;
54 std::cout << std::get<0>(b2) << std::endl;
55 std::cout << get<"a">(b3) << std::endl;
56 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
57
58 While a bit more verbose, named containers are more closely related to
59 structs, and thus less error prone.
60
61 Addtionally, named containers also allow for 'tie' with names:
62
63 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
64 float a, b; char c;
65 tie<"a", "b", "c">{a,b,c} = bar3();
66 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
67
68 The following will also take the order of names in account:
69
70 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
71 named<std::tuple<float, char, float>, "b", "c", "a"> b4 = bar3();
72
73 std::cout << get<"a">(b4) << " == " << get<"a">(b3) << std::endl;
74 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
75
76 Similarly there are 2 helper methods for making a named structure:
77
78 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
79 auto c1 = make_named<"a", "b", "c">( std::tuple<float,float,char>(1,2,'a') );
80 auto c2 = make_named<"a", "b", "c">(1, 2, 'a'); // produces a named tuple
81 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
82
83 To pick a subset of named elements use:
84
85 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
86 auto c3 = pick<"a", "c">(c2);
87 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
88
89 All of this can be combined with structured bindings:
90
91 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
92 auto [a, c] = pick<"a", "c">( foo3() );
93 auto [e,f,g] = foo3();
94 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
95
96 The former has the preference, since it allows to:
97 + pick a subset (i.e., skip elements) as demonstrated
98 + explicitely name the elements you pick.
99
100 Named structures can also be recursive; a shortcut for get allows to
101 directly query the recursive values:
102
103 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
104 auto c4 = make_named<"a">(make_named<"b", "c">('b', 'c'));
105 std::cout << get<"a">(c4) << std::endl; // regular get: (b = b, c = c)
106 std::cout << get<"a", "b">(c4) << std::endl; // recursive get: b
107 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
108
109 Note: a recusive named type wraps the inner named type in a tuple. Without
110 wrapping, the names of the inner type are overwritten:
111
112 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
113 named< named<std::tuple<int,int>, "a", "b">, "c", "d" > t0{1,2}; // (c = 1, d = 2)
114 named< named<std::tuple<int,int>, "a", "b">, "c" > t1; // ERROR
115 named< std::tuple<named<std::tuple<int,int>, "c", "d">>, "e"> t2(t0); // (e = (c = 1, d = 2))
116 named< std::tuple<named<std::tuple<int,int>, "c", "d">>, "e", "f"> t3; // ERROR
117 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
118
119*************************************************************************/
120
121namespace bbm {
122
123 /**********************************************************************/
124 /*! \brief named container
125
126 \tparam T = underlying container type; must support std::get and std::tuple_size_v
127 \tparam NAMES = names of each element
128 ***********************************************************************/
129 template<typename T, string_literal... NAMES> requires (sizeof...(NAMES) == 0) || (concepts::gettable<T> && (sizeof...(NAMES) == std::tuple_size_v<std::decay_t<T>>))
130 struct named : public T
131 {
132 public:
133 using value_type = T;
134
135 //! \brief query name by index
136 template<size_t IDX> requires (IDX < sizeof...(NAMES))
137 static constexpr auto name = std::get<IDX>(std::make_tuple(NAMES...));
138
139 //! \brief tuple of all names
140 static constexpr auto names = std::make_tuple(NAMES...);
141
142 //! \brief check if has_name
143 template<string_literal NAME>
144 static constexpr bool has_name = (named<T,NAMES...>::template _find_name<0,NAME,NAMES...>() != sizeof...(NAMES));
145
146 //! \brief find index of name (size if not found); uses a linear search
147 template<string_literal NAME>
148 static constexpr size_t find_name = named<T,NAMES...>::template _find_name<0,NAME,NAMES...>();
149
150 //! \brief size (number of names/elements in value_type)
151 static constexpr size_t size = sizeof...(NAMES);
152
153 //! \brief Forwarding constructor
154 template<typename... Ts>
155 constexpr named(Ts&&...ts) : T{std::forward<Ts>(ts)...} {}
156
157 //! \brief Reshuffle constructor (based on matching names)
158 template<typename U, string_literal... UNAMES> requires (sizeof...(NAMES) == sizeof...(UNAMES))
159 constexpr named(named<U, UNAMES...>&& src) : named(get<NAMES>(std::forward<decltype(src)>(src))...) { }
160
161 //! \brief Reshuffle constructor (based on matching names)
162 template<typename U, string_literal... UNAMES> requires (sizeof...(NAMES) == sizeof...(UNAMES))
163 constexpr named(const named<U, UNAMES...>& src) : named(get<NAMES>(src)...) { }
164
165 //! \brief reshuffle assignment
166 template<typename U, string_literal... UNAMES> requires (sizeof...(NAMES) == sizeof...(UNAMES))
167 named operator=(const named<U,UNAMES...>& src)
168 {
169 ((this->template get<UNAMES>() = src. template get<UNAMES>()), ...);
170 return *this;
171 }
172
173 //! \brief unnamed assignment
174 template<typename... Ts> requires (sizeof...(NAMES) == sizeof...(Ts))
175 named operator=(const std::tuple<Ts...>& src)
176 {
177 values() = src;
178 return *this;
179 }
180
181 //! @{ \names Querry/cast values from the underlying container
182 constexpr T& values(void) { return *this; }
183 constexpr const T& values(void) const { return *this; }
184
185 constexpr operator T(void) { return values(); }
186 //! @}
187
188 //! @{ \name 'get' by name or index
189 template<size_t IDX> constexpr decltype(auto) get(void) { return std::get<IDX>(values()); }
190 template<size_t IDX> constexpr decltype(auto) get(void) const { return std::get<IDX>(values()); }
191
192 template<string_literal NAME, string_literal... SUBNAME> constexpr decltype(auto) get(void)
193 {
194 // find name
195 constexpr size_t idx = _find_name<0,NAME,NAMES...>();
196 static_assert( idx < sizeof...(NAMES), "named structure does not contain requested name.");
197
198 // retreive value; recurse if necessary
199 if constexpr (sizeof...(SUBNAME) == 0) return get<idx>();
200 else return get<idx>().template get<SUBNAME...>();
201 }
202
203 template<string_literal NAME, string_literal... SUBNAME> constexpr decltype(auto) get(void) const
204 {
205 // find name
206 constexpr size_t idx = _find_name<0,NAME,NAMES...>();
207 static_assert( idx < sizeof...(NAMES), "named structure does not contain requested name.");
208
209 // retreive value; recurse if necessary
210 if constexpr (sizeof...(SUBNAME) == 0) return get<idx>();
211 else return get<idx>().template get<SUBNAME...>();
212 }
213 //! @}
214
215 //! \brief ostream
216 friend std::ostream& operator<<(std::ostream& s, const named& n)
217 {
218 s << "(";
219 CONSTFOR(idx, sizeof...(NAMES),
220 {
221 if constexpr (idx != 0) s << ", ";
222
223 // print name
224 s << n.template name<idx> << " = ";
225
226 // add "" if a string type
227 if constexpr (bbm::is_string_type_v< std::tuple_element_t<idx, named> >) s << std::string("\"") + std::get<idx>(n) << std::string("\"");
228
229 // otherwise print value
230 else s << std::get<idx>(n);
231 });
232 s << ")";
233 return s;
234 }
235
236 private:
237 template<size_t IDX, string_literal NAME, string_literal N, string_literal... Ns>
238 static constexpr size_t _find_name(void)
239 {
240 if constexpr (NAME == N) return IDX;
241 else if constexpr (sizeof...(Ns) != 0) return _find_name<IDX+1, NAME, Ns...>();
242 else return sizeof...(NAMES);
243 }
244 };
245
246
247 /**********************************************************************/
248 /*! @{ \name Type traits
249 **********************************************************************/
250 template<typename T> struct is_named : std::false_type {};
251 template<typename T, string_literal... NAMES> struct is_named<named<T,NAMES...>> : std::true_type {};
252
253 template<typename T>
255 //! @}
256
257 /**********************************************************************/
258 /*! @{ \name anonymize trait: remove the names
259 **********************************************************************/
260 template<typename T>
261 constexpr decltype(auto) anonymize_v(T&& t)
262 {
263 if constexpr (is_named_v<T>) return t.values();
264 else return std::forward<T>(t);
265 }
266
267 template<typename T>
268 using anonymize_t = std::decay_t<decltype(anonymize_v(std::declval<T>()))>;
269 //! @}
270
271 /**********************************************************************/
272 /*! @{ \name named_equivalence trait: do two types have the same set of names?
273 **********************************************************************/
274 namespace detail {
275 template<typename U, typename T> struct include_names_from : std::false_type {};
276 template<typename U, typename T, string_literal... NAMES> requires is_named_v<U>
277 struct include_names_from<U, named<T,NAMES...>>: std::bool_constant< (U::template has_name<NAMES> && ...) > {};
278 }
279
280 template<typename U, typename V> struct named_equivalence : std::false_type {};
281 template<typename U, typename V> requires is_named_v<U> && is_named_v<V>
282 struct named_equivalence<U,V> : std::conjunction< bbm::detail::include_names_from<std::decay_t<U>,std::decay_t<V>>, bbm::detail::include_names_from<std::decay_t<V>,std::decay_t<U>> > {};
283
284 template<typename U, typename V>
286 //! @}
287
288 /**********************************************************************/
289 /*! \brief Make a named of a gettable type (with size == #NAMES); renames if
290 the type is a named container.
291 **********************************************************************/
292 template<string_literal... NAMES, typename T> requires (sizeof...(NAMES) == 0) || (concepts::gettable<T> && (sizeof...(NAMES) == std::tuple_size_v<std::decay_t<T>>))
293 constexpr named<anonymize_t<T>, NAMES...> make_named(T&& t) { return named<anonymize_t<T>,NAMES...>{anonymize_v(std::forward<T>(t))}; }
294
295 /**********************************************************************/
296 /*! \brief Make a named tuple from a list of arguments (number of arguments == #NAMES)
297 **********************************************************************/
298 template<string_literal... NAMES, typename... Ts> requires (sizeof...(NAMES) == sizeof...(Ts))
299 constexpr auto make_named(Ts&&... ts) { return make_named<NAMES...>(std::make_tuple(std::forward<Ts>(ts)...)); }
300
301 /**********************************************************************/
302 /*! \brief Pick a subset/reshuffle a named container T and return as a named tuple
303 **********************************************************************/
304 template<string_literal... NAMES, typename T> requires is_named_v<T>
305 constexpr auto pick(T&& t) { return make_named<NAMES...>(get<NAMES>(std::forward<T>(t))...); }
306
307 /**********************************************************************/
308 /*! \brief Tie by name
309 **********************************************************************/
310 template<string_literal... NAMES, typename... Ts> requires (sizeof...(NAMES) == sizeof...(Ts))
311 named<std::tuple<Ts&...>, NAMES...> tie(Ts&&... src) { return std::tie(std::forward<Ts>(src)...); }
312
313 /**********************************************************************/
314 /*! @{ \name Get by name
315 **********************************************************************/
316 template<string_literal NAME, string_literal... SUBNAME, typename T> requires is_named_v<T>
317 inline constexpr decltype(auto) get(T&& src) { return src.template get<NAME, SUBNAME...>(); }
318
319 template<string_literal NAME, string_literal... SUBNAME, typename T> requires is_named_v<T>
320 inline constexpr decltype(auto) get(const T& src) { return src.template get<NAME, SUBNAME...>(); }
321 //! @}
322
323} // end bbm namespace
324
325namespace std {
326
327 //! \brief tuple_size specialization (to support structured binding)
328 template<typename T, bbm::string_literal... NAMES>
329 struct tuple_size<bbm::named<T,NAMES...>> : tuple_size<T> {};
330
331 //! \brief tuple_element specialization (to support structured binding)
332 template<size_t idx, typename T, bbm::string_literal... NAMES>
333 struct tuple_element<idx, bbm::named<T,NAMES...>> : tuple_element<idx, T> {};
334
335}
336
337///////////////////
338// Include utils //
339///////////////////
340#include "named_util.h"
341
342#endif /* _BBM_NAMED_H_ */
std::get supported
Definition: util.h:175
Complile-time for loop.
#define CONSTFOR(IDX, NUMITR,...)
HELPER MACRO.
Definition: constfor.h:63
Additional convenience methods for named tuples.
Definition: aggregatebsdf.h:29
constexpr named< anonymize_t< T >, NAMES... > make_named(T &&t)
Make a named of a gettable type (with size == #NAMES); renames if the type is a named container.
Definition: named.h:293
constexpr bool is_named_v
Definition: named.h:254
named< std::tuple< Ts &... >, NAMES... > tie(Ts &&... src)
Tie by name.
Definition: named.h:311
static constexpr bool named_equivalence_v
Definition: named.h:285
constexpr decltype(auto) anonymize_v(T &&t)
Definition: named.h:261
constexpr auto pick(T &&t)
Pick a subset/reshuffle a named container T and return as a named tuple.
Definition: named.h:305
size_t size(T &&t)
Definition: iterator_util.h:22
constexpr decltype(auto) get(T &&src)
Definition: named.h:317
std::decay_t< decltype(anonymize_v(std::declval< T >()))> anonymize_t
Definition: named.h:268
decltype(auto) value(T &&t)
return the value of an attribute, or if not an attribute the object
Definition: attribute_value.h:20
Definition: named.h:325
Definition: named.h:250
Definition: named.h:280
named container
Definition: named.h:131
constexpr decltype(auto) get(void) const
Definition: named.h:203
constexpr T & values(void)
\names Querry/cast values from the underlying container
Definition: named.h:182
constexpr decltype(auto) get(void)
Definition: named.h:189
constexpr named(const named< U, UNAMES... > &src)
Reshuffle constructor (based on matching names)
Definition: named.h:163
friend std::ostream & operator<<(std::ostream &s, const named &n)
ostream
Definition: named.h:216
constexpr decltype(auto) get(void)
Definition: named.h:192
constexpr named(named< U, UNAMES... > &&src)
Reshuffle constructor (based on matching names)
Definition: named.h:159
constexpr const T & values(void) const
Definition: named.h:183
static constexpr size_t _find_name(void)
Definition: named.h:238
constexpr named(Ts &&...ts)
Forwarding constructor.
Definition: named.h:155
constexpr decltype(auto) get(void) const
Definition: named.h:190
T value_type
Definition: named.h:133
Definition: string_literal.h:16
Additional basic helper concepts.