Detection Idiom#
The Detection Idiom is used to recognize, in an SFINAE-friendly way, the validity of any C++ expression.
Header File: <Kokkos_DetectionIdiom.hpp>
The Kokkos Detection Idiom is based upon the detection idiom from Version 2 of the C++ Extensions for Library Fundamentals, ISO/IEC TS 19568:2017, a draft of which can be found here <https://cplusplus.github.io/fundamentals-ts/v2.html#meta.detect>.
The original C++ proposal can be found at here <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4436.pdf>.
API#
// VOID_T and DETECTOR are exposition-only and not intended to be used directly.
// Convenient metafunction to leverage SFINAE
template<class...>
using VOID_T = void;
// Primary template for types not supporting the archetypal Op<Args...>
template<class Default, class /* AlwaysVoid */, template<class...> class /* Op */, class... /* Args */>
struct DETECTOR {
using value_t = std::false_type;
using type = Default;
};
// Specialization for types supporting the archetypal Op<Args...>
template<class Default, template<class...> class Op, class... Args>
struct DETECTOR<Default, VOID_T<Op<Args...>>, Op, Args...> {
using value_t = std::true_type;
using type = Op<Args...>;
};
namespace Kokkos {
// Simplification of the type returned by detected_t for types not supporting the archetype provided
struct nonesuch {
nonesuch(nonesuch&&) = delete;
~nonesuch() = delete;
};
// is_detected is an alias for std::true_type if Op<Args...> is a valid type
// otherwise, an alias for std::false_type
template <template <class...> class Op, class... Args>
using is_detected =
typename DETECTOR<nonesuch, void, Op, Args...>::value_t;
// detected_t is an alias for Op<Args...> if Op<Args...> is a valid type
// otherwise, an alias for Kokkos::nonesuch
template <template <class...> class Op, class... Args>
using detected_t = typename DETECTOR<nonesuch, void, Op, Args...>::type;
// detected_or_t is an alias for Op<Args...> if Op<Args...> is a valid type
// otherwise, an alias for Default
template <class Default, template <class...> class Op, class... Args>
using detected_or_t = typename DETECTOR<Default, void, Op, Args...>::type;
// is_detected_exact is an alias for std::true_type if Op<Args...> is the same type as Expected
// otherwise, an alias for std::false_type
template <class Expected, template <class...> class Op, class... Args>
using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
// is_detected_convertible is an alias for std::true_type if Op<Args...> is convertible to To
// otherwise, an alias for std::false_type
template <class To, template <class...> class Op, class... Args>
using is_detected_convertible =
std::is_convertible<detected_t<Op, Args...>, To>;
// C++17 or later convenience variables
template <template <class...> class Op, class... Args>
inline constexpr bool is_detected_v = is_detected<Op, Args...>::value;
template <class Expected, template <class...> class Op, class... Args>
inline constexpr bool is_detected_exact_v =
is_detected_exact<Expected, Op, Args...>::value;
template <class Expected, template <class...> class Op, class... Args>
inline constexpr bool is_detected_convertible_v =
} // Kokkos namespace
Examples#
Detecting an expression#
Suppose we needed to write a type trait to detect if a given type T
is copy assignable. First we write an archetype helper alias:
template<class T>
using copy_assign_t = decltype(std::declval<T&>() = std::declval<T const&>());
Then the trait can be easily expressed as:
template<class T>
using is_copy_assignable = Kokkos::is_detected<copy_assign_t, T>;
If we also wanted to check that the return type of the copy assignment is T&
, we would use:
template<class T>
using is_canonical_copy_assignable = Kokkos::is_detected_exact<T&, copy_assign_t, T>;
Detecting a nested typedef#
Suppose we want to use a nested MyType::difference_type
if it exists, otherwise, we want to use std::ptrdiff_t
:
First we write an archetype helper alias:
template<class T>
using diff_t = typename T::difference_type;
Then we can declare our type:
using our_difference_type = Kokkos::detected_or_t<std::ptrdiff_t, diff_t, MyType>;