user2729541
user2729541

Reputation: 153

Invoke a may not existed member function by template

How can I call a may-existing member function by template technology, I am not want to use virtual method, because the class is arbitrary.

For example:

class A {
    void setValue(int value);
};

How to define a template to call class A's setValue if it existing, else do nothing.

The template is something like:

template <typename T>
struct call_if_exist {
    void invokeOrNot(T& a, int value){ // EXISTING: setValue
        a.setValue(value);
    }
}
template <typename T>
struct call_if_exist {
    void invokeOrNot(T& a, int value){ // NOT EXISTING: do nothing
    }
}

It's about invoking, not checking.

Upvotes: 1

Views: 144

Answers (1)

Manu343726
Manu343726

Reputation: 14174

You can take advantage of SFINAE to make a class template that checks for the existence of a member function in a given class, and use the result as a boolean flag to specialize your template for the case when there is no function, and the case when the member function exists:

template<typename T , bool member_exists = has_set_value<T>::result>
struct call_if_exist;

template <typename T>
struct call_if_exist<T,true> {
    void invokeOrNot(T& a, int value){ // EXISTING: setValue
        a.setValue(value);
    }
}
template <typename T>
struct call_if_exist<T,false> {
    void invokeOrNot(T& a, int value){ // NOT EXISTING: do nothing
    }
}

Edit: The has_set_value trait

template<typename T>
class has_set_value
{
    typedef struct{ char c[1]; } yes;
    typedef struct{ char c[2]; } no;

    template<typename U> static yes test(&U::set_value);
    template<typename U> static no  test(...);

public:
    static const bool result = sizeof( test<T>(NULL) ) == sizeof( yes );
};

This class is the typical example of the usage of SFINAE to check for the existence of a member (type or function) of a certain class.

First we define two typedefs, yes and no, which are used to differentiate overload resolutions through the sizeof operator.
The first overload of test() has a pointer to the member function as parameter, and the last is an overload which goal is to be called by everything thats not a pointer to the member. This is done through a variadic-function (Note the ellipsis), which can be used with any kind of parameter.

The point of the implementation is even if the second overload can hold any parameter, the first is an explicit case for pointers to our member function. So if the parameter could be a pointer to the function, the call is resolved to the first function, else its resolved to the second.

As I said before, we use the typedefs to differentiate the overload resolution through the sizeof operator: Note that the first overload returns yes and the later returns no. So if the size of the result type of the call to test() using a pointer (NULL) is equal to the size of yes, means that the overload is resolved to the first, and the class passed as parameter (T) has a member function called set_value.

Alexandrescu's Modern C++ Dessign includes an example of this kind of trait in chapter two to check if one type is implicitly convertible to other.

Upvotes: 1

Related Questions