Olumide
Olumide

Reputation: 5809

Using SFINAE to detect if overloaded free-standing function exists at compile-time

Here is my unsuccessful attempt at detecting whether a free-standing function void set( T& , void (T::*Func)( const char* ) , const char* str ) exists for any class/struct T, in C++98/03.

#include <iostream>
#include <typeinfo>

struct Foo;
struct Bar;
struct Fuu;

void set( Foo& , void (Foo::*Func)( const char* ) , const char* str )
{
}

void set( Bar& , void (Bar::*Func)( const char* ) , const char* str )
{
}

template <typename T>
class setter_is_implemented
{
public: 
    typedef void (*SetterFunction)( T& , void (T::*)( const char* ) , const char* );

    typedef char one;
    typedef long two;

    template <typename C> static one test( C c, SetterFunction = c ) ;
    template <typename C> static two test(...);    

public:
    enum { value = sizeof( test<T>(&set)) == sizeof(one) };
};

int main()
{
    std::cout << setter_is_implemented<Foo>::value << std::endl;
    std::cout << setter_is_implemented<Bar>::value << std::endl;
    std::cout << setter_is_implemented<Fuu>::value << std::endl;
}

GCC error message

Test.cpp:24:66: error: ‘c’ was not declared in this scope
     template <typename C> static one test( C c, SetterFunction = c ) ;
                                                                  ^
Test.cpp: In instantiation of ‘class setter_is_implemented<Foo>’:
Test.cpp:33:44:   required from here
Test.cpp:28:35: error: address of overloaded function with no contextual type information
     enum { value = sizeof( test<T>(&set)) == sizeof(one) };
                                   ^
Test.cpp: In instantiation of ‘class setter_is_implemented<Bar>’:
Test.cpp:34:44:   required from here
Test.cpp:28:35: error: address of overloaded function with no contextual type information
Test.cpp: In instantiation of ‘class setter_is_implemented<Fuu>’:
Test.cpp:35:44:   required from here
Test.cpp:28:35: error: address of overloaded function with no contextual type information

Upvotes: 2

Views: 509

Answers (1)

Massimiliano Janes
Massimiliano Janes

Reputation: 5624

AFAIK, taking the address of an overloaded function happens before overload resolution, so SFINAE cannot work in that way. This is also harder because your function returns void (otherwise, one would just check the size of the result ). My C++03 metaprogramming is rusty, but this should work (live on coliru):

namespace setter_is_implemented_detail {
  struct dummy1 { char c[2]; };
  typedef char dummy2;

  dummy2 operator,(dummy1, dummy1);

  template<typename T> dummy1 set(T const&,...);

  template <typename T>
  struct impl
  {
    typedef void (T::*FT)( const char* );
    static T& x;

    enum { value = sizeof( set(x,static_cast<FT>(0),static_cast<const char*>(0)), dummy1() ) != sizeof(dummy2) };
  };
}

template <typename T>
struct setter_is_implemented: setter_is_implemented_detail::impl<T>{};

note a few things:

  • set() needs to be found by ADL here (not a big deal, as far as I can tell from your problem description)(*)
  • the comma operator trick(**) is needed due to the void return type; in gcc, you can take the sizeof(void), so the code can be simplified there
  • this won't work for non object types (due to the static T&). It can be made to work though, if you need so...

(*) Note that this cannot work for every possible set() overload; this is true for any sfinae, even in c++17, set() must always be sfinae-friendly in order to work.

If you need to match sfinae-friendly set() overloads exactly, it's still possible to do it, but it's more complex; I'd do it in two phases: first you use the above code to find possible candidates, then you check the function pointer conversion, somewhat like you do in your original code ..

(**) This trick works because if set() has void return type (and hence exists) the comma is always interpreted as the built-in comma operator (see c++03[3.9.1/9]: "An expression of type void shall be used only as an expression statement (6.2), as an operand of a comma expression[...]") resulting in the right-most expression. Otherwise, the dummy set and comma operator are used.

Upvotes: 2

Related Questions