Search code examples
c++c++11templatessfinae

Check if a class has a member function of a given signature


I'm asking for a template trick to detect if a class has a specific member function of a given signature.

The problem is similar to the one cited here http://www.gotw.ca/gotw/071.htm but not the same: in the item of Sutter's book he answered to the question that a class C MUST PROVIDE a member function with a particular signature, else the program won't compile. In my problem I need to do something if a class has that function, else do "something else".

A similar problem was faced by boost::serialization but I don't like the solution they adopted: a template function that invokes by default a free function (that you have to define) with a particular signature unless you define a particular member function (in their case "serialize" that takes 2 parameters of a given type) with a particular signature, else a compile error will happens. That is to implement both intrusive and non-intrusive serialization.

I don't like that solution for two reasons:

  1. To be non intrusive you must override the global "serialize" function that is in boost::serialization namespace, so you have IN YOUR CLIENT CODE to open namespace boost and namespace serialization!
  2. The stack to resolve that mess was 10 to 12 function invocations.

I need to define a custom behavior for classes that has not that member function, and my entities are inside different namespaces (and I don't want to override a global function defined in one namespace while I'm in another one)

Can you give me a hint to solve this puzzle?


Solution

  • I'm not sure if I understand you correctly, but you may exploit SFINAE to detect function presence at compile-time. Example from my code (tests if class has member function size_t used_memory() const).

    template<typename T>
    struct HasUsedMemoryMethod
    {
        template<typename U, size_t (U::*)() const> struct SFINAE {};
        template<typename U> static char Test(SFINAE<U, &U::used_memory>*);
        template<typename U> static int Test(...);
        static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
    };
    
    template<typename TMap>
    void ReportMemUsage(const TMap& m, std::true_type)
    {
            // We may call used_memory() on m here.
    }
    template<typename TMap>
    void ReportMemUsage(const TMap&, std::false_type)
    {
    }
    template<typename TMap>
    void ReportMemUsage(const TMap& m)
    {
        ReportMemUsage(m, 
            std::integral_constant<bool, HasUsedMemoryMethod<TMap>::Has>());
    }