1

I try to learn SFINAE right now, but it seems I have a problem with coercion, how can I do to make hasRead<Y> and hasRead<Z> fail since the method argument doesn't correspond to an std::uint16_t ?

I joined my code to see what can be done to make it work like I want !

Thanks in advance :)

#include <cstdint>
#include <iostream>
#include <utility>

template<typename Class>
struct hasRead {
private:
    template<typename T>
    static constexpr auto check(T *) -> typename std::is_same<
        decltype(std::declval<T>().read(std::declval<uint16_t>())), uint8_t>::type;


    template<typename>
    static constexpr std::false_type check(...);

    typedef decltype(check<Class>(0)) type;

public:
    static constexpr bool value = type::value;
};

struct X {
    uint8_t read(uint16_t x) { return 3; }
};

struct Y {
    uint8_t read(uint8_t x) { return 3; }
};

struct Z {
    uint8_t read(int64_t x) { return 3; }
};

static_assert(hasRead<X>, "");
static_assert(hasRead<Y>, "");
static_assert(hasRead<Z>, "");
max66
  • 65,235
  • 10
  • 71
  • 111
uknys
  • 59
  • 5

2 Answers2

2

I want to offer a more modern approach to detecting the existence of member functions, know as the detection idiom.

The abridged version only requires C++11. It is more concise, and doing so offers the ability to detect template member functions too (which max66's version doesn't).

template<typename T, uint8_t (T::*)(uint16_t) = &T::read>
using detectRead = void;

template<typename, typename = void>
struct hasRead : std::false_type {};

template<typename T>
struct hasRead<T, detectRead<T>> : std::true_type {};

You use it the same way

struct A { uint8_t read(uint16_t); };
struct B {};
struct C { uint8_t read(uint32_t); };
struct D
{
    template<typename T, typename U>
    U read(T);
};

void test()
{
    static_assert(hasRead<A>::value, "");  // OK
    static_assert(hasRead<B>::value, "");  // fails
    static_assert(hasRead<C>::value, "");  // fails
    static_assert(hasRead<D>::value, "");  // OK
}

Live

†To be pedantic, the usage of SFINAE in partial specializations is ill-formed in C++11 due to wording defects

Passer By
  • 19,325
  • 6
  • 49
  • 96
0

I suppose you can check that (1) the function exist and accept a uint16_t and that (2) it's type is uint8_t(T::*)(uint16_t).

Something like

template <typename Class>
struct hasRead
 {
   private:
      template <typename T>
      static constexpr auto check (T * t)
         -> decltype( t->read(uint16_t{}),
            std::is_same<decltype(&T::read), uint8_t(T::*)(uint16_t)>{} );

      template<typename>
      static constexpr std::false_type check(...);

   public:
      static constexpr bool value = decltype(check<Class>(nullptr))::value;
 };

The following is a full compiling example

#include <cstdint>
#include <iostream>
#include <utility>

template <typename Class>
struct hasRead
 {
   private:
      template <typename T>
      static constexpr auto check (T * t)
         -> decltype( t->read(uint16_t{}),
            std::is_same<decltype(&T::read), uint8_t(T::*)(uint16_t)>{} );

      template<typename>
      static constexpr std::false_type check(...);

   public:
      static constexpr bool value = decltype(check<Class>(nullptr))::value;
 };

struct X { uint8_t read(uint16_t x) { return 3; } };
struct Y { uint8_t read(uint8_t x) { return 3; } };
struct Z { uint8_t read(int64_t x) { return 3; } };
struct A { };

int main()
 {
   static_assert( true  == hasRead<X>::value, "" );
   static_assert( false == hasRead<Y>::value, "" );
   static_assert( false == hasRead<Z>::value, "" );
   static_assert( false == hasRead<A>::value, "" );
 }
max66
  • 65,235
  • 10
  • 71
  • 111