C ++ SFINAE check method in class pointer

I have a code like this:

template<class TABLELOADER = void>
class DiskFileFlush{
   TABLELOADER *_loader;

public:
   void process(){
       // I want to call this,
       // only if loader->refresh() exists.
       notifyLoader();
   }

    bool notifyLoader(){
        if (loader != nullptr)
            return loader->refresh();

        return false;
    }
};

I want to call notifyLoader()only if it loader->refresh()exists.

I also use voidas the default type, is there a better way to do the same?

+4
source share
4 answers

The trick is to use SFINAE and the template specialization together.

The following example compiles with gcc 5.3:

#include <iostream>

template<typename T> struct has_refresh_operator
{
        //! Yes return value

        typedef char yes[1];

        //! No return value

        typedef char no[2];

        template<typename S>
        static yes &check(decltype( (*(S *)nullptr)->refresh()) *);

        template<typename S>
        static no &check(...);

        //! Determine whether the class implements ->refresh() method.

    static const bool value = sizeof(check<T>(0)) == sizeof(yes);
};

// Now, use a specialized template to figure out what to do:

template<bool> class invoke_refresh;

template<> class invoke_refresh<true> {
public:

    template<typename T>
    static inline auto doit(const T &t)
    {
        return t->refresh();
    }
};

template<> class invoke_refresh<false> {
public:
    template<typename T>
    static inline bool doit(const T &t)
    {
        return false;
    }
};

// Now, let try some examples:

template<class TABLELOADER = void>
class DiskFileFlush{
   TABLELOADER *_loader;

public:

    DiskFileFlush(TABLELOADER *p) : _loader(p) {}

   void process(){
       // I want to call this,
       // only if loader->refresh() exists.
       notifyLoader();
   }

    bool notifyLoader(){
        if (_loader != nullptr)
            return invoke_refresh<has_refresh_operator<TABLELOADER *>::value>::doit(_loader);
        return false;
    }
};

// Try: class implements refresh(), class doesn't implement refresh(),
// and a void pointer.

class foo1 {

public:
    bool refresh()
    {
        std::cout << "Foo" << std::endl;
        return true;
    }
};

class foo2 {

public:
};

int main()
{
    foo1 bar1;
    foo2 bar2;

    DiskFileFlush<foo1> foobar1(&bar1);
    DiskFileFlush<foo2> foobar2(&bar2);
    DiskFileFlush<void> baz(0);

    foobar1.process();
    foobar2.process();
    baz.process();
}
+2
source

SFINAE can now be done in a much more convenient way, thanks to the expression-SFINAE:

template<class TABLELOADER = void>
class DiskFileFlush{
   TABLELOADER *_loader;

public:
   void process(){
       notifyLoader(0);
   }

    // Best match for the call above (0 is an int)
    template <class TL = TABLELOADER>
    auto notifyLoader(int) -> decltype(std::declval<TL&>().refresh()) {
        std::cout << "Refreshing!\n";

        if (_loader != nullptr)
            return _loader->refresh();

        return false;
    }

    // Fallback with low-priority varargs
    bool notifyLoader(...) {
        std::cout << "Not refreshing!\n";
        return false;
    }
};

Live on coliru

You probably want to hide this from the public interface of your class by providing a public method notifyLoader()that calls notifyLoader(0).

+2

, , .

Since I do not need this class TABLELOADERfor anything, but when calling this method, I made an explicit specialized specialization.

I agree that this is not exactly SFINAE, and it will not compile if I use a type that does not have loader->refresh()and is different from std::nullptr_t.

Here's what it looks like at the end:
(the code is simplified and probably has errors)

template<class TABLELOADER = std::nullptr_t>
class DiskFileFlush{
   TABLELOADER *_loader;

public:
   void process(){
       // I want to call this,
       // only if loader->refresh() exists.
       notifyLoader(_loader);
   }

   template<class T>
   static bool _notifyLoader(T *loader){
       if (loader)
        return loader->refresh();

       return false;
   }

   static bool _notifyLoader(std::nullptr_t *){
       return false;
   }
};
0
source

This is the easiest way to get as close to the elusive static_if(can_do_this).

// the if branch
template <typename T> 
    auto do_refresh (T* loader) -> decltype(
                                       loader->refresh(), // <-- "can_do_this" part
                                       bool())
    {
        loader->refresh();
        return false;
    };

// the else branch
template <typename T>
    auto do_refresh (...) -> bool
    {
        return false;
    }

bool refresh()
{
    return do_refresh(_loader);
}
0
source

All Articles