MetaUtils.h
Go to the documentation of this file.
1 /**
2  * @addtogroup Utilities General utilities
3  * @brief General programming utilities.
4  *
5  * @{
6  */
7 /**
8  * @defgroup Metaprogramming General utilities for metaprogramming
9  * @brief General utilities for use with templates and metaprogramming.
10  */
11 /**
12  * @}
13  */
14 
15 /** ****************************************************************************
16  * @file larcorealg/CoreUtils/MetaUtils.h
17  * @brief Basic C++ metaprogramming utilities.
18  * @author Gianluca Petrillo (petrillo@fnal.gov)
19  * @date January 12, 2017
20  * @ingroup Metaprogramming
21  */
22 
23 #ifndef LARCOREALG_COREUTILS_METAUTILS_H
24 #define LARCOREALG_COREUTILS_METAUTILS_H
25 
26 
27 // C/C++ standard libraries
28 #include <array>
29 #include <string>
30 #include <string_view>
31 #include <memory> // std::addressof()
32 #include <functional> // std::reference_wrapper<>
33 #include <type_traits>
34 
35 
36 /**
37  * @namespace util
38  * @brief Namespace for general, non-LArSoft-specific utilities.
39  * @ingroup Utilities
40  */
41 namespace util {
42 
43 
44  //--- BEGIN MetaprogrammingBase ----------------------------------------------
45  /**
46  * @defgroup MetaprogrammingBase Simple utility traits
47  * @brief Simple traits for the implementation of other traits.
48  * @ingroup Metaprogramming
49  */
50  /// @{
51 
52  namespace details {
53 
54  /// Implementation detail of `staticDumpClassName()`.
55  template <typename T>
57 
58  } // namespace details
59 
60 
61  //----------------------------------------------------------------------------
62  /// Trait returning the very same type as in the template argument.
63  template <typename T>
64  struct self_type { using type = T; };
65 
66  /// The very same type as in the template argument.
67  template <typename T>
68  using self_t = typename self_type<T>::type;
69 
70 
71  //----------------------------------------------------------------------------
72  /**
73  * @brief A `std::false_type` with a template argument.
74  * @see util::always_true_type, util::always_false_v
75  *
76  * This type allows a `static_assert` to fail only when the template type it's
77  * in is being instantiated:
78  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
79  * template <typename T>
80  * struct MandatoryCustomizationPoint {
81  * static_assert(util::always_false_type<T>(), "You have to specialize!");
82  * };
83  *
84  * template <typename T>
85  * struct MandatoryCustomizationPoint<std::vector<T>> {
86  * using type = typename std::vector<T>::reference;
87  * };
88  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
89  * In this example, using `std::false_type` instead of
90  * `util::always_false_type` might have tripped the compiler to trigger the
91  * assertion failure even if the class is not instantiated.
92  */
93  template <typename>
94  struct always_false_type: public std::false_type {};
95 
96 
97  //----------------------------------------------------------------------------
98  /**
99  * @brief A templated constant, always false.
100  * @see util::always_false_type, util::always_true_v
101  *
102  * This constant allows a `static_assert` to fail only when the template type
103  * it's in is being instantiated:
104  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
105  * template <typename T>
106  * struct MandatoryCustomizationPoint {
107  * static_assert(util::always_false_v<T>, "You have to specialize!");
108  * };
109  *
110  * template <typename T>
111  * struct MandatoryCustomizationPoint<std::vector<T>> {
112  * using type = typename std::vector<T>::reference;
113  * };
114  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
115  * In this example, using `std::false_type` might have tipped the compiler to
116  * trigger the assertion failure even if the class is not instantiated.
117  */
118  template <typename>
119  constexpr bool always_false_v = false;
120 
121 
122  /**
123  * @brief A `std::true_type` with a template argument.
124  * @see util::always_false_type, util::always_true_v
125  *
126  * This is one way to allow to specialize for classes with a certain type:
127  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
128  * template <typename T, typename = void>
129  * class ReferenceTypeExtractor {
130  * static_assert(util::always_false_type<T>(), "Type has no reference!");
131  * };
132  *
133  * template <typename Cont>
134  * struct ReferenceTypeExtractor<
135  * Cont,
136  * std::enable_if_t
137  * <util::always_true_type<typename Cont::value_type>::value>
138  * >
139  * {
140  * using type = typename Cont::reference;
141  * };
142  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
143  */
144  template <typename>
145  struct always_true_type: public std::true_type {};
146 
147 
148  /**
149  * @brief A template constant always true.
150  * @see util::always_true_type, util::always_false_v
151  *
152  * This is one way to allow to specialize for classes with a certain type:
153  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
154  * template <typename T, typename = void>
155  * class ReferenceTypeExtractor {
156  * static_assert(util::always_false_v<T>, "Type has no reference!");
157  * };
158  *
159  * template <typename Cont>
160  * struct ReferenceTypeExtractor
161  * <Cont, std::enable_if_t<util::always_true_v<typename Cont::value_type>>>
162  * {
163  * using type = typename Cont::reference;
164  * };
165  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
166  */
167  template <typename>
168  constexpr bool always_true_v = true;
169 
170 
171  template <bool Value>
172  using bool_constant
173  [[deprecated("use `std::bool_constant` instead (`#include <type_traits>`")]]
174  = std::bool_constant<Value>;
175 
176  template <typename BoolTrait>
177  using negation
178  [[deprecated("use `std::bool_constant` instead (`#include <type_traits>`")]]
179  = std::negation<BoolTrait>;
180 
181  /// The negation of `std::is_same`.
182  template <typename A, typename B>
183  using is_not_same = std::negation<std::is_same<A, B>>;
184 
185 
186  //----------------------------------------------------------------------------
187  /**
188  * @brief Trait: index of the first occurrence of `T` among the specified
189  * `Types`, starting from the one with index `StartFrom`.
190  * @tparam T the type of check the presence of
191  * @tparam StartFrom number of `Types` that will be ignored
192  * @tparam Types the possible types `T` can match.
193  * @see `util::find_next_type`
194  *
195  * The value of the trait is the index of `T` within the specified list of
196  * `Types` (first type as index `0`).
197  * The first `StartFrom` `Types` are ignored, but still counted.
198  * The match is exact, as in `std::is_same`.
199  * If none of the `Types` exactly matches `T`, the trait value will be the
200  * number of types (i.e. `sizeof...(Types)`), which is the index after the
201  * last of the types.
202  *
203  * This is a integral trait (type `std::size_t`): use it as
204  * `std::integer_constant`.
205  */
206  template <typename T, std::size_t StartFrom, typename... Types>
208 
209  template <typename T, std::size_t StartFrom, typename... Types>
210  constexpr std::size_t find_next_type_v
211  = find_next_type<T, StartFrom, Types...>::value;
212 
213 
214  //----------------------------------------------------------------------------
215  /**
216  * @brief Trait: index of the first occurrence of `T` among the specified
217  * `Types`.
218  * @tparam T the type of check the presence of
219  * @tparam Types the possible types `T` can match.
220  * @see `util::find_next_type`
221  *
222  * The value of the trait is the index of `T` within the specified list of
223  * `Types` (first type as index `0`). The match is exact, as in
224  * `std::is_same`. If none of the `Types` exactly matches `T`, the trait value
225  * will be the number of types (i.e. `sizeof...(Types)`), which is the index
226  * after the last of the types.
227  *
228  * This is a integral trait (type `std::size_t`): use it as
229  * `std::integer_constant`.
230  */
231  template <typename T, typename... Types>
232  using find_type = find_next_type<T, 0U, Types...>;
233 
234 
235  /// Index of the first occurrence of `T` among the specified `Types`.
236  /// @see `util::find_type`
237  template <typename T, typename... Types>
238  constexpr std::size_t find_type_v = find_type<T, Types...>::value;
239 
240 
241  //----------------------------------------------------------------------------
242  /**
243  * @brief Trait: whether `T` is among the specified `Types`.
244  * @tparam T the type of check the presence of
245  * @tparam Types the possible types `T` can match.
246  *
247  * Matching is for the exact type, as in `std::is_same`.
248  *
249  * This is a boolean trait: use it as `std::bool_constant`.
250  */
251  template <typename T, typename... Types>
252  struct is_any_of;
253 
254  /// Whether `T` is among the specified `Types` (see `util::is_any_of`).
255  template <typename T, typename... Types>
256  constexpr bool is_any_of_v = is_any_of<T, Types...>::value;
257 
258 
259  //----------------------------------------------------------------------------
260  /// Whether `T` and `U` are the same type, after being applied `std::decay`.
261  template <typename T, typename U>
262  constexpr auto is_same_decay_v
263  = std::is_same_v<std::decay_t<T>, std::decay_t<U>>;
264 
265 
266  //----------------------------------------------------------------------------
267  // @{
268  /**
269  * @brief Helper to determine the type of a variable at compilation time.
270  * @tparam T type to be investigated
271  *
272  * It may be difficult to understand which type is being used in a failing
273  * static assertion or in some complicate metaprogramming code (is there any
274  * other kind?), especially when due to a compilation failure the code can't
275  * be run.
276  * This class is supposed to help by forcing the compiler to a halt, and it is
277  * devised so that the compiler should print in the error message what it
278  * thinks the type `T` is.
279  *
280  * An example of usage:
281  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
282  * #include "larcorealg/CoreUtils/MetaUtils.h"
283  *
284  * void f() {
285  * constexpr auto v = 5U - 6U; // which type is `v` of?
286  * util::staticDumpClassName(v);
287  * }
288  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
289  * For example, Clang 5.0.1 emits these errors:
290  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
291  * In file included from metatest.cpp:1:
292  * ./MetaUtils.h:217:7: error: static_assert failed "ClassNameStaticDumper<T>: look for T in the error message context"
293  * static_assert(
294  * ^
295  * ./MetaUtils.h:228:39: note: in instantiation of template class 'util::details::ClassNameStaticDumper<unsigned int>' requested here
296  * void staticDumpClassName() { (void) details::ClassNameStaticDumper<T>(); }
297  * ^
298  * ./MetaUtils.h:195:46: note: in instantiation of function template specialization 'util::staticDumpClassName<unsigned int>' requested here
299  * [[noreturn]] void staticDumpClassName(T) { staticDumpClassName<T>(); }
300  * ^
301  * metatest.cpp:5:16: note: in instantiation of function template specialization 'util::staticDumpClassName<unsigned int>' requested here
302  * (void) util::staticDumpClassName(v);
303  * ^
304  * 1 error generated.
305  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
306  * From the first "note" we can see that the type of `v` is `unsigned int`.
307  * Note that if `v` is not copiable, an additional error will be emitted.
308  * To avoid that, the type can be specified as template parameter, as in
309  * `util::staticDumpClassName<decltype(v)>()`.
310  * The same program is processed by GNU GCC with an error message:
311  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
312  * In file included from metatest.cpp:1:0:
313  * MetaUtils.h: In instantiation of ‘struct util::details::ClassNameStaticDumper<unsigned int>’:
314  * MetaUtils.h:248:48: required from ‘void util::staticDumpClassName() [with T = unsigned int]’
315  * MetaUtils.h:215:68: required from ‘void util::staticDumpClassName(T) [with T = unsigned int]’
316  * metatest.cpp:5:30: required from here
317  * MetaUtils.h:237:7: error: static assertion failed: ClassNameStaticDumper<T>: look for T in the error message context
318  * static_assert(
319  * ^~~~~~~~~~~~~
320  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
321  * where the type is mentioned in all the three context lines.
322  */
323  template <typename T>
324  void staticDumpClassName();
325 
326  template <typename T>
327  void staticDumpClassName(T) { staticDumpClassName<T>(); }
328  // @}
329 
330 
331  /// @}
332  //--- END MetaprogrammingBase ------------------------------------------------
333 
334 
335  //--- BEGIN Type identification ----------------------------------------------
336  /**
337  * @defgroup MetaprogrammingTypeIdentification Determination of specific types
338  * @brief Traits to identify specific types.
339  * @ingroup Metaprogramming
340  */
341  /// @{
342 
343  //----------------------------------------------------------------------------
344 
345  /**
346  * @brief Trait describing whether `T` is a template instance of `Template`.
347  * @tparam Template template class to be detected
348  * @tparam T type to be tested
349  *
350  * This trait is true if the type `T` is an instance of template class
351  * `Template`, that is, if `T` is `Template<...>`, with the ellipsis
352  * represents any template argument. Before being tested, `T` is stripped
353  * of reference and constantness qualifiers, so that for example the answer
354  * will be the same for `std::vector<int>` as for `std::vector<int> const`,
355  * `std::vector<int> volatile&`, etc.
356  *
357  * @bug The limitation of this implementation is that only `Template` types
358  * taking only type arguments can be used. For example, attempting to
359  * use it with `std::array`, which contains a non-type argument (of type
360  * `std::size_t`), will cause a compilation error. For example, GCC 7.2
361  * reports:
362  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
363  * error: type/value mismatch at argument 1 in template parameter list for ‘template<template<class ...> class Template, class T> constexpr const bool util::is_instance_of_v<Template, T>’
364  * static_assert(util::is_instance_of_v<std::array, std::array<int, 2U>>);
365  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
366  *
367  */
368  template <template <typename...> typename Template, typename T>
370 
371  /// A constant describing whether `T` is a template instance of `Template`.
372  /// @see `util::is_instance_of`
373  template <template <typename...> typename Template, typename T>
375 
376 
377  //----------------------------------------------------------------------------
378  /**
379  * @brief Identifies whether the specified type is a STL array.
380  * @tparam T the type to be tested
381  * @see `util::is_STLarray_v`
382  */
383  template <typename T>
384  struct is_STLarray;
385 
386  /**
387  * @brief A constant describing whether the specified type is a STL array.
388  * @tparam T the type to be tested
389  * @see `util::is_STLarray`
390  */
391  template <typename T>
393 
394 
395  //----------------------------------------------------------------------------
396  /**
397  * @brief Identifies whether the specified type is a `std::reference_wrapper`.
398  * @tparam T the type to be tested
399  * @see `util::is_reference_wrapper_v`
400  */
401  template <typename T>
403 
404  /**
405  * @brief A constant describing whether the specified type is a
406  * `std::reference_wrapper`.
407  * @tparam T the type to be tested
408  * @see `util::is_reference_wrapper`
409  */
410  template <typename T>
412 
413 
414  //----------------------------------------------------------------------------
415  /**
416  * @brief Identifies whether the specified type is a `std::unique_ptr`.
417  * @tparam T the type to be tested
418  * @see `util::is_unique_ptr_v`
419  */
420  template <typename T>
422 
423  /**
424  * @brief A constant describing whether the specified type is a
425  * `std::unique_ptr`.
426  * @tparam T the type to be tested
427  * @see `util::is_unique_ptr`
428  */
429  template <typename T>
431 
432 
433  //----------------------------------------------------------------------------
434  /**
435  * @brief Trait: whether type `T` is a character type.
436  * @tparam T the type to be tested
437  *
438  * Character types are `char` (in all its sign options), `wchar_t`, `char32_t`
439  * and `char16_t`, in any combination of constantness and volatility.
440  * References to types yield the same value as the types they reference.
441  */
442  template <typename T>
444 
445  /// Whether type `T` is a character type (see `util::is_character_type`).
446  template <typename T>
448 
449 
450  //----------------------------------------------------------------------------
451  /**
452  * @brief Trait: whether type `T` is a character string type.
453  * @tparam T the type to be tested
454  * @see `util::is_character_type`
455  *
456  * In this definition, any container of character types is a string.
457  * A container is defined as a type having a `value_type` member.
458  * Also, C-style arrays and pointers to characters are considered strings.
459  * Reference types yield the same value as their referenced type.
460  */
461  template <typename T>
463 
464  /// Whether type `T` is a character string type (see `util::is_string_type`).
465  template <typename T>
467 
468 
469  //----------------------------------------------------------------------------
470  /**
471  * @brief Trait: whether type `T` is a STL string type.
472  * @tparam T the type to be tested
473  *
474  * This trait has `value` `true` if `T` is an instance of `std::basic_string`
475  * template, `false` otherwise.
476  */
477  template <typename T>
479 
480  /// Whether type `T` is a character string type
481  /// (see `util::is_basic_string_type`).
482  template <typename T>
484 
485 
486  //----------------------------------------------------------------------------
487  /**
488  * @brief Trait: whether type `T` is a `std::string_view` type.
489  * @tparam T the type to be tested
490  *
491  * This trait has `value` `true` if `T` is an instance of
492  * `std::basic_string_view` template, `false` otherwise.
493  */
494  template <typename T>
496 
497  /// Whether type `T` is a character string type
498  /// (see `util::is_basic_string_view_type`).
499  template <typename T>
500  constexpr bool is_basic_string_view_type_v
502 
503 
504  /// @}
505  //--- END Type identification ------------------------------------------------
506 
507 
508  //--- BEGIN Type manipulation ------------------------------------------------
509  /**
510  * @defgroup MetaprogrammingTypeManipulation Manipulation of types
511  * @brief Traits to change types.
512  * @ingroup Metaprogramming
513  */
514  /// @{
515 
516  //----------------------------------------------------------------------------
517  /**
518  * @brief Trait with type `Base`, plus the constantness as in `Key`.
519  * @tparam Base the basic type being returned
520  * @tparam Key a type expressing the constantness wanted for `Base`
521  *
522  * The `type` member of this trait is:
523  * * the type `Base` with constantness removed, if `Key` is non-constant type
524  * * the type `Base` with constantness added, if `Key` is a constant type
525  *
526  * This trait passes through references. Both `Base` and `Key` are treated as
527  * they were no references (e.g. a `int const&` is treated as a `int const`).
528  * The referenceness of the type in the trait is the same as the one of
529  * `Base`.
530  *
531  * Therefore, for example:
532  * * `with_const_as<int const, double>` yields `int`;
533  * * `with_const_as<int const, double const>` yields `int const`;
534  * * `with_const_as<int const&, double>` yields `int&`;
535  * * `with_const_as<int, double const&>` yields `int const`.
536  *
537  * R-value references are likewise taken into account.
538  */
539  template <typename Base, typename Key>
541 
542  /**
543  * @brief The type `Base`, plus the constantness as in `Key`.
544  * @tparam Base the basic type being returned
545  * @tparam Key a type expressing the constantness wanted for `Base`
546  * @see `util::with_const_as`
547  */
548  template <typename Base, typename Key>
550 
551 
552  //----------------------------------------------------------------------------
553  /**
554  * @brief Trait with type `T` stripped of all known reference types.
555  * @tparam T type to remove referenceness from
556  *
557  * In addition of the standard C++ references, this trait also removes all
558  * pseudo-referenceness known (to it).
559  * That currently includes:
560  * * C++ l-value and r-value reference
561  * * `std::reference_wrapper`
562  *
563  * The implementation removes all references recursively.
564  */
565  template <typename T>
567 
568  /**
569  * @brief The type `T` stripped of all known reference types.
570  * @tparam T type to remove referenceness from
571  * @see `util::strip_referenceness_type`
572  */
573  template <typename T>
575 
576 
577  //----------------------------------------------------------------------------
578  /**
579  * @brief Returns the address of the referenced object.
580  * @tparam Ref type of reference
581  * @param ref reference
582  * @return a pointer to the referenced object
583  *
584  * This function also manages `std::reference_wrapper` arguments, by returning
585  * the address of the object they reference.
586  * In all other cases, the return value is simply `std::addressof(obj)`.
587  */
588  template <typename Ref>
589  auto referenced_address(Ref&& ref);
590 
591 
592  //----------------------------------------------------------------------------
593  /**
594  * @brief Functor applying the proper `referenced_address()` function.
595  * @see `referenced_address()`
596  *
597  * This class operates in the same way as `util::referenced_address()`, but it
598  * is easier to use in STL algorithms since it's not a template:
599  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
600  * std::vector<int> data(4U, 0);
601  * std::vector<int const*> dataPtr;
602  * std::transform(data.cbegin(), data.cend(), std::back_inserter(dataPtr),
603  * util::reference_addresser());
604  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
605  * will fill `dataPtr` with the pointers to the elements in `data`.
606  * This is easier than taking the address of the correct template instance of
607  * `util::referenced_address()`, or than writing a lambda function for that.
608  */
610  template <typename Ref>
611  decltype(auto) operator() (Ref&& ref) const
612  { return addressof(std::forward<Ref>(ref)); }
613 
614  template <typename Ref>
615  static decltype(auto) addressof(Ref&& ref)
616  { return referenced_address(std::forward<Ref>(ref)); }
617 
618  }; // struct reference_addresser
619 
620 
621  //----------------------------------------------------------------------------
622  /**
623  * @brief Trait with type `T` into `std::reference_wrapper` if reference.
624  * @tparam T type to be wrapped
625  * @see `util::lvalue_reference_into_wrapper()`
626  *
627  * If the argument type `T` is a l-value reference, the corresponding `type`
628  * will be a `std::reference_wrapper` object wrapping the type `T` references
629  * (constantness will be preserved).
630  * If the argument is already a `std::reference_wrapper` (possibly constant,
631  * possibly any reference), no action is taken and `type` is the same as `T`
632  * except for no reference.
633  * Otherwise, `type` will be `T` itself with any reference removed.
634  */
635  template <typename T>
637 
638  /**
639  * @brief The type `T` stripped of all known reference types.
640  * @tparam T type to remove referenceness from
641  * @see `util::lvalue_reference_into_wrapper_type`
642  */
643  template <typename T>
646 
647 
648  /**
649  * @brief Converts a l-value reference object into a `std::reference_wrapper`.
650  * @tparam T type of the object to be converted
651  * @param obj object to be converted
652  * @return either `obj` or a `std::reference_wrapper` around it
653  * @see `util::lvalue_reference_into_wrapper_type`
654  *
655  * This function operates on a fashion similar to
656  * `util::lvalue_reference_into_wrapper_type`, but it performs the conversion
657  * of an instantiated object rather than just reporting a type.
658  */
659  template <typename T>
662 
663 
664  //----------------------------------------------------------------------------
665 
666 
667  /// @}
668  //--- END Type manipulation --------------------------------------------------
669 
670 
671 } // namespace util
672 
673 
674 //------------------------------------------------------------------------------
675 //--- Template implementation
676 //------------------------------------------------------------------------------
677 namespace util {
678 
679  //----------------------------------------------------------------------------
680  namespace details {
681 
682  //--------------------------------------------------------------------------
683  template
684  <std::size_t Index, std::size_t Skip, typename T, typename... Types>
686 
687  template <
688  std::size_t Index, std::size_t Skip,
689  typename T,
690  typename Type, typename... Others
691  >
692  struct find_type_impl<Index, Skip, T, Type, Others...>
693  : std::integral_constant<std::size_t,
694  (Skip == 0) && std::is_same_v<T, Type>
695  ? Index
696  : find_type_impl<Index + 1U, ((Skip > 0U)? Skip - 1U: 0U), T, Others...>
697  ::value
698  >
699  {};
700 
701  template <std::size_t Index, std::size_t Skip, typename T>
702  struct find_type_impl<Index, Skip, T>
703  : std::integral_constant<std::size_t, Index>
704  {};
705 
706 
707  //--------------------------------------------------------------------------
708  template <typename T, typename = void>
710  util::is_any_of_v<
711  std::decay_t<T>,
712  signed char,
713  unsigned char,
714  char,
715  wchar_t,
716 #ifdef __cpp_char8_t // C++20
717  char8_t,
718 #endif // __cpp_char8_t
719  char16_t, // this is defined unsigned
720  char32_t // this is defined unsigned
721  >>
722  {};
723 
724 
725  //--------------------------------------------------------------------------
726  template <typename T, typename = void>
727  struct is_string_type_impl: std::false_type {};
728 
729  template <typename T>
731  T,
732  std::enable_if_t<is_character_type_impl<typename T::value_type>::value>
733  >
734  : std::true_type
735  {};
736 
737  template <typename T>
739  T,
740  std::enable_if_t<
741  std::is_pointer_v<std::decay_t<T>>
742  && is_character_type_impl<std::remove_pointer_t<std::decay_t<T>>>::value
743  >
744  >
745  : std::true_type
746  {};
747 
748  template <typename T>
750  T,
751  std::enable_if_t<
752  std::is_array_v<std::decay_t<T>>
753  && is_character_type_impl<std::remove_extent_t<std::decay_t<T>>>::value
754  >
755  >
756  : std::true_type
757  {};
758 
759 
760  //--------------------------------------------------------------------------
761  template <typename T>
762  struct is_basic_string_type_impl: std::false_type {};
763 
764 
765  template <typename... Args>
766  struct is_basic_string_type_impl<std::basic_string<Args...>>
767  : std::true_type
768  {};
769 
770 
771  //--------------------------------------------------------------------------
772  template <typename T>
773  struct is_basic_string_view_type_impl: std::false_type {};
774 
775 
776  template <typename... Args>
777  struct is_basic_string_view_type_impl<std::basic_string_view<Args...>>
778  : std::true_type
779  {};
780 
781 
782  //--------------------------------------------------------------------------
783  /// Implementation detail of `staticDumpClassName()`.
784  template <typename T>
785  struct ClassNameStaticDumper {
786  static_assert(
788  "ClassNameStaticDumper<T>: look for T in the error message context"
789  );
790  }; // struct ClassNameStaticDumper
791 
792  //--------------------------------------------------------------------------
793  // implementation for `with_const_as`
794 
795  // - final implementation:
796  template <typename Base, typename /* Key */, typename = void>
798  { using type = std::remove_const_t<Base>; };
799 
800  template <typename Base, typename Key>
802  <Base, Key, std::enable_if_t<std::is_const_v<Key>>>
803  { using type = std::add_const_t<Base>; };
804 
805  // - implementation dispatcher for reference types
806  // - pass through for not-reference types
807  template <typename Base, typename Key, typename = void>
809  // - lvalue reference
810  template <typename Base, typename Key>
812  <Base, Key, std::enable_if_t<std::is_lvalue_reference_v<Base>>>
813  {
814  using type = std::add_lvalue_reference_t
816  };
817  // - rvalue reference
818  template <typename Base, typename Key>
820  <Base, Key, std::enable_if_t<std::is_rvalue_reference_v<Base>>>
821  {
822  using type = std::add_rvalue_reference_t
824  };
825 
826  // - key management
827  template <typename Base, typename Key>
829  : with_const_as_dispatch_ref<Base, std::remove_reference_t<Key>> {};
830 
831  // - top level implementation dispatcher
832  template <typename Base, typename Key>
834 
835 
836  //--------------------------------------------------------------------------
837  //--- implementation of `is_instance_of`
838  template <template <typename...> typename Template, typename T>
839  struct is_instance_of_impl: std::false_type {};
840 
841 
842  template <template <typename...> typename Template, typename... Args>
843  struct is_instance_of_impl<Template, Template<Args...>>: std::true_type {};
844 
845 
846  //--------------------------------------------------------------------------
847  //--- implementation of `strip_referenceness_type`
848 
849  template <typename T>
851 
852  // implementation layer dealing with `std::reference_wrapper`
853  template <typename T, typename = void>
855  { using type = T; }; // exit here
856 
857  // - handle any constantness and volatility
858  template <typename T>
860  T,
861  std::enable_if_t<util::is_reference_wrapper_v<std::remove_cv_t<T>>>
862  >
863  : strip_referenceness_type_impl<typename T::type> // back to square one
864  {};
865 
866  // implementation layer dealing with C++ references
867  template <typename T>
870 
871  template <typename T>
874 
875  template <typename T>
878 
879  // entry point: start by dealing with C++ references
880  template <typename T>
882  {};
883 
884 
885  //--------------------------------------------------------------------------
886  //--- implementation of `referenced_address()`
887 
888  template <typename T, typename = void>
890  static auto addressof(T& obj) { return std::addressof(obj); }
891  };
892 
893  template <typename T>
895  <T, std::enable_if_t<util::is_reference_wrapper_v<T>>>
896  {
897  static auto addressof(T& obj) { return std::addressof(obj.get()); }
898  };
899 
900 
901  //--------------------------------------------------------------------------
902  //--- implementation of `lvalue_reference_into_wrapper_type`
903  /*
904  * The implementation develops across levels:
905  * 1. any kind of `std::reference_wrapper` is handled
906  * 2. l-value references are handled
907  */
908  template <typename T>
910  using type = std::remove_reference_t<T>;
911  };
912 
913  template <typename T>
915  using type = std::reference_wrapper<T>;
916  };
917 
918  template <typename T, typename = void>
920  using type
922  };
923 
924  template <typename T>
926  <T, std::enable_if_t<util::is_reference_wrapper_v<T>>>
927  {
928  using type = std::remove_reference_t<T>;
929  };
930 
931  template <typename T>
934  {};
935 
936 
937  //--------------------------------------------------------------------------
938 
939 
940  } // namespace details
941 
942 
943  //----------------------------------------------------------------------------
944  template <template <typename...> typename Template, typename T>
945  struct is_instance_of
946  : details::is_instance_of_impl<Template, std::decay_t<T>>
947  {};
948 
949  //----------------------------------------------------------------------------
950  template <typename T, std::size_t StartFrom, typename... Types>
951  struct find_next_type: details::find_type_impl<0U, StartFrom, T, Types...> {};
952 
953 
954  //----------------------------------------------------------------------------
955  template <typename T, typename... Types>
956  struct is_any_of:
957  std::bool_constant<((find_type_v<T, Types...>) < sizeof...(Types))>
958  {};
959 
960 
961  //----------------------------------------------------------------------------
962  template <typename T>
963  struct is_character_type: details::is_character_type_impl<T> {};
964 
965 
966  //----------------------------------------------------------------------------
967  template <typename T>
968  struct is_string_type: details::is_string_type_impl<T> {};
969 
970 
971  //----------------------------------------------------------------------------
972  template <typename T>
973  struct is_basic_string_type
974  : details::is_basic_string_type_impl<std::decay_t<T>> {};
975 
976 
977  //----------------------------------------------------------------------------
978  template <typename T>
979  struct is_basic_string_view_type
980  : details::is_basic_string_view_type_impl<std::decay_t<T>>
981  {};
982 
983 
984  //----------------------------------------------------------------------------
985  template <typename T>
986  void staticDumpClassName() { (void) details::ClassNameStaticDumper<T>(); }
987 
988  //----------------------------------------------------------------------------
989  template <typename>
990  struct is_STLarray: public std::false_type {};
991 
992  template <typename T, std::size_t N>
993  struct is_STLarray<std::array<T, N>>: public std::true_type {};
994 
995  //----------------------------------------------------------------------------
996  template <typename Base, typename Key>
997  struct with_const_as: public details::with_const_as_dispatcher<Base, Key> {};
998 
999  //----------------------------------------------------------------------------
1000  template <typename T>
1001  struct strip_referenceness_type
1002  : public details::strip_referenceness_type_impl<T>
1003  {};
1004 
1005  //----------------------------------------------------------------------------
1006  template <typename T>
1007  struct lvalue_reference_into_wrapper_type
1008  : public details::lvalue_reference_into_wrapper_type_impl<T>
1009  {};
1010 
1011  //----------------------------------------------------------------------------
1012  template <typename Ref>
1013  auto referenced_address(Ref&& ref)
1014  { return details::referenced_address_impl<Ref>::addressof(ref); }
1015 
1016  //----------------------------------------------------------------------------
1017 
1018 } // namespace util
1019 
1020 
1021 //------------------------------------------------------------------------------
1022 
1023 #endif // LARCOREALG_COREUTILS_METAUTILS_H
constexpr bool is_string_type_v
Whether type T is a character string type (see util::is_string_type).
Definition: MetaUtils.h:466
std::add_rvalue_reference_t< typename with_const_as_impl< std::remove_reference_t< Base >, Key >::type > type
Definition: MetaUtils.h:823
Implementation detail of staticDumpClassName().
Definition: MetaUtils.h:56
typename strip_referenceness_type< T >::type strip_referenceness_t
The type T stripped of all known reference types.
Definition: MetaUtils.h:574
Namespace for general, non-LArSoft-specific utilities.
Functor applying the proper referenced_address() function.
Definition: MetaUtils.h:609
constexpr bool always_true_v
A template constant always true.
Definition: MetaUtils.h:168
Abstract interface for a template.
Definition: template.h:542
#define Template
Definition: declinfo.cpp:738
Identifies whether the specified type is a STL array.
Definition: MetaUtils.h:384
int Type
Definition: 018_def.c:12
constexpr auto is_same_decay_v
Whether T and U are the same type, after being applied std::decay.
Definition: MetaUtils.h:263
constexpr bool is_basic_string_type_v
Definition: MetaUtils.h:483
typename lvalue_reference_into_wrapper_type_impl_final< T >::type type
Definition: MetaUtils.h:921
constexpr std::size_t find_next_type_v
Definition: MetaUtils.h:211
STL namespace.
typename with_const_as< Base, Key >::type with_const_as_t
The type Base, plus the constantness as in Key.
Definition: MetaUtils.h:549
unsigned int Index
std::integral_constant< bool, Value > bool_constant
Definition: ProviderPack.h:314
Trait with type Base, plus the constantness as in Key.
Definition: MetaUtils.h:540
typename self_type< T >::type self_t
The very same type as in the template argument.
Definition: MetaUtils.h:68
A std::false_type with a template argument.
Definition: MetaUtils.h:94
constexpr bool is_reference_wrapper_v
A constant describing whether the specified type is a std::reference_wrapper.
Definition: MetaUtils.h:411
Trait: whether type T is a character string type.
Definition: MetaUtils.h:462
std::negation< std::is_same< A, B >> is_not_same
The negation of std::is_same.
Definition: MetaUtils.h:183
Trait: whether type T is a character type.
Definition: MetaUtils.h:443
Trait returning the very same type as in the template argument.
Definition: MetaUtils.h:64
constexpr bool always_false_v
A templated constant, always false.
Definition: MetaUtils.h:119
constexpr bool is_basic_string_view_type_v
Definition: MetaUtils.h:501
constexpr bool is_unique_ptr_v
A constant describing whether the specified type is a std::unique_ptr.
Definition: MetaUtils.h:430
constexpr bool is_character_type_v
Whether type T is a character type (see util::is_character_type).
Definition: MetaUtils.h:447
A std::true_type with a template argument.
Definition: MetaUtils.h:145
void staticDumpClassName()
Helper to determine the type of a variable at compilation time.
Definition: MetaUtils.h:986
auto lvalue_reference_into_wrapper(T &&obj)
Converts a l-value reference object into a std::reference_wrapper.
Definition: MetaUtils.h:660
Trait with type T stripped of all known reference types.
Definition: MetaUtils.h:566
Trait: index of the first occurrence of T among the specified Types, starting from the one with index...
Definition: MetaUtils.h:207
constexpr std::size_t find_type_v
Definition: MetaUtils.h:238
Trait describing whether T is a template instance of Template.
Definition: MetaUtils.h:369
constexpr bool is_any_of_v
Whether T is among the specified Types (see util::is_any_of).
Definition: MetaUtils.h:256
std::add_lvalue_reference_t< typename with_const_as_impl< std::remove_reference_t< Base >, Key >::type > type
Definition: MetaUtils.h:815
Trait: whether type T is a STL string type.
Definition: MetaUtils.h:478
Trait: whether T is among the specified Types.
Definition: MetaUtils.h:252
Trait with type T into std::reference_wrapper if reference.
Definition: MetaUtils.h:636
Trait: whether type T is a std::string_view type.
Definition: MetaUtils.h:495
auto referenced_address(Ref &&ref)
Returns the address of the referenced object.
Definition: MetaUtils.h:1013
constexpr bool is_STLarray_v
A constant describing whether the specified type is a STL array.
Definition: MetaUtils.h:392
typename lvalue_reference_into_wrapper_type< T >::type lvalue_reference_into_wrapper_t
The type T stripped of all known reference types.
Definition: MetaUtils.h:645
constexpr bool is_instance_of_v
Definition: MetaUtils.h:374