2
\$\begingroup\$

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.

\$\endgroup\$
4
  • \$\begingroup\$ Is a C++20 concept an option for you? \$\endgroup\$
    – Edward
    Commented Dec 12, 2019 at 15:19
  • \$\begingroup\$ @Edward It is not a solution at the moment, since I am limited to c++14. But I will have look, since the day may come ... \$\endgroup\$
    – user186243
    Commented Dec 12, 2019 at 15:35
  • \$\begingroup\$ Couldn't you use something like template<class T> struct has_trait : std::false_type {}; and template<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\$
    – AlexV
    Commented Dec 12, 2019 at 16:51
  • \$\begingroup\$ @Edward the requires expression looks like it would make this a lot prettier. \$\endgroup\$
    – user186243
    Commented Dec 13, 2019 at 7:33

1 Answer 1

2
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.