EIC Software
Reference for
EIC
simulation and reconstruction software on GitHub
|
#include <functional>
#include <string>
#include <type_traits>
Go to the source code of this file.
Classes | |
struct | Acts::detail::nonesuch |
struct | Acts::detail::detector< Default, AlwaysVoid, Op, Args > |
struct | Acts::detail::detector< Default, std::void_t< Op< Args...> >, Op, Args...> |
Namespaces | |
namespace | Acts |
Set the Geometry Context PLUGIN. | |
namespace | Acts::detail |
These functions perform the transport of a covariance matrix using given Jacobians. The required data is provided by the stepper object with some additional data. Since this is a purely algebraic problem the calculations are identical for StraightLineStepper and EigenStepper . As a consequence the methods can be located in a seperate file. | |
namespace | Acts::Concepts |
Macros | |
#define | METHOD_TRAIT(trait_name, method_name) |
Typedefs | |
template<template< class...> class Op, class... Args> | |
using | Acts::Concepts::is_detected = typename detail::detector< detail::nonesuch, void, Op, Args...>::value_t |
template<template< class...> class Op, class... Args> | |
using | Acts::Concepts::detected_t = typename detail::detector< detail::nonesuch, void, Op, Args...>::type |
template<class Expected , template< class...> class Op, class... Args> | |
using | Acts::Concepts::is_detected_exact = std::is_same< Expected, detected_t< Op, Args...>> |
template<class To , template< class...> class Op, class... Args> | |
using | Acts::Concepts::is_detected_convertible = std::is_convertible< detected_t< Op, Args...>, To > |
template<class Default , template< class...> class Op, class... Args> | |
using | Acts::Concepts::detected_or = detail::detector< Default, void, Op, Args...> |
Variables | |
template<bool... Bs> | |
constexpr bool | Acts::Concepts::require = std::conjunction<std::bool_constant<Bs>...>::value |
template<bool... Bs> | |
constexpr bool | Acts::Concepts::either = std::disjunction<std::bool_constant<Bs>...>::value |
template<bool... Bs> | |
constexpr bool | Acts::Concepts::disallow = not require<Bs...> |
template<template< class...> class Op, class... Args> | |
constexpr bool | Acts::Concepts::exists = is_detected<Op, Args...>::value |
template<class To , template< class...> class Op, class... Args> | |
constexpr bool | Acts::Concepts::converts_to = is_detected_convertible<To, Op, Args...>::value |
template<class Exact , template< class...> class Op, class... Args> | |
constexpr bool | Acts::Concepts::identical_to = is_detected_exact<Exact, Op, Args...>::value |
template<typename T , typename R , template< class...> class M, typename... Arguments> | |
constexpr bool | Acts::Concepts::has_method = M<T, R, Arguments...>::template tv<T>::value |
template<typename T , template< class...> class M, typename V > | |
constexpr bool | Acts::Concepts::has_member = identical_to<V, M, T> |
#define METHOD_TRAIT | ( | trait_name, | |
method_name | |||
) |
These helpers allow writing checks. The missing piece is something that you can put into these. This is the point where this turns into type checking. Op
from above can be anything. For instance, we can write an expression, and have the compiler try to calculate that expression's resulting type, like so: ``` decltype(std::declval<T>().member_a) ``
std::declval<T>()constructs a pseudo-value of type
Twhich works even if
Tis not actually constructible like this. Then we access a member called
member_ainside and instruct the compiler to calculate the resulting type using
decltype`. This will only work if the expression is valid. If not, this would normally cause a compilation error. If we wrap it into the detection idiom, however, we can turn that compilation error into a simple constexpr false!
To do that, we need to put that expression above into a metafunction. If we simply wrote ``` is_detected<decltype(std::declval<T>().member_a)> `<tt> where</tt>decltype(std::declval<T>().member_a)<tt>is</tt>Op<tt>, and</tt>Args
is empty, we still get a compilation error. This is because the compiler evaluates the first argument before even passing it into is_detected
, and that means outside the overload resolution context. This is why we need to pass a metafunction around the expression as Op
, and the concrete type T
as Args
so that the actual evaluation of the expression happens inside the overload resolution context. Luckily, writing a metafunction around the expression is as easy as ``` template <typename t>=""> using member_a_t = decltype(std::declval<T>().member_a>); `<tt> and we can then use it like</tt>is_detected<member_a_t, T>
.
Basically, what you have to do to write type assertions using this pattern is metafunctions like the one described above. Those can be member checks, like shown before, nested type checks like ``` template <typename t>=""> using nested_a_t = typename T::NestedA; ``` and checks for contained templates like ``` template <typename t>=""> using meta_t = typename T::template meta<void, void>; ``` but also arbitrary expressions, like operators, or even operators between types. For instance, say we have two types U
and V
. We can then write a metafunction that attempts to instantiate a binary operation between the two: ``` template <typename U, typename V> using binary_op_t = decltype(std::declval<U>() + std::declval<V>()); `` and simply takes both
Uand
Vas meta-arguments. Invoked like
is_detected<binary_op_t, TypeA, TypeB>on some types
TypeA and
TypeB`, this will tell you whether you can call that binary operation on values of those types.
Implementing method checks is a little more involved. A simple method check can be written like ``` template <typename t>=""> using foo_method_t = decltype(std::declval<T>().foo(double, int)>); ```. This only checks if the given expression is valid. That means this will evaluate to true even if the actual arguments of that function are references, as the compiler will figure that out behind the scenes and still allow you to call it. That can be fine, if you're really only interested if that specific call is allowed. Remember that decltype
calculates the type of the expression. In this context, it will evaluate to the return type of the called function. Using identical_to
you can therefore assert the return type to be a given value.
You might, however, want to constrain the exact types of the arguments, and the method's const qualifier. This is less straightforward. To achieve this, a macro is provided below, which will generate the required code for a trait predicate. You cannot use the result of this macro directly with the helpers defined above. What the macro provides is a metafunction, which still takes the type, the return type and the arguments as metaarguments. So the result of the macro only encodes the name of the method, not the rest of its signature. That means you can use the same method trait to check for any signature involving the same method name. Say you generated ``` // foo_method_t is the name of the struct generated by this macro, // foo is the name of the method you want to check for. METHOD_TRAIT(foo_method_t, foo); `<tt>. An additional helper</tt>has_method
is provided, which wraps a little boilerplate to check a specific signature. You can then write ``` has_method<T, R, foo_method_t, bool, const int&> ``` to check for a signature of the form ``` R T::foo(bool, const int&) ```. If you want to check for a const method you can modify this to ``` has_method<const T, R, foo_method_t, bool, const int&> ```. Note that both will only evaluate to true if the const qualifier matches what you gave exactly. If you want to check for a method of a given specifier and you don't care if the method is const or not, you have to write out both variants explicitly, and combine them with either
. This macro generates some boilerplate code that is necessary to correctly implement a method type trait that evaluates to constexpr bools correctly
trait_name | The resulting name of the trait. |
method_name | The name of the method the trait shall check. |
Definition at line 340 of file TypeTraits.hpp.
View newest version in sPHENIX GitHub at line 340 of file TypeTraits.hpp