File TypeTraits.hpp¶
Defines
-
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 typeT
which works even ifT
is not actually constructible like this. Then we access a member calledmember_a
inside and instruct the compiler to calculate the resulting type usingdecltype
. 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
whereis_detected<decltype(std::declval<T>().member_a)>
decltype(std::declval<T>().member_a)
isOp
, andArgs
is empty, we still get a compilation error. This is because the compiler evaluates the first argument before even passing it intois_detected
, and that means outside the overload resolution context. This is why we need to pass a metafunction around the expression asOp
, and the concrete typeT
asArgs
so that the actual evaluation of the expression happens inside the overload resolution context. Luckily, writing a metafunction around the expression is as easy asand we can then use it liketemplate <typename T> using member_a_t = decltype(std::declval<T>().member_a>);
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
and checks for contained templates liketemplate <typename T> using nested_a_t = typename T::NestedA;
but also arbitrary expressions, like operators, or even operators between types. For instance, say we have two typestemplate <typename T> using meta_t = typename T::template meta<void, void>;
U
andV
. We can then write a metafunction that attempts to instantiate a binary operation between the two:and simply takes bothtemplate <typename U, typename V> using binary_op_t = decltype(std::declval<U>() + std::declval<V>());
U
andV
as meta-arguments. Invoked likeis_detected<binary_op_t, TypeA, TypeB>
on some typesTypeA
andTypeB
, 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
// 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);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
has_method<T, R, foo_method_t, bool, const int&>An additional helper `has_method` is provided, which wraps a little boilerplate to check a specific signature. You can then write
R T::foo(bool, const int&)to check for a signature of the form
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 withIf you want to check for a const method you can modify this to
either
. This macro generates some boilerplate code that is necessary to correctly implement a method type trait that evaluates to constexpr bools correctly- Parameters
trait_name – The resulting name of the trait.
method_name – The name of the method the trait shall check.
-
namespace Acts
Set the Geometry Context PLUGIN.
Set the Calibration Context PLUGIN.
Convenience functions to ease creation of and Acts::InterpolatedMaterialMap and to avoid code duplication.
Set the Mangetic Field Context PLUGIN.
Convenience functions to ease creation of and Acts::InterpolatedBFieldMap and to avoid code duplication.
Currently implemented for the two most common formats: rz and xyz.
-
namespace Concepts
Typedefs
-
using detected_or = detail::detector<Default, void, Op, Args...>¶
Helper which invokes the detector with a default type, and resolves to the type.
- Template Parameters
Default – The type to resolve to if
Op<Args...>
does not resolve.Op – The operation
Args – The argument to the operation
-
using detected_t = typename detail::detector<detail::nonesuch, void, Op, Args...>::type¶
This type calls into the detector (same as
is_detected
) but it extracts the return type ofOp<Args...>
.- Template Parameters
Op – The operation
Args – The arguments to the operation
-
using is_detected = typename detail::detector<detail::nonesuch, void, Op, Args...>::value_t¶
This type ties together the detection idiom.
It instantiates the
detector
template with theOp
andArgs
and resolves to the exact value type. In essence, ifOp<Args...>
succeeds, this will evaluate tostd::true_type
, and if not, it will evaluate tostd::false_type
.- Template Parameters
Op – The operation to test
Args – The arguments to the operation
-
using is_detected_convertible = std::is_convertible<detected_t<Op, Args...>, To>¶
This evaluates
Op
inside the detector, and checks whether the resolved type is convertible toTo
.- Template Parameters
To – The type to check convertibility to.
Op – The operation
Args – The arguments to the operation
-
using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>¶
This invokes
detected_t
, and checks whether its result matchesExpected
.- Template Parameters
Expected – The expected result of the operation.
Op – The operation
Args – The arguments to the operation
Variables
-
template<class To, template<class...> class Op, class ...Args>
constexpr bool converts_to = is_detected_convertible<To, Op, Args...>::value¶ Alias to conversion check, which also extracts the constexpr boolean value.
- Template Parameters
To – The type to check convertibility to.
Op – The operation
Args – The arguments to the operation.
-
template<bool... Bs>
constexpr bool disallow = not require<Bs...>¶ Alias for the negation of a
require
.This is essentially a NOT ANY test.
- Template Parameters
Bs – The booleans.
-
template<bool... Bs>
constexpr bool either = std::disjunction<std::bool_constant<Bs>...>::value¶ Helper which forms the logical OR of its arguments.
Converts to
std::bool_constant
.- Template Parameters
Bs – The booleans to combine.
-
template<template<class...> class Op, class ...Args>
constexpr bool exists = is_detected<Op, Args...>::value¶ Alias to
is_detected
which unpacks the constexpr boolean value.- Template Parameters
Op – The operation
Args – The arguments to the operation.
-
template<typename T, template<class...> class M, typename V>
constexpr bool has_member = identical_to<V, M, T>¶ Helper to assert if a member of a given type exists.
Basically only calls into
identical_to
but is nicer to read.- Template Parameters
T – The type to check existence of member on.
M – The member type trait
V – The type that the member is supposed to have.
-
template<typename T, typename R, template<class...> class M, typename ...Arguments>
constexpr bool has_method = M<T, R, Arguments...>::template tv<T>::value¶ Helper which evaluates whether the type
T
has a method with a given signature.- Template Parameters
T – The type to check on. This can contain a const qualifier if you want to check on that.
R – The return type
M – The method trait, as generated by METHOD_TRAIT
Arguments – The argument types that make up the signature.
-
template<class Exact, template<class...> class Op, class ...Args>
constexpr bool identical_to = is_detected_exact<Exact, Op, Args...>::value¶ Unpacks the constexpr boolean value from
is_detected_exact
- Template Parameters
Exact – The type to check identity against
Op – The operation
Args – The arguments to the operation.
-
template<bool... Bs>
constexpr bool require = std::conjunction<std::bool_constant<Bs>...>::value¶ Define some sort of “Domain Specific Language” to declare concepts a little more naturally.
These are taken from https://izzys.casa/2016/09/implementing-concepts-in-cxx/ Helper which combines a set of predicates (constexpr bools) with a logical AND. Converts to
std::bool_constant
.- Template Parameters
Bs – The booleans to combine
-
using detected_or = detail::detector<Default, void, Op, Args...>¶
-
namespace Concepts