3

Suppose I have such template:

template<class T>
class A
{
   ...
};

I want this template can be specialized only if type which will be substituted in place of T have certain interface. For example, this type must have such two methods:

int send(const char* buffer, size_t size);
int receive(char* buffer, size_t size);

How can i make this restrictions on the template? Thanks for the help!

UPD:

This question is about SFINAE? not about inheretince or class design.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Kirill Chernikov
  • 1,387
  • 8
  • 21
  • 2
    Don't confuse templates with an abstract base class requirement. If you need a specific set of functions, define that interface as a class and make it a simple typed requirement. – tadman Jul 14 '17 at 05:40
  • 2
    Possible duplicate of [Check if class has function with signature](https://stackoverflow.com/questions/24975147/check-if-class-has-function-with-signature) – K. Kirsz Jul 14 '17 at 05:46
  • @tadman that assumes that you can add the base class to all the types you are interested in. – Caleth Jul 14 '17 at 07:33
  • @Caleth That's what multiple-inheritance is all about. Until C++ has protocol support that's how things shake out. – tadman Jul 14 '17 at 07:34
  • @tadman what I mean is that you may be receiving objects with those methods from a 3rd party, where you don't control the instantiation, so you can't change the class – Caleth Jul 14 '17 at 07:36
  • @Caleth That's a valid concern and a good thing to note here. I was suggesting it from an API design perspective if this is something someone has full control over. – tadman Jul 14 '17 at 07:37
  • Friends thanks for your comments. This question is about SFINAE, not about inheritance and design of classes. I want to make dispacther that can work with different classes (not necessary my) which provide socket or pipe interaction. – Kirill Chernikov Jul 17 '17 at 06:29

3 Answers3

6

The very easy way is to use T::send and T::receive within A, any type not implementing those will result in a compile time failure to instantiate the template. You only need SFINAE to distinguish between specialisations of templates.

e.g.

template<class T>
class A
{
    void useT(T & theT)
    {
        char buf[20]
        theT.send("Some Thing", 11);
        theT.recieve(buf, 20);
    }
};
Caleth
  • 52,200
  • 2
  • 44
  • 75
1

The other answer is clearly preferable but since you explicitly asked for SFINAE, here you go:

#include <iostream>
#include <utility>

// std::void_t in C++17
template < typename... >
using void_t = void;


// Check if there is a member function "send" with the signature
// int send(const char*, size_t)
template < typename T >
using send_call_t = decltype(std::declval<T>().send(std::declval<char const *>(), std::declval<size_t>()));

template < typename, typename = void_t<> >
struct is_send_callable : std::false_type {};

template < typename T >
struct is_send_callable< T, void_t< send_call_t<T> > > : std::is_same< send_call_t<T>, int > {};


// Check if there is a member function "receive" with the signature
// int receive(const char*, size_t)
template < typename T >
using recv_call_t = decltype(std::declval<T>().receive(std::declval<char *>(), std::declval<size_t>()));

template < typename, typename = void_t<> >
struct is_recv_callable : std::false_type {};

template < typename T >
struct is_recv_callable< T, void_t< recv_call_t<T> > > : std::is_same< recv_call_t<T>, int > {};


// Make a struct which implements both
struct sndrecv
{
  int send(const char* buffer, size_t size)
  {
    std::cout << "Send: " << buffer << ' ' << size << '\n';
    return 0;
  }

  int receive(char* buffer, size_t size)
  {
    std::cout << "Receive: " << buffer << ' ' << size << '\n';
    return 0;
  }
};


// Disable A if T does not have send and receive
template < typename T, typename >
class A;

template < typename T, typename = typename std::enable_if< is_send_callable<T>::value && is_recv_callable<T>::value >::type >
class A {};


int main() {
  A<sndrecv> a;
//A<int> b; // BOOM!
}
Henri Menke
  • 10,705
  • 1
  • 24
  • 42
0

Check if class has method with certain signature is common application of SFINAE principle. For instance you can check there and there.

Viktor
  • 1,004
  • 1
  • 10
  • 16