ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TypeTraits.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file TypeTraits.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2019 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
11 #include <functional>
12 #include <string>
13 #include <type_traits>
14 
15 namespace Acts {
16 
17 namespace detail {
18 
50 struct nonesuch {
51  ~nonesuch() = delete;
52  nonesuch(nonesuch const&) = delete;
53  void operator=(nonesuch const&) = delete;
54 };
55 
64 template <class Default, class AlwaysVoid, template <class...> class Op,
65  class... Args>
66 struct detector {
67  using value_t = std::false_type;
68  using type = Default;
69 };
70 
77 template <class Default, template <class...> class Op, class... Args>
78 struct detector<Default, std::void_t<Op<Args...>>, Op, Args...> {
79  // Note that std::void_t is a C++17 feature
80  using value_t = std::true_type;
81  using type = Op<Args...>;
82 };
83 
84 } // namespace detail
85 
86 namespace concept {
87 
96  template <template <class...> class Op, class... Args>
97  using is_detected =
99 
106  template <template <class...> class Op, class... Args>
107  using detected_t =
108  typename detail::detector<detail::nonesuch, void, Op, Args...>::type;
109 
117  template <class Expected, template <class...> class Op, class... Args>
118  using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
119 
128  template <class To, template <class...> class Op, class... Args>
130  std::is_convertible<detected_t<Op, Args...>, To>;
131 
139  template <class Default, template <class...> class Op, class... Args>
140  using detected_or = detail::detector<Default, void, Op, Args...>;
141 
152  template <bool... Bs>
153  constexpr bool require = std::conjunction<std::bool_constant<Bs>...>::value;
154 
160  template <bool... Bs>
161  constexpr bool either = std::disjunction<std::bool_constant<Bs>...>::value;
162 
167  template <bool... Bs>
168  constexpr bool disallow = not require<Bs...>;
169 
175  template <template <class...> class Op, class... Args>
176  constexpr bool exists = is_detected<Op, Args...>::value;
177 
184  template <class To, template <class...> class Op, class... Args>
185  constexpr bool converts_to = is_detected_convertible<To, Op, Args...>::value;
186 
193  template <class Exact, template <class...> class Op, class... Args>
194  constexpr bool identical_to = is_detected_exact<Exact, Op, Args...>::value;
195 
205  template <typename T, typename R, template <class...> class M,
206  typename... Arguments>
207  constexpr bool has_method = M<T, R, Arguments...>::template tv<T>::value;
208 
216  template <typename T, template <class...> class M, typename V>
217  constexpr bool has_member = identical_to<V, M, T>;
218 
222 } // namespace concept
223 } // namespace Acts
224 
340 #define METHOD_TRAIT(trait_name, method_name) \
341  template <class T, typename R, typename... Arguments> \
342  struct trait_name { \
343  /* Meta function to check if a type has a const qualifier*/ \
344  /* (by stripping it and seeing if something changed */ \
345  template <typename T_> \
346  static constexpr bool is_const = \
347  not std::is_same_v<std::remove_const_t<T_>, T_>; \
348  \
349  /*These following meta-functions basically to this: they check whether or \
350  * not the actual function pointer extracted through `&T::method_name` can \
351  * be assigned to a prepared function pointer type with the given \
352  * signature. This checks the exact signature, and not just callability \
353  * nad validity of the expression. */ \
354  \
355  /* Meta function which constructs the right type to check a function \
356  * pointer, non-const version*/ \
357  template <typename T_, typename = int> \
358  struct fptr_meta { \
359  template <typename... Arguments_> \
360  using type = typename std::integral_constant< \
361  decltype(std::declval<T_>().method_name( \
362  std::declval<Arguments_>()...)) (T_::*)(Arguments_...), \
363  &T_::method_name>::value_type; \
364  }; \
365  \
366  /* Meta function which constructs the right type to check a function \
367  * pointer, const version*/ \
368  /* The `const` needs to be put in there in one specific spot, that's why \
369  * the metafunction is needed*/ \
370  template <typename T_> \
371  struct fptr_meta<T_, std::enable_if_t<is_const<T_>, int>> { \
372  template <typename... Arguments_> \
373  using type = typename std::integral_constant< \
374  decltype(std::declval<T_>().method_name( \
375  std::declval<Arguments_>()...)) (T_::*)(Arguments_...) const, \
376  &T_::method_name>::value_type; \
377  }; \
378  \
379  /* Helper on top of the function pointer metafunction */ \
380  template <typename T_, typename... Arguments_> \
381  using fptr_meta_t = typename fptr_meta<T_>::template type<Arguments_...>; \
382  \
383  /* Trait check for the qualifier and the return type of the function */ \
384  /* This does not check the const qualifier at all */ \
385  template <typename T_, typename... Arguments_> \
386  using qual_ret = decltype( \
387  std::declval<T_>().method_name(std::declval<Arguments_>()...)); \
388  \
389  /* The problem is this: while the above is fine with and without const, \
390  * and with and without exact argument type match, the assignment to the \
391  * function pointer fails hard if there is no method at all with the given \
392  * name. That is undesirable. The following first uses the expression \
393  * validity check to assert that there is in fact a method of the given \
394  * name, and only if that is the case, try to compile the function pointer \
395  * based signature check. That way, there is no hard failures, only \
396  * substitution failures and we're happy. */ \
397  template <typename T_, typename = int> \
398  struct tv { \
399  static constexpr bool value = false; \
400  }; \
401  template <typename T_> \
402  struct tv<T_, std::enable_if_t< \
403  is_detected_exact<R, qual_ret, T_, Arguments...>::value, \
404  int>> { \
405  /* This is only ever evaluate if the method exists!*/ \
406  static constexpr bool value = \
407  is_detected<fptr_meta_t, T, Arguments...>::value; \
408  }; \
409  }