I want to check that a some type has some methods:
using Arg_t2 = int;
using Arg_t3 = int;
template<class T>
struct ValidType{
using Ret_t1 = T;
using Arg_t1 = int;
// ...
Ret_t1 method1(Arg_t1,Arg_t2,Arg_t3);
//...
};
Therefor I wrote a type-trait:
#include <type_traits>
template<class T,class _ = void> struct is_my_valid : ::std::false_type {};
template<class T> struct is_my_valid<T,::std::enable_if_t<
::std::is_same<typename T::Ret_t1,decltype(::std::declval<T>().method1(::std::declval<typename T::Arg_t1>(),::std::declval<Arg_t2>(),::std::declval<Arg_t3>()))>::value
// && ...
> > : ::std::true_type {};
We can test this with:
static_assert(is_my_valid<ValidType<int>>::value,"");
What are my options to make the definition of is_my_valid
this look prettier?
Edit: I basicly want to describe an Interface with type traits. I want to do this, because the interface includes types, that depend on template parameters.
I also like that an type does not have to be implemented using the ValidType in anyway to be valid.
1 Answer 1
Edit: Only after posting did I see that the question was tagged c++14
. This code relies on std::void_t
from c++17
which is trivial to implement in c++14
.
You can extract some of the functionality to a reusable detect
type-trait that only needs to be supplied a type alias to work.
namespace detail {
template <template <class...> class CheckTemplate, class = void, class... Object>
struct detect_impl : std::false_type {};
template <template <class...> class CheckTemplate, class... Object>
struct detect_impl<CheckTemplate, std::void_t<CheckTemplate<Object...>>, Object...> : std::true_type {};
} // namespace detail
template <template <class...> class CheckTemplate, class... Object>
constexpr bool detect_v = detail::detect_impl<CheckTemplate, void, Object...>{};
With that in place all we need is to use it as such.
template <class T>
using is_valid = decltype(std::declval<T>().method1(0, 0, 0));
template <typename T>
constexpr bool is_valid_v = detect_v<is_valid, T>;
static_assert(is_valid_v<ValidType<int>>)
The Object
pack let's us make a more flexible is_valid
that takes the argument types as parameters if we want.
template <class T, class... Args>
using is_valid_flex = decltype(std::declval<T>().method1(std::declval<Args>()...));
template <class... Args>
constexpr bool is_valid_flex_v = detect_v<is_valid, Args...>;
static_assert(is_valid_flex_v<ValidType<int>, int, int int>);
Additional modification is possible if we want to involve the return-type of the method.
concept
an option for you? \$\endgroup\$template<class T> struct has_trait : std::false_type {};
andtemplate<class T> struct has_trait<ValidType<T>> : std::true_type {};
? This seems much more concise, though I admit that it implies more "manual" labor. Maybe it would help to give more context to better judge how appropriate your solution is. \$\endgroup\$requires
expression looks like it would make this a lot prettier. \$\endgroup\$