Loading...
Searching...
No Matches
args.h
Go to the documentation of this file.
1#ifndef _BBM_ARGS_H_
2#define _BBM_ARGS_H_
3
5
6#include "core/arg.h"
7#include "core/error.h"
8
9#include "util/constfor.h"
10#include "util/constforeach.h"
12#include "util/macro_util.h"
13#include "util/make_from.h"
14#include "util/named.h"
15
16/************************************************************************/
17/*! \file args.h
18 \brief Provides a more flexible argument passing to function and methods.
19
20 Example:
21
22 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
23 void foo( args<arg<float, "a">, arg<float, "b">> args)
24 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25
26 defines a method foo that takes two arguments named "a" and "b" that each
27 are a float. The basic way to call the method is:
28
29 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
30 foo({1.0f, 2.0f});
31 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32
33 note the additional enclosing curly brackets. See below for ways to remove
34 the need for the brackets. If there are multiple specializations of 'foo' then
35 it might be needed to specify the type of the arguments:
36
37 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
38 foo( args<arg<float,"a">, arg<float, "b">>{1.0f, 2.0f} );
39 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
40
41 However, other methods for passing the arguments are also supported:
42
43 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
44 foo({"a"_arg = 1.0f, "b"_arg = 2.0f});
45 foo({"b"_arg = 2.0f, "a"_arg = 1.0f});
46 foo({1.0f, "b"_arg = 2.0f});
47 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
48
49 are also valid. Arguments can also be passed by their position in the args:
50
51 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
52 foo({"0"_arg = 1.0f, "1"_arg = 2.0f}); // a = 1.0f, b = 2.0f;
53 foo({"1"_arg = 2.0f, "0"_arg = 1.0f}); // a = 1.0f, b = 2.0f;
54 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
55
56 More complex forms are possible depending on the argument
57 defintions (see below).
58
59 To acess the argument values in the method, one can use:
60
61 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
62 args.template value<0>(); // zero-th argument.
63 args.template value<"a">(); // argument named "a"
64 args.value("a"_arg); // argument named "a"
65 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
66
67 Alternative, a helper macro is provided to create alias variables:
68
69 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
70 BBM_IMPORT_ARGS(args, a, b);
71 std::cerr << a << std::endl;
72 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
73
74 In the above case, an 'a' and 'b' alias is created to args.value("a"_arg)
75 and agrs.value("b"_arg).
76
77 Argument types can also be refernces (const and non-const):
78
79 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
80 foo(args<arg<float&, "a">, arg<const float&, "b">> args)
81 {
82 BBM_IMPORT_ARGS(args, a, b);
83 std::cerr << b << std::endl;
84 a = 10.0f;
85 }
86 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
87
88 In this case, "a" is a reference to a float, and setting "a = 10.0f" will
89 have the expected result that the value of the referenced variable is
90 affected outside the function body.
91
92 "b" is declared a const float& and supports the same functionality as in
93 regular C++ calls. I.e., when an rvalue is passed, its scope is extended as
94 long as 'args' scope is alive.
95
96 Arguments can also be given a default value with 'ArgDef':
97
98 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
99 foo(args<arg<const float&, "a", ArgDef(1.0f)>, arg<const float&, "b", ArgDef(2.0f)>> args)
100 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
101
102 In this case 'ArgDef' is macro that wraps the default value in a lambda
103 function. This means that the default value cannot depend on run-time
104 information.
105
106 Default values allow for other flexible method calling stratgies:
107
108 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
109 foo({}); // a = 1.0f, b = 2.0f
110 foo({10.0f}); // a = 10.0f, b = 2.0f
111 foo({"b"_arg = 11.f}); // a = 1.0f, b = 11.0f
112 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
113
114 The algorithm for assigning arguments (implemented below in _find_index_arg)
115 is that for each argument in args, the list of passed arguments is searched
116 for a argument that matches one of the following rules (in order of
117 precedence):
118
119 + Use the argument if it has a matching name (and a compatible type); or
120 + Use the argument if it has a numerical name that matches the position of the argument in args (and compatible type); or
121 + Use the argument at the same position in the list of arguments if it has no name and a compatible type; or
122 + The argument has a unique type and an argument with the same unique type is passed
123
124 Note: a compile error will occur when:
125 + Not all arguments passed can be assigned to an argument in args.
126 + An argument with an invalid name is passed.
127
128
129 An example of matching unique types is given below:
130
131 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
132 foo(args<arg<uniqueA, "a">, arg<uniqueB, "b">> args)
133 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
134
135 where 'uniqueA' and 'unqiueB' are unique types (i.e., uniqueA != uniqueB),
136 such as differnt enum_types. In this case one can call 'foo' as:
137
138 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
139 foo({uniqueA(), uniqueB()});
140 foo({uniqueB(), uniqueA()});
141 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
142
143 Given an args definition, two helper static constexpr are defined to check
144 whether a list of argument types is compatible:
145
146 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
147 using args_type = args<arg<float, "a">, arg<float, "b", ArgDef(1.0f)>>;
148 std::cerr << args_type::is_compatible<float, float> << std::endl; // true
149 std::cerr << args_type::is_compatible<float> << std::endl; // true
150 std::cerr << args_type::is_compatible<> << std::endl; // false
151 std::cerr << args_type::is_compatible<arg<float, "a">> << std::endl; // true
152 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
153
154 The helper 'is_cpp_compatible' returns true if the argument list is
155 compatible according to classic c++ argument passing rules.
156
157
158 To remove the curly brackets around the arguments, two helper macros are
159 given: one for the case where the method is declared with an 'args'
160 argument, and one where the method is declared in a classic C++ manner.
161
162 Case 1: the method 'foo' has an 'args' argument.
163
164 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
165 BBM_FORWARD_ARGS(foo, arg<float, "a">, arg<float, "b">);
166 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
167
168 forwards the call to 'foo' to the version with 'args':
169
170 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
171 foo("b"_arg = 2.0f, "0"_arg = 1.0f);
172 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
173
174 will forward to:
175
176 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
177 foo(args<arg<float, "a">, arg<float, "b">{"b"_arg = 2.0f, "0"_arg = 1.0f});
178 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
179
180 Note that the list of 'arg' needs to match the signature of the 'args' of
181 'foo' to forward too. A variant of this macro named
182 'BBM_FORWARD_ARGS_CONST' operates exactly the same except that it declares
183 the forward method as 'const'.
184
185 Case 2: the method 'foo' has classic C++ style arguments:
186
187 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
188 BBM_FORWARD_CPP_ARGS(foo, arg<float, "a">, arg<float, "b">)
189 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
190
191 thus exactly the same arguments as the previous case. Usage is exactly the
192 same too. The provided arg list must exactly match the types of the
193 original 'foo' method signature, _including_ any default values! If not,
194 the forwarding method will not be able to detect when to use the original
195 function (with C++ arguments) or when it needs to use an intermediate 'args'
196 forwarding call (i.e., using the is_cpp_compatible static constexpr).
197 Similar as with the previous case, 'BBM_FORWARD_CPP_ARGS_CONST' declares the
198 forwarding method as 'const'.
199
200 To forward constructor calls, two similar macros are defined:
201 + BBM_CONSTRUCTOR_FORWARD_ARGS
202 + BBM_CONSTRUCTOR_FORWARD_CPP_ARGS
203 which use exactly the same set of arguments as for regular functions and
204 operate in roughly the same way.
205
206*************************************************************************/
207namespace bbm {
208
209 //! \brief Forward declaration
210 template<typename... ARGS> requires (is_arg_v<ARGS> && ...) struct args;
211
212 /**********************************************************************/
213 /*! @{ \name Type Traits
214 **********************************************************************/
215 namespace detail {
216 template<typename T> struct is_args_impl : std::false_type {};
217 template<typename... Ts> struct is_args_impl<args<Ts...>> : std::true_type {};
218 }
219
220 template<typename T>
221 using is_args = detail::is_args_impl<std::decay_t<T>>;
222
223 template<typename T>
224 inline constexpr bool is_args_v = is_args<T>::value;
225 //! @}
226
227 /**********************************************************************/
228 /*! @{ \name Create a bbm::args type from either a list of bbm::arg or an bbm::args.
229 **********************************************************************/
230 namespace detail {
231 template<typename... Ts> struct add_args_impl;
232 template<typename... Ts> requires (is_arg_v<Ts> && ...) struct add_args_impl <Ts...> { using type = args<Ts...>; };
233 template<typename T> requires is_args_v<T> struct add_args_impl<T> { using type = T; };
234 }
235
236 template<typename... Ts>
237 using add_args = detail::add_args_impl<Ts...>;
238
239 template<typename... Ts>
240 using add_args_t = typename add_args<Ts...>::type;
241 //! @}
242
243 /**********************************************************************/
244 /*! \brief Main declaration of 'args'
245 **********************************************************************/
246 template<typename... ARGS> requires (is_arg_v<ARGS> && ...)
247 struct args
248 {
249 private:
250 //! @{ \name Helper forward functions
251 template<typename... Ts> static constexpr bool _is_compatible_fwd(void) { return _is_compatible<std::tuple<Ts...>>(); }
252 template<typename... Ts> static constexpr bool _is_cpp_compatible_fwd(void) { return _is_cpp_compatible<std::tuple<Ts...>>(); }
253 //! @}
254
255 public:
256 /********************************************************************/
257 /*! @{ \name compatibility checks of series of arguments with ARGS
258 ********************************************************************/
259 template<typename... Ts>
260 static constexpr bool is_compatible = _is_compatible_fwd<Ts...>();
261
262 template<typename... Ts>
263 static constexpr bool is_cpp_compatible = _is_cpp_compatible_fwd<Ts...>();
264 //! @}
265
266
267 /********************************************************************/
268 /*! @{ \name Constructors
269 *********************************************************************/
270 //! \brief Copy constructor for compatible arg_lists
271 template<typename... Ts> requires is_compatible<Ts...>
272 constexpr args(args<Ts...>&& arg) : _values( _retrieve_args(arg.values(), std::make_index_sequence<sizeof...(ARGS)>{}) ) {}
273
274 //! \brief Constructor from a compatible list of arguments
275 template<typename... Ts> requires is_compatible<Ts...>
276 constexpr args(Ts&&... arg) : _values( _retrieve_args(std::forward_as_tuple(std::forward<Ts>(arg)...), std::make_index_sequence<sizeof...(ARGS)>{}) ) {}
277 //! @}
278
279 /********************************************************************/
280 /*! @{ \name Inspectors
281 ********************************************************************/
282 static constexpr size_t size = sizeof...(ARGS);
283
284 //! \brief type of the IDX argument
285 template<size_t IDX> using type = std::tuple_element_t<IDX, std::tuple<ARGS...>>;
286
287 //! \brief name of the IDX argument
288 template<size_t IDX> static constexpr string_literal name = std::tuple_element_t<IDX, std::tuple<ARGS...>>::name;
289
290 //! \brief Returns a tuple of all arguments' values
291 inline constexpr auto values(void) const { return _value( std::make_index_sequence<sizeof...(ARGS)>{} ); }
292
293 //! \brief type of values()
294 using values_t = bbm::named< std::tuple<typename ARGS::type...>, ARGS::name... >;
295
296 //! \brief Get the IDX-th argument (bbm::arg type)
297 template<size_t IDX> inline constexpr decltype(auto) get(void) { return std::get<IDX>(_values); }
298 template<size_t IDX> inline constexpr decltype(auto) get(void) const { return std::get<IDX>(_values); }
299
300 //! \brief Get the value of the IDX-th argument
301 template<size_t IDX> inline constexpr decltype(auto) value(void) { return get<IDX>().value(); }
302 template<size_t IDX> inline constexpr decltype(auto) value(void) const { return get<IDX>().value(); }
303
304 //! \brief Get the argument matching a given name (bbm::arg type)
305 template<string_literal Name, typename T=void, typename D=void>
306 inline constexpr decltype(auto) get(arg<T,Name,D> = arg<T,Name,D>{})
307 {
308 const size_t idx = _lookup<Name>();
309 static_assert(idx < size, BBM_NO_MATCH);
310 return std::get<idx>(_values);
311 }
312
313 template<string_literal Name, typename T=void, typename D=void>
314 inline constexpr decltype(auto) get(arg<T,Name,D> = arg<T,Name,D>{}) const
315 {
316 const size_t idx = _lookup<Name>();
317 static_assert(idx < size, BBM_NO_MATCH);
318 return std::get<idx>(_values);
319 }
320
321 //! \brief Get the value of the argument with a given name
322 template<string_literal Name, typename T=void, typename D=void> inline constexpr decltype(auto) value(arg<T,Name,D> = arg<T,Name,D>{}) { return get<Name>().value(); }
323 template<string_literal Name, typename T=void, typename D=void> inline constexpr decltype(auto) value(arg<T,Name,D> = arg<T,Name,D>{}) const { return get<Name>().value(); }
324 //! @}
325
326 private:
327 /********************************************************************/
328 /*! \brief Lookup a name.
329
330 \param name = bbm::arg with name to look up
331 \returns the index of the matching arg (or size if failed)
332 *********************************************************************/
333 template<string_literal Name, typename T=void, typename D=void>
334 static inline constexpr size_t _lookup(arg<T, Name, D> = arg<T, Name, D>{})
335 {
336 size_t result = size;
337
338 CONSTFOR(idx, sizeof...(ARGS),
339 {
340 // matching name?
341 if constexpr (type<idx>::name == Name) result = idx;
342
343 // name matches "idx"?
344 if constexpr (to_string_literal<idx>() == Name) result = idx;
345 });
346
347 // Done (returns 'size' if fail)
348 return result;
349 }
350
351 /********************************************************************/
352 /*! \brief Populate the 'args' from a tuple of arguments
353
354 \param src = tuple of argument values
355 \param index_sequence [0, sizeof...(ARGS)]
356 \returns tuple of values
357 ********************************************************************/
358 template<typename TUP, size_t... IDX>
359 static inline constexpr decltype(auto) _retrieve_args(TUP&& src, std::index_sequence<IDX...>)
360 {
361 return std::make_tuple( _retrieve_arg<IDX>(std::forward<TUP>(src))... );
362 }
363
364 /********************************************************************/
365 /*! \brief Find in a tuple of arguments the one that best matches the 'IDX'-th ARG in ARGS
366
367 \tparam IDX = index in ARGS to find the best match
368 \param src = tuple of argument values
369 \return the matching ARG from TUP
370 *********************************************************************/
371 template<size_t IDX, typename TUP>
372 static inline constexpr decltype(auto) _retrieve_arg(TUP&& src)
373 {
374 using arg_type = type<IDX>;
375
376 // Find best matching element in TUP and returns its index
377 constexpr size_t idx = _find_arg_index<IDX, TUP>();
378
379 // if found, use value to create arg
380 if constexpr (idx != std::tuple_size_v<TUP>)
381 {
382 decltype(auto) arg = std::get<idx>( std::forward<TUP>(src) );
383 return arg_type(std::forward<decltype(arg)>(arg));
384 }
385
386 // else return empty (if available)
387 else if constexpr (std::is_constructible_v<arg_type>) return arg_type{};
388
389 // No match found and not default constructible => compile time error
390 else static_assert(dependent_false_v<TUP>, BBM_NO_MATCH);
391 }
392
393 /********************************************************************/
394 /*! \brief Checks if a tuple of arguments is compatible with ARGS
395
396 \tparam TUP = tuple of argument types
397 \returns true if the argument types in TUP form a valid initialization for ARGS
398 ********************************************************************/
399 template<typename TUP>
400 static inline constexpr bool _is_compatible(void)
401 {
402 bool compatible = true;
403 bool arg_used[ std::tuple_size_v<TUP> ];
404 std::fill_n(arg_used, std::tuple_size_v<TUP>, false);
405
406 // Check found argument in TUP for each ARGS
407 CONSTFOR(idx, sizeof...(ARGS),
408 {
409 const size_t argIdx = _find_arg_index<idx, TUP>();
410
411 // not found => check if default exists
412 if constexpr (argIdx >= std::tuple_size_v<TUP>)
413 {
414 if constexpr (!std::is_constructible_v<type<idx>>) compatible = false;
415 }
416
417 // check if already assigned before
418 else if(arg_used[ argIdx ]) compatible = false;
419
420 // found => mark as used
421 else arg_used[ argIdx ] = true;
422 });
423
424 // check if all arguments in TUP have been used
425 return compatible && std::all_of(arg_used, arg_used + std::tuple_size_v<TUP>, [](bool b) { return b; });
426 }
427
428
429 /********************************************************************/
430 /*! \brief Check if a tuple of arguments is compatible in a classic C++ sense with ARGS
431
432 \tparam TUP = tuple of argument types
433 \param index_sequence [0, sizeof...(ARGS)]
434 \returns true if the argument types in TUP form a valid initialization for ARGS
435 ********************************************************************/
436 template<typename TUP>
437 static inline constexpr bool _is_cpp_compatible(void)
438 {
439 bool compatible = true;
440 bool defaulted = false; // keeps track if a previous argument has been defaulted
441 bool arg_used[ std::tuple_size_v<TUP> ];
442 std::fill_n(arg_used, std::tuple_size_v<TUP>, false);
443
444 CONSTFOR(idx, sizeof...(ARGS),
445 {
446 const size_t found_idx = _find_arg_index<idx, TUP>();
447
448 // if found: no previous arg can be defaulted, and index must match, and arg element is !is_arg_v
449 if constexpr (found_idx != std::tuple_size_v<TUP>)
450 {
451 arg_used[ found_idx ] = true; // mark as used
452 if(defaulted) compatible = false;
453 if constexpr (found_idx != idx) compatible = false;
454 if constexpr (is_arg_v< std::tuple_element_t<found_idx, TUP> >) compatible = false;
455 }
456
457 // if not found: type<idx> must be default constructible
458 else
459 {
460 defaulted = true;
461 if constexpr (!std::is_constructible_v<type<idx>>) compatible = false;
462 }
463 });
464
465 // check if all arguments in TUP have been used
466 return compatible && std::all_of(arg_used, arg_used + std::tuple_size_v<TUP>, [](bool b) { return b; });
467 }
468
469 /********************************************************************/
470 /*! \brief Find the element in a tuple of argument values that best matches the IDX-th 'ARGS'
471
472 \tparam IDX = index in 'ARGS' to match
473 \tparam TUP = tuple of argument values
474 \returns the index in TUP of the best matching element; returns sizeof(TUP) is no match is found.
475
476 Matching procedure:
477 + Check for matching name and compatible type
478 + Check for matching "IDX" name in TUP with compatible type
479 + If TUP[IDX] is type compatible
480 + If ARGS[IDX] type is unique and there exists a unique matching type in TUP
481 + Else return sizeof<TUP> (fail)
482 *********************************************************************/
483 template<size_t IDX, typename TUP>
484 static inline constexpr size_t _find_arg_index(void)
485 {
486 size_t result = std::tuple_size_v<TUP>;
487 using arg_type = type<IDX>;
488
489 // if ARGS[IDX] has valid name => see if matching name exists in TUP (that is type compatible)
490 if constexpr (!arg_type::name.empty)
491 {
492 CONSTFOR(tupIdx, std::tuple_size_v<TUP>,
493 {
494 using tup_type = std::tuple_element_t<tupIdx, TUP>;
495
496 if constexpr (is_arg_v<tup_type>)
497 if constexpr (std::decay_t<tup_type>::name == arg_type::name)
498 if constexpr (std::convertible_to<typename std::decay_t<tup_type>::type, arg_type>) result = tupIdx;
499 });
500
501 // if found => terminate search
502 if(result != std::tuple_size_v<TUP>) return result;
503 }
504
505 // ELSE: check if there exists an argument in TUP with name "IDX" (and is type compatible)
506 CONSTFOR(tupIdx, std::tuple_size_v<TUP>,
507 {
508 using tup_type = std::tuple_element_t<tupIdx, TUP>;
509 if constexpr (is_arg_v<tup_type>)
510 if constexpr (std::decay_t<tup_type>::name == to_string_literal<IDX>())
511 if constexpr (std::convertible_to<typename std::decay_t<tup_type>::type, arg_type>) result = tupIdx;
512 });
513
514 // if found => terminate search
515 if(result != std::tuple_size_v<TUP>) return result;
516
517 // ELSE: check if TUP[IDX] and ARGS[IDX] are type compatible
518 if constexpr (IDX < std::tuple_size_v<TUP>)
519 {
520 using tup_type = std::tuple_element_t<IDX, TUP>;
521
522 // handle non-bbm::arg arguments in TUP
523 if constexpr (!is_arg_v<tup_type>)
524 {
525 if constexpr (std::convertible_to<tup_type, arg_type>) return IDX;
526 }
527
528 // argument in TUP is a bbm::arg (with an empty name!)
529 else if constexpr (std::decay_t<tup_type>::name.empty)
530 if constexpr (std::convertible_to<typename std::decay_t<tup_type>::type, arg_type>) return IDX;
531 }
532
533 // ELSE: if arg_type::type is unique, is there an exact matching (unique) type in TUP?
534 size_t arg_count = 0;
535 CONSTFOREACH(ARG, ARGS,
536 {
537 arg_count += std::is_same_v<std::decay_t<typename arg_type::type>, std::decay_t<typename ARG::type>>;
538 });
539
540 if(arg_count == 1) // if unique arg_type
541 {
542 size_t tup_count = 0;
543 size_t found_idx = std::tuple_size_v<TUP>;
544 CONSTFOR(tupIdx, std::tuple_size_v<TUP>,
545 {
546 using tup_type = std::tuple_element_t<tupIdx, TUP>;
547 if constexpr (std::is_same_v<std::decay_t<tup_type>, std::decay_t<typename arg_type::type>>)
548 {
549 tup_count++;
550 found_idx = tupIdx;
551 }
552 });
553
554 // if unique tup_type (that matches arg_type); return index
555 if(tup_count == 1 && found_idx < std::tuple_size_v<TUP>) return found_idx;
556 }
557
558 // ELSE: fail
559 return result;
560 }
561
562 /********************************************************************/
563 /*! \brief Return the value of all arguments
564
565 \param index_sequence = [0, sizeof(ARGS)]
566 \returns named tuple of argument values
567 ********************************************************************/
568 template<size_t... IDX>
569 inline constexpr auto _value(std::index_sequence<IDX...>) const
570 {
571 return make_named<name<IDX>...>(std::forward_as_tuple( value<IDX>()... ));
572 }
573
574 private:
575 ////////////////
576 // Member Data
577 ////////////////
578 std::tuple<ARGS...> _values;
579 };
580
581
582 /**********************************************************************/
583 /*! \brief Print bbm::args
584 **********************************************************************/
585 template<typename... ARGS> requires (bbm::is_arg_v<ARGS> && ...)
586 std::ostream& operator<<(std::ostream& s, const bbm::args<ARGS...>& args)
587 {
588 CONSTFOR(idx, sizeof...(ARGS),
589 {
590 if constexpr (idx > 0) s << ", ";
591 s << args.template get<idx>();
592 });
593 return s;
594 }
595
596} // end bbm namespace
597
598
599/************************************************************************/
600/*! \brief Macro for creating aliases of the arguments in args
601
602 \param ARGS = name of args
603 \param ... = comma separated list of argument names to import.
604*************************************************************************/
605#define BBM_IMPORT_ARGS(ARGS, ...) \
606 auto& _bbm_import_args = ARGS; \
607 BBM_FOREACH( _BBM_IMPORT_ARG_HELPER, __VA_ARGS__ ) \
608
609//! \brief Helper macro for BBM_IMPORT_ARGS
610#define _BBM_IMPORT_ARG_HELPER(ARGNAME) BBM_IMPORT_ARG(_bbm_import_args, ARGNAME)
611
612/************************************************************************/
613/*! \brief Macro for creating an alias for a single arg in args
614
615 \param ARGS = name of args
616 \param ARGNAME = name of the arg to import (without "")
617*************************************************************************/
618#define BBM_IMPORT_ARG(ARGS, ARGNAME) \
619 decltype(auto) ARGNAME = ARGS.value(#ARGNAME ## _arg); \
620
621
622/************************************************************************/
623/*! \brief Macro for forwarding parameters (without {}) to a method with args
624
625 \param NAME = method name
626 \param CTE = 'const' or empty to declare the method const.
627 \param ... = list of arg<> definitions (or a single args<> definition)
628
629 Limitation: the class cannot have a template parameter named _ArgTs
630*************************************************************************/
631#define _BBM_FORWARD_ARGS(NAME, CTE, ...) \
632 template<typename... _ArgTs> requires \
633 (bbm::add_args_t<__VA_ARGS__>::template is_compatible<_ArgTs...> && \
634 !(bbm::is_args_v<_ArgTs> && ...)) \
635 inline constexpr auto NAME(_ArgTs&&... t) CTE \
636 { \
637 return NAME(bbm::add_args_t<__VA_ARGS__>{std::forward<_ArgTs>(t)...}); \
638 } \
639
640//! \brief Forward arguments (without {}) to a method with args
641#define BBM_FORWARD_ARGS(NAME, ...) BBM_CALL(_BBM_FORWARD_ARGS, NAME, , __VA_ARGS__)
642
643//! \brief Forward arguments (without {}) to a _const_ method with args
644#define BBM_FORWARD_ARGS_CONST(NAME, ...) BBM_CALL(_BBM_FORWARD_ARGS, NAME, const, __VA_ARGS__)
645
646
647/************************************************************************/
648/*! \brief Macro for forwarding parameters via args to a regular CPP method.
649
650 \param NAME = method name
651 \param CTE = 'const' or empty to declare the method const.
652 \param ... = list of arg<> definitions (or a single args<> definition)
653
654 Limitation: the class cannot have a template parameter named _ArgTs
655*************************************************************************/
656#define _BBM_FORWARD_CPP_ARGS(NAME, CTE, ...) \
657 template<typename... _ArgTs> requires \
658 (!bbm::add_args_t<__VA_ARGS__>::template is_cpp_compatible<_ArgTs...> && \
659 bbm::add_args_t<__VA_ARGS__>::template is_compatible<_ArgTs...> && \
660 !(bbm::is_args_v<_ArgTs> && ...)) \
661 inline constexpr auto NAME(_ArgTs&&... t) CTE \
662 { \
663 auto launcher = [&]<typename ARGS, size_t... IDX>(ARGS&& args, std::index_sequence<IDX...>) { return NAME(args.template value<IDX>()...); }; \
664 return launcher(bbm::add_args_t<__VA_ARGS__>{std::forward<_ArgTs>(t)...}, std::make_index_sequence<bbm::add_args_t<__VA_ARGS__>::size>{}); \
665 } \
666
667//! \brief Forward arguments via args to a method with CPP args.
668#define BBM_FORWARD_CPP_ARGS(NAME, ...) BBM_CALL(_BBM_FORWARD_CPP_ARGS, NAME, , __VA_ARGS__)
669
670//! \brief Forward arguments via args to a _const_ method with CPP args.
671#define BBM_FORWARD_CPP_ARGS_CONST(NAME, ...) BBM_CALL(_BBM_FORWARD_CPP_ARGS, NAME, const, __VA_ARGS__)
672
673
674/************************************************************************/
675/*! \brief Macro for forwarding parameters (without {}) to a constructor with args
676
677 \param NAME = class name
678 \param ... = list of arg<> definitions (or a single args<> definition)
679
680 Remarks:
681 + Avoid hijacking the copy constructor by ignoring single arguments of the same type as the class
682 + A default (empty) constructor is created if all arg have an associated default value.
683
684 Limitation: the class cannot have a template parameter named _ArgTs
685*************************************************************************/
686#define _BBM_CONSTRUCTOR_FORWARD_ARGS(NAME, ...) \
687 template<typename... _ArgTs> requires \
688 (bbm::add_args_t<__VA_ARGS__>::template is_compatible<_ArgTs...> && \
689 !(bbm::is_args_v<_ArgTs> && ...) && \
690 !(sizeof...(_ArgTs) == 1 && (std::is_same_v<NAME, std::decay_t<_ArgTs>> && ...))) \
691 inline constexpr NAME(_ArgTs&&... t) \
692 : NAME(bbm::add_args_t<__VA_ARGS__>{std::forward<_ArgTs>(t)...}) {}\
693 \
694 template<typename=void> requires bbm::add_args_t<__VA_ARGS__>::template is_compatible<> \
695 inline constexpr NAME(void) : NAME( bbm::make_from<bbm::add_args_t<__VA_ARGS__>>()) {} \
696
697//! \brief Forward arguments (without {}) to a constructor with args
698#define BBM_CONSTRUCTOR_FORWARD_ARGS(...) BBM_CALL(_BBM_CONSTRUCTOR_FORWARD_ARGS, __VA_ARGS__)
699
700
701/************************************************************************/
702/*! \brief Macro for forwarding parameters (without {}) to a constructor with regular C++ args
703
704 \param NAME = class name
705 \param ... = list of arg<> definitions (or a single args<> definition)
706
707 Remarks:
708 + Avoid hijacking the copy constructor by ignoring single arguments of the same type as the class
709 + A helper constructor that takes args and an index_sequence is created.
710 + There is no need for a default constructor as this will be handled (if necessary) by the original constructor.
711
712 Limitation: the class cannot have a template parameter named _ArgTs or _ArgIdx
713*************************************************************************/
714#define _BBM_CONSTRUCTOR_FORWARD_CPP_ARGS(NAME, ...) \
715 template<size_t... _ArgIdx> requires (sizeof...(_ArgIdx) == bbm::add_args_t<__VA_ARGS__>::size) \
716 inline constexpr NAME(bbm::add_args_t<__VA_ARGS__>&& args, std::index_sequence<_ArgIdx...>) \
717 : NAME(args.template value<_ArgIdx>()...) {} \
718 \
719 template<typename... _ArgTs> requires \
720 (!bbm::add_args_t<__VA_ARGS__>::template is_cpp_compatible<_ArgTs...> && \
721 bbm::add_args_t<__VA_ARGS__>::template is_compatible<_ArgTs...> && \
722 !(bbm::is_args_v<_ArgTs> && ...) && \
723 !(sizeof...(_ArgTs) == 1 && (std::is_same_v<NAME, std::decay_t<_ArgTs>> && ...))) \
724 inline constexpr NAME(_ArgTs&&... t) \
725 : NAME(bbm::add_args_t<__VA_ARGS__>{std::forward<_ArgTs>(t)...}, std::make_index_sequence<bbm::args<__VA_ARGS__>::size>{}) {} \
726 \
727//! \brief Forward arguments (without {}) to a constructor with C++ args.
728#define BBM_CONSTRUCTOR_FORWARD_CPP_ARGS(...) BBM_CALL(_BBM_CONSTRUCTOR_FORWARD_CPP_ARGS, __VA_ARGS__)
729
730
731#endif /* _BBM_ARGS_H_ */
Structure to store an argument, with possibly a type, name and default value. Refer to args....
Complile-time for loop.
#define CONSTFOR(IDX, NUMITR,...)
HELPER MACRO.
Definition: constfor.h:63
Compile-time for each loop.
#define CONSTFOREACH(ITR_TYPE, TYPE_LIST,...)
HELPER MACRO.
Definition: constforeach.h:53
Predefined exceptions for common errors.
#define BBM_NO_MATCH
Definition: error.h:51
concept to check if a type has a valid string_converter.
General macro utilities.
Tools for making an object:
Definition: aggregatebsdf.h:29
detail::is_args_impl< std::decay_t< T > > is_args
Definition: args.h:221
std::ostream & operator<<(std::ostream &s, const BSDF &bsdf)
Definition: bsdf_base.h:138
detail::add_args_impl< Ts... > add_args
Definition: args.h:237
typename add_args< Ts... >::type add_args_t
Definition: args.h:240
constexpr bool is_args_v
Definition: args.h:224
Forward declaration of bbm::arg.
Definition: arg.h:27
Definition: named.h:325
Forward declaration.
Definition: args.h:248
static constexpr bool _is_compatible_fwd(void)
Definition: args.h:251
constexpr args(args< Ts... > &&arg)
Copy constructor for compatible arg_lists.
Definition: args.h:272
constexpr auto _value(std::index_sequence< IDX... >) const
Return the value of all arguments.
Definition: args.h:569
std::tuple_element_t< IDX, std::tuple< ARGS... > > type
type of the IDX argument
Definition: args.h:285
constexpr decltype(auto) get(void)
Get the IDX-th argument (bbm::arg type)
Definition: args.h:297
constexpr decltype(auto) value(void)
Get the value of the IDX-th argument.
Definition: args.h:301
static constexpr size_t _find_arg_index(void)
Find the element in a tuple of argument values that best matches the IDX-th 'ARGS'.
Definition: args.h:484
std::tuple< ARGS... > _values
Definition: args.h:578
static constexpr bool _is_compatible(void)
Checks if a tuple of arguments is compatible with ARGS.
Definition: args.h:400
constexpr decltype(auto) value(arg< T, Name, D >=arg< T, Name, D >{})
Get the value of the argument with a given name.
Definition: args.h:322
constexpr decltype(auto) value(void) const
Definition: args.h:302
static constexpr bool is_compatible
Definition: args.h:260
constexpr args(Ts &&... arg)
Constructor from a compatible list of arguments.
Definition: args.h:276
constexpr decltype(auto) get(arg< T, Name, D >=arg< T, Name, D >{})
Get the argument matching a given name (bbm::arg type)
Definition: args.h:306
static constexpr string_literal name
name of the IDX argument
Definition: args.h:288
constexpr decltype(auto) get(arg< T, Name, D >=arg< T, Name, D >{}) const
Definition: args.h:314
static constexpr decltype(auto) _retrieve_arg(TUP &&src)
Find in a tuple of arguments the one that best matches the 'IDX'-th ARG in ARGS.
Definition: args.h:372
static constexpr bool is_cpp_compatible
Definition: args.h:263
constexpr decltype(auto) value(arg< T, Name, D >=arg< T, Name, D >{}) const
Definition: args.h:323
static constexpr bool _is_cpp_compatible_fwd(void)
Definition: args.h:252
static constexpr bool _is_cpp_compatible(void)
Check if a tuple of arguments is compatible in a classic C++ sense with ARGS.
Definition: args.h:437
static constexpr size_t _lookup(arg< T, Name, D >=arg< T, Name, D >{})
Lookup a name.
Definition: args.h:334
static constexpr size_t size
Definition: args.h:282
constexpr decltype(auto) get(void) const
Definition: args.h:298
static constexpr decltype(auto) _retrieve_args(TUP &&src, std::index_sequence< IDX... >)
Populate the 'args' from a tuple of arguments.
Definition: args.h:359
constexpr auto values(void) const
Returns a tuple of all arguments' values.
Definition: args.h:291
named container
Definition: named.h:131
Definition: string_literal.h:16
convert other types to a string literal.
A wrapper for STL containers such as tuple, pair, and array. These containers force the programmer to...