haint504
haint504

Reputation: 103

Check the existence of a template function

How can I check the existence of a template function like this: Checking if reader struct has read arithmetic value

struct reader {
    template<typename T>
    std::enable_if_t<std::is_arithmetic<T>::value, T> read() {
        return {};
    }
};

I use a checker like this:

template <typename T>
struct test_read {
    static constexpr auto value = std::is_convertible<decltype(std::declval<T>().read<int>()),int>::value;      
};

But the compiler complains:

error: wrong number of template arguments (1, should be 2)
 static constexpr auto value = std::is_convertible<decltype(std::declval<T>().read<int>()),int>::value;

Please give me your advice on that.

Thank you.


Update: Here is the final version I got after discussion, I hope everyone will find it helpful for your code

struct not_reader {

};


struct reader {
    template<typename T>
    std::enable_if_t<std::is_arithmetic<T>::value, T> read() {
        return {};
    }
};


template<class T, class Elem>
struct has_read {
private:
    template<class C, typename=void>
    struct test_read : std::false_type {
    };
    template<class C>
    struct test_read<C, typename std::enable_if<std::is_convertible<decltype(std::declval<C>().template read<Elem>()), Elem>::value>::type>
            : std::true_type {
    };
public:
    using type = typename test_read<T>::type;
    static constexpr bool value = test_read<T>::value;
};


static_assert(has_read<reader, int>::value, "reader should have int read()");
static_assert(!has_read<not_reader, int>::value, "not_reader should not have int read()");

Upvotes: 4

Views: 267

Answers (2)

AndyG
AndyG

Reputation: 41090

To briefly restate your problem in terms that are a bit clearer:

  • You have some function that will return T if T satisfies is_arithmetic, otherwise it returns void
  • You want to assert that calling this function with int will return a type convertible to int

I think the shortest path to fix your code is to take advantage of std::result_of (C++11/14, use std::invoke_result_t in C++17):

template<class T>
struct test_read {
static constexpr auto value = std::is_convertible<
    typename std::result_of<decltype(&T::template read<int>)(T)>::type, int
    >::value;    
};

Live Demo

Some notes about this solution:

  • When specifying the read member function of T (reader), we need to use the template keyword to inform the compiler that the name reader is a template.
  • Use of result_of requires a function-like syntax of F(Args), so here we are getting the type of reader::read as the F portion, and then passing reader as the Args portion
    • we must pass an instance of T (reader) to read because it is a member function (rather than static or free), and member functions implicitly take a reference to the instance of the class they're being called on.

Upvotes: 1

max66
max66

Reputation: 66200

You forgot template before read()

static constexpr auto value
    = std::is_convertible<
         decltype(std::declval<T>().template read<int>()),int>::value;  
// .................................#########    

But I don't think that your code can check " if reader struct has read arithmetic value": try calling test_read with type int and you should get a compilation error.

The following is an example of an alternative solution

#include <type_traits>

struct reader
 {
   template<typename T>
   std::enable_if_t<std::is_arithmetic<T>::value, T> read()
    { return {}; }
 };

template <typename, typename = void>
struct readTypeRet
 { using type = void; };

template <typename T>
struct readTypeRet<T, decltype(std::declval<T>().template read<int>(), void())>
 { using type = decltype(std::declval<T>().template read<int>()); };

template <typename T>
struct test_read
    : public std::is_convertible<typename readTypeRet<T>::type, int>
 { };

int main ()
 {
   static_assert(test_read<reader>::value == true, "!");
   static_assert(test_read<int>::value == false,   "!");
 }

Upvotes: 4

Related Questions