Loading...
Searching...
No Matches
named_util.h
Go to the documentation of this file.
1#ifndef _BBM_NAMED_UTIL_H_
2#define _BBM_NAMED_UTIL_H_
3
4#include "util/named.h"
5
6/************************************************************************/
7/*! \file named_util.h
8 \brief Additional convenience methods for named tuples.
9*************************************************************************/
10
11namespace bbm {
12
13 /**********************************************************************/
14 /*! \brief value copy a named tuple
15
16 \param src = named tuple, possibly with references
17 \returns a copy of the named tuple without references
18 ***********************************************************************/
19 template<typename TUP, string_literal... NAMES> requires bbm::is_tuple_v<TUP>
20 inline constexpr auto value_copy_named(const named<TUP, NAMES...>& src)
21 {
22 return make_named<NAMES...>( value_copy_tuple(src.values()) );
23 }
24
25 //! \brief type of value copying a named typle
26 template<typename T> requires is_named_v<T> && is_tuple_v<typename T::value_type>
27 using value_copy_named_t = decltype( value_copy_named(std::declval<T>()) );
28
29
30 /**********************************************************************/
31 /*! \brief cat named types
32
33 named_cat( named<std::tuple<...>, "A", "B">{a,b}, named<std::tuple<...>, "C">{c} )
34
35 yields
36
37 named<std::tuple<...>, "A", B", "C">{a,b,c}
38 **********************************************************************/
39 template<typename... T> requires (is_named_v<T> && ...)
40 inline constexpr auto named_cat(T&&... t)
41 {
42 // helper lambda; use tuple_cat on names and values
43 auto merge = [&]<size_t... IDX>(std::index_sequence<IDX...>)
44 {
45 constexpr auto names = std::tuple_cat( std::decay_t<T>::names...);
46 return make_named< std::get<IDX>(names)... >( std::tuple_cat(t.values()...) );
47 };
48
49 // if empty return default; otherwise call helper lambda
50 if constexpr (sizeof...(T) == 0) return named<std::tuple<>>();
51 else return merge(std::make_index_sequence< (std::tuple_size_v<std::decay_t<T>> + ...) >{});
52 }
53
54 //! \brief type of concatting multiple named tuples.
55 template<typename... T> requires (is_named_v<T> && ...)
56 using named_cat_t = decltype( named_cat(std::declval<T>()...) );
57
58
59 /**********************************************************************/
60 /*! \brief get a subset of a named tuple
61
62 \tparam START = index of first element
63 \tparam COUNT = number of elements
64 \param named = named tuple
65 \returns named tuple with elements [START, START+1, ..., START+COUNT-1
66 ***********************************************************************/
67 template<size_t START, size_t COUNT, typename NAMED> requires is_named_v<NAMED> && ((START+COUNT) <= std::decay_t<NAMED>::size)
68 inline constexpr auto subnamed(NAMED&& named)
69 {
70 // zero length case or beyond end
71 if constexpr (COUNT == 0) return named<std::tuple<>{};
72
73 // otherwise select subset
74 else
75 {
76 // helper lambda to extract the elements
77 auto extract = [&]<size_t... IDX>(std::index_sequence<IDX...>)
78 {
79 return bbm::named<std::tuple< typename std::tuple_element_t<START+IDX, std::decay_t<NAMED>>... >, std::decay_t<NAMED>::template name<START+IDX>...>(std::get<START+IDX>(std::forward<NAMED>(named))...);
80 };
81
82 return extract(std::make_index_sequence<COUNT>{});
83 }
84 }
85
86 //! \brief type of subnamed
87 template<size_t START, size_t COUNT, typename NAMED> requires is_named_v<NAMED> && ((START+COUNT) <= std::decay_t<NAMED>::size)
88 using subnamed_t = decltype( subnamed<START,COUNT,NAMED>( std::declval<NAMED>() ) );
89
90
91 /**********************************************************************/
92 /*! \brief prefix names in type
93
94 prefix_names<"BLA_", named<std::tuple<...>, "A", "B">{a,b}
95
96 yields
97
98 named<std::tuple<...>, "BLA_A", "BLA_B">{a,b}
99 *********************************************************************/
100 template<string_literal PREFIX, typename T, string_literal... NAMES>
101 inline constexpr auto prefix_names(named<T, NAMES...> t)
102 {
103 return static_cast<named<T, (PREFIX+NAMES)...>>(t);
104 }
105
106 //! \brief type of named tuple with pre-fixed name.
107 template<string_literal PREFIX, typename T> requires is_named_v<T>
108 using prefix_names_t = decltype(prefix_names<PREFIX>(std::declval<T>()));
109
110
111 /**********************************************************************/
112 /*! \brief postfix names in type
113
114 postfix_names<"_BLA", named<std::tuple<...>, "A", "B">{a,b}
115
116 yields
117
118 named<std::tuple<...>, "A_BLA", "B_BLA">{a,b}
119 *********************************************************************/
120 template<string_literal POSTFIX, typename T, string_literal... NAMES>
121 inline constexpr auto postfix_names(named<T, NAMES...> t)
122 {
123 return static_cast<named<T, (NAMES + POSTFIX)...>>(t);
124 }
125
126 //! \brief type of named tuple with post-fixed name.
127 template<string_literal POSTFIX, typename T> requires is_named_v<T>
128 using postfix_names_t = decltype(post_names<POSTFIX>(std::declval<T>()));
129
130
131 /*** Named flatten implementation details ***/
132 namespace detail {
133
134 template<string_literal PREFIX, string_literal SEP, bool cat_names, typename T>
135 inline constexpr auto named_flatten(T&& t)
136 {
137 // base case: T is not named
138 if constexpr (!is_named_v<T>) { return make_named<PREFIX>(std::forward_as_tuple(t)); }
139
140 // else: recurse
141 else
142 {
143 auto flatten = [&]<size_t... IDX>(std::index_sequence<IDX...>)
144 {
145 constexpr auto names = std::decay_t<T>::names;
146 return named_cat( (named_flatten<std::get<IDX>(names), SEP, cat_names>(std::get<IDX>(std::forward<T>(t))) )...);
147 };
148
149 // cat all
150 auto result = flatten(std::make_index_sequence<std::decay_t<T>::size>{});
151
152 // prefix names if requested.
153 if constexpr (cat_names && !PREFIX.empty) return prefix_names<PREFIX + SEP>( result );
154 else return result;
155 }
156 }
157
158 } // end detail namespace
159
160 /**********************************************************************/
161 /*! \brief flatten a named type without merging names
162
163 named_flatten( named< std::tuple<named<std::tuple<float, char>, "A", "B">, int>, "C", "D" > )
164
165 yields
166
167 named<std::tuple<float, char, int>, "A", "B", "D">
168 **********************************************************************/
169 template<typename T> requires is_named_v<T>
170 inline constexpr auto named_flatten(T&& t) { return bbm::detail::named_flatten<"", "", false>(std::forward<T>(t)); }
171
172 /**********************************************************************/
173 /*! \brief flatten a named type with merging names
174
175 merge_named_flatten( named< std::tuple<named<std::tuple<float, char>, "A", "B">, int>, "C", "D" > )
176
177 yields
178
179 named<std::tuple<float, char, int>, "C.A", "C.B", "D">
180
181 The default seperator symbol is ".", but this can be changed by passing a
182 different symbols as the first template argument.
183 **********************************************************************/
184 template<string_literal SEP=".", typename T> requires is_named_v<T>
185 inline constexpr auto merge_named_flatten(T&& t) { return bbm::detail::named_flatten<"", SEP, true>(std::forward<T>(t)); }
186
187
188 /*** Implementation detail for is_named_sorted ***/
189 namespace detail {
190 template<typename NAMED>
191 inline constexpr bool is_named_sorted(void)
192 {
193 auto all_sorted = [&]<size_t... IDX>(std::index_sequence<IDX...>)
194 {
195 return ((std::decay_t<NAMED>::template name<IDX> < std::decay_t<NAMED>::template name<IDX+1>) && ...);
196 };
197
198 if constexpr (std::decay_t<NAMED>::size <= 1) return true;
199 else return all_sorted(std::make_index_sequence<std::decay_t<NAMED>::size - 1>{});
200 }
201 } // end detail namespace
202
203 /**********************************************************************/
204 /*! \brief true if named tuple is sorted by name
205 ********************************************************************/
206 template<typename NAMED> requires is_named_v<NAMED>
207 static constexpr bool is_named_sorted_v = detail::is_named_sorted<NAMED>();
208
209
210 /*** Implementation details for binary_search_named ***/
211 namespace detail {
212 template<string_literal NAME, typename NAMED, size_t START=0, size_t END = NAMED::size>
213 inline constexpr size_t binary_search_named(void)
214 {
215 if constexpr (START >= END) return START;
216 else
217 {
218 constexpr size_t IDX = (START + END) / 2;
219 if constexpr (IDX == NAMED::size) return IDX;
220 else if constexpr (NAMED::template name<IDX> == NAME) return IDX;
221 else if constexpr (NAMED::template name<IDX> < NAME) return binary_search_named<NAME, NAMED, IDX+1, END>();
222 else return binary_search_named<NAME, NAMED, START, IDX>();
223 }
224 }
225 } // end detail namespace
226
227 /**********************************************************************/
228 /*! \brief binary search a _sorted_ named tuple
229
230 \tparam NAME = name to search for
231 \tparam NAMED = named tuple
232 \returns index of best matching name (index of first name that is equal or larger)
233 ***********************************************************************/
234 template<string_literal NAME, typename NAMED> requires is_named_v<NAMED> && is_named_sorted_v<NAMED>
235 static constexpr size_t binary_search_named_v = detail::binary_search_named<NAME, std::decay_t<NAMED>>();
236
237
238 /**********************************************************************/
239 /*! \brief sort a named tuple by name using insert-sort.
240
241 \param named = named tuple to sort
242 \tparam IDX = start index of elements to sort; recommended default value = 0
243 \tparam PARTIAL = partial solution to insert into; recommended default value = empty named tuple
244 \returns sorted(NAMED[IDX..END], PARTIAL)
245
246 Details: this is a constexpr recursive method. It will attempt to insert
247 the IDX-th element in PARTIAL, and then recurse to the IDX+1 element,
248 until all elements are inserted.
249 ************************************************************************/
250 template<typename NAMED, size_t IDX=0, typename PARTIAL=named<std::tuple<>>>
251 inline constexpr auto sort_named(NAMED&& named, PARTIAL&& partial = PARTIAL{})
252 {
253 // if all processed, return PARTIAL 'p'
254 if constexpr (std::decay_t<NAMED>::size <= IDX) return partial;
255
256 // insert the IDX-th element from 'n' in PARTIAL 'p', and then recurse to insert IDX+1
257 else
258 {
259 // insert
260 auto insert = []<string_literal NAME>(auto&& t, auto&& n)
261 {
262 using N = decltype(n);
263 using T = decltype(t);
264 constexpr size_t INSERT = binary_search_named_v<NAME, N>;
265 if constexpr (INSERT == 0) return named_cat( make_named<NAME>(std::forward<T>(t)), std::forward<N>(n) );
266 else if constexpr (INSERT == std::decay_t<N>::size) return named_cat( std::forward<N>(n), make_named<NAME>(std::forward<T>(t)) );
267 else return named_cat( subnamed<0, INSERT>(std::forward<N>(n)), make_named<NAME>(std::forward<T>(t)), subnamed<INSERT, std::decay_t<N>::size - INSERT>(std::forward<N>(n)) );
268 };
269
270 // recurse
271 return sort_named<NAMED, IDX+1>( std::forward<NAMED>(named), insert.template operator()< std::decay_t<NAMED>::template name<IDX> >( std::get<IDX>(std::forward<NAMED>(named)), partial) );
272 }
273 }
274
275
276 /*** Implementation detail for binary_search_named with strings ***/
277 namespace detail {
278 /********************************************************************/
279 /*! \brief recusively search a sorted named tuple for a name that matches
280 a string, and then call a lambda function to process the found element.
281
282 \tparam START = start index in named to search
283 \tparam END = end index (+1) in named to search
284 \param str = string to match
285 \param named = named tuple to seach in
286 \param process = lambda to call on found result
287 \param context... = additional variables to pass to the process lambda
288
289 See bbm::find_named_sorted for details on the process lambda.
290 ***********************************************************************/
291 template<size_t START, size_t END, typename NAMED, typename PROCESS, typename... Ts>
292 inline auto binary_search_named(const std::string& str, NAMED&& named, PROCESS&& process, Ts&&... context)
293 {
294 // recursion stopped without finding an exact match; call process with best matching index.
295 if constexpr (START >= END) return process.template operator()<START>(str, std::forward<NAMED>(named), std::forward<Ts>(context)...);
296
297 // recurse
298 else
299 {
300 constexpr size_t IDX = (START+END) / 2;
301 static constexpr size_t size = std::decay_t<NAMED>::size;
302 std::string name(std::decay_t<NAMED>::template name<IDX>);
303
304 // match found (or out of bound) => call process with best matching index
305 if(IDX == size || name == str) return process.template operator()<IDX>(str, std::forward<NAMED>(named), std::forward<Ts>(context)...);
306
307 // recurse right
308 else if(name < str) return binary_search_named<IDX+1, END>(str, std::forward<NAMED>(named), std::forward<PROCESS>(process), std::forward<Ts>(context)...);
309
310 // recurse left
311 else return binary_search_named<START, IDX>(str, std::forward<NAMED>(named), std::forward<PROCESS>(process), std::forward<Ts>(context)...);
312 }
313
314 // Done.
315 }
316 } // end detail namespace
317
318 /**********************************************************************/
319 /*! \brief Run-time binary search for a matching name in a named tuple based
320 on a string. The (index of the) found element is forwarded to a
321 processing lambda.
322
323 \param str = string of name to find
324 \param named = (sorted) named tuple
325 \param process = lambda function to process the found element
326 \param context... = additional parameters to pass to process
327
328 Q&A:
329
330 + Why call a processing lambda? Each element in the tuple can potentially
331 have a different type. Hence we cannot just return the found result.
332
333 + Why not return an index to the found element? The returned index would
334 not be a constexpr, and hence you cannot use it to get the corresponding
335 element with std::get.
336
337 + What is the signature of the process lambda: The lambda takes the index
338 of the found element as a template parameter (size_t), and it takes the
339 following run-time arguments: a 'const std::string&' of the searched for
340 string, the named tuple, and all context variables.
341
342 + What if no match is found? the process lambda is expected to check if
343 (index < NAMED::size) and that (str == std::get<IDX>(named))
344
345 + What is the goal of the 'context' variables? To give the processing
346 lambda access to any data that it needs to complete its task (including
347 variables to store results in).
348
349 + Can process return a result? Yes, as long as the return type is the
350 same regardless of the index of the found element. Consider using std::any
351 or context variables if this is not possible.
352
353 ***********************************************************************/
354 template<typename NAMED, typename PROCESS, typename... Ts> requires
355 is_named_v<NAMED> && // must be a named tuple
356 is_named_sorted_v<NAMED> && // named tuple must be sorted
357 requires(PROCESS&& p) // check the signature of process
358 { p.template operator()<size_t(0)>(std::declval<std::string>(), std::declval<NAMED>(), std::declval<Ts>()...); }
359 inline auto binary_search_named(const std::string& str, NAMED&& named, PROCESS&& process, Ts&&... context)
360 {
361 return bbm::detail::binary_search_named<0, std::decay_t<NAMED>::size>(str, std::forward<NAMED>(named), std::forward<PROCESS>(process), std::forward<Ts>(context)...);
362 }
363
364
365 /**********************************************************************/
366 /*! \brief linear search for unsorted named tuples
367
368 \param str = string to find
369 \param named = named tuple to search
370 \param process = lambda function to process the found element
371 \param context... = additional parameters to pass to process
372
373 Process will be called with IDX == NAMED::size if not found.
374
375 ***********************************************************************/
376 template<typename NAMED, typename PROCESS, typename... Ts> requires
377 is_named_v<NAMED> && // must be a named tuple
378 requires(PROCESS&& p) // check the signature of process
379 { p.template operator()<size_t(0)>(std::declval<std::string>(), std::declval<NAMED>(), std::declval<Ts>()...); }
380 inline auto linear_search_named(const std::string& str, NAMED&& named, PROCESS&& process, Ts&&... context)
381 {
382 using result_t = decltype( process.template operator()<size_t(0)>(std::declval<std::string>(), std::declval<NAMED>(), std::declval<Ts>()...) );
383
384 // lambda returns void
385 if constexpr (std::is_void_v<result_t>)
386 {
387 bool found = false;
388
389 CONSTFOR(IDX, std::decay_t<NAMED>::size,
390 {
391 if(str == std::string(std::decay_t<NAMED>::template name<IDX>)) process.template operator()<IDX>(str, std::forward<NAMED>(named), std::forward<Ts>(context)...);
392 });
393
394 // if not found, call with out-of-bound index.
395 if(!found) process.template operator()<std::decay_t<NAMED>::size>(str, std::forward<NAMED>(named), std::forward<Ts>(context)...);
396
397 }
398
399 // else lambda returns a value
400 else
401 {
402 result_t result;
403 bool found = false;
404
405 CONSTFOR(IDX, std::decay_t<NAMED>::size,
406 {
407 if(str == std::string(std::decay_t<NAMED>::template name<IDX>))
408 {
409 result = process.template operator()<IDX>(str, std::forward<NAMED>(named), std::forward<Ts>(context)...);
410 found = true;
411 }
412 });
413
414 // if not found, call with out-of-bound index.
415 if(!found) result = process.template operator()<std::decay_t<NAMED>::size>(str, std::forward<NAMED>(named), std::forward<Ts>(context)...);
416
417 return result;
418 }
419
420 // Done.
421 }
422
423} // end bbm namespace
424
425#endif /* _BBM_NAMED_UTIL_H_ */
#define CONSTFOR(IDX, NUMITR,...)
HELPER MACRO.
Definition: constfor.h:63
Definition: aggregatebsdf.h:29
constexpr auto named_cat(T &&... t)
cat named types
Definition: named_util.h:40
constexpr auto merge_named_flatten(T &&t)
flatten a named type with merging names
Definition: named_util.h:185
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
decltype(prefix_names< PREFIX >(std::declval< T >())) prefix_names_t
type of named tuple with pre-fixed name.
Definition: named_util.h:108
constexpr auto value_copy_named(const named< TUP, NAMES... > &src)
value copy a named tuple
Definition: named_util.h:20
auto linear_search_named(const std::string &str, NAMED &&named, PROCESS &&process, Ts &&... context)
linear search for unsorted named tuples
Definition: named_util.h:380
constexpr auto sort_named(NAMED &&named, PARTIAL &&partial=PARTIAL{})
sort a named tuple by name using insert-sort.
Definition: named_util.h:251
constexpr auto subnamed(NAMED &&named)
get a subset of a named tuple
Definition: named_util.h:68
auto binary_search_named(const std::string &str, NAMED &&named, PROCESS &&process, Ts &&... context)
Run-time binary search for a matching name in a named tuple based on a string. The (index of the) fou...
Definition: named_util.h:359
decltype(subnamed< START, COUNT, NAMED >(std::declval< NAMED >())) subnamed_t
type of subnamed
Definition: named_util.h:88
static constexpr bool is_named_sorted_v
true if named tuple is sorted by name
Definition: named_util.h:207
static constexpr size_t binary_search_named_v
binary search a sorted named tuple
Definition: named_util.h:235
size_t size(T &&t)
Definition: iterator_util.h:22
decltype(value_copy_named(std::declval< T >())) value_copy_named_t
type of value copying a named typle
Definition: named_util.h:27
constexpr auto named_flatten(T &&t)
flatten a named type without merging names
Definition: named_util.h:170
constexpr auto prefix_names(named< T, NAMES... > t)
prefix names in type
Definition: named_util.h:101
decltype(named_cat(std::declval< T >()...)) named_cat_t
type of concatting multiple named tuples.
Definition: named_util.h:56
constexpr auto postfix_names(named< T, NAMES... > t)
postfix names in type
Definition: named_util.h:121
constexpr auto value_copy_tuple(const std::tuple< ARGS... > &tup)
Value-copy a tuple.
Definition: tuple.h:61
decltype(post_names< POSTFIX >(std::declval< T >())) postfix_names_t
type of named tuple with post-fixed name.
Definition: named_util.h:128
Definition: named.h:325
named container
Definition: named.h:131
constexpr T & values(void)
\names Querry/cast values from the underlying container
Definition: named.h:182
Definition: string_literal.h:16
A wrapper for STL containers such as tuple, pair, and array. These containers force the programmer to...