1

So I have the following piece of code that works perfectly under VS2012. It detects whether a global function 'Bar' exists.

Sadly this fails to compile under clang 3.5 with this error:

error : use of undeclared identifier 'Bar'
  template <typename T> static int Foo( decltype( Bar )* ) { return 2; }            
                                                  ^ 

Does clang support this kind of thing and if so what is the correct syntax?

Thanks

template <typename T> static int Foo( decltype( Bar )* ) { return 2; }      
template <typename T> static int Foo(...) { return 1; } 

void main()
{
    printf("%d", Foo<int>(nullptr));
}
user176168
  • 1,294
  • 1
  • 20
  • 30

1 Answers1

2

If you know the exact signature of the function you are looking for, it can be solved with SFINAE (tested with clang 3.6 and gcc 4.9):

#include <stdio.h>

int SomeMethod(double d) {
        // nop
        return 0;
}

struct HasSomeMethod {
        typedef char no[2];

        template <typename C>
        static auto test(C c) -> decltype(SomeMethod(c));
        //static auto test(C c) -> decltype(SomeOtherMethod(c));

        template <typename>
        static no& test(...);

        static const bool value = sizeof(test<double>(1.0)) == sizeof(int);
};

int main()
{
    printf("%d\n", HasSomeMethod::value);
}

This outputs 1, and 0 if you switch the test method to the commented version.

It works with any global method as long as it has at least one parameter you can extract to a template parameter.

SFINAE is an abbreviation for "Substitution Failure Is Not An Error". Which means, you have to:

  • use a template parameter which will be substituted by the compiler
  • your subject must somehow depend on this template parameter

My example solves this latter by providing a templated parameter to the subject function - if there is no such function, it can't be substituted, which is valid.

Same result if there is no such overload you are looking for. You can change the parameter of SomeMethod in the code, or at the testing point, and you'll see.

Unfortunately, for this case using type_traits, and similar tricks isn't possible. For example, the following method:

template<typename C>
static auto test(C c) -> typename std::is_same<decltype(SomeXXMethod()), decltype(c)>::type

has the same problem as your original code. For member detection this works because C::SomeMethod depends on the template parameter.

Because of this requirement, a method with no parameter, but a non-void return type probably can be checked, but I have no idea how right now.

But if you have a void method without parameters, I'm sure you won't find a standard way to do it - you can't find any valid type specification which has both your type and that method in it.

Clang specific

If you just want a Clang specific check, there are Clang specific extensions

For example, you can use the #if __is_identifier(SomeMethod) macro, which checks if a given identifier exists. It might be a variable, or a function, or anything, but inside this block, you can use SFINAE to decide it's type.

Dutow
  • 5,638
  • 1
  • 30
  • 40
  • Thanks Dutow! What if the function signature is void bar(void)? It looks like the above is using the template argument of double to decide whether the function exists. – user176168 Sep 30 '15 at 19:42
  • Sorry for some reason completely missed you saying that above! Thanks – user176168 Sep 30 '15 at 19:44
  • 1
    I edited my answer - you won't find a standard compliant method for checking a void(void) method - but there is a clang extension helping you with it, if you only need it with clang. – Dutow Oct 01 '15 at 05:26
  • Thanks for a wonderful answer Dutow. I couldnt have asked for anymore. It just so happens the extension will work fine for my particular case where I dont want the user of my UE4 Slate UI wrapper to have to laboriously write all the optional delegates for a UI element. Instead I detect whether a function has been given and then return an empty delegate if not. Anyway thank you again! – user176168 Oct 01 '15 at 08:34
  • i just tried. `__is_identifier` seems to test if something is a valid identifier, not if it exists, which makes sense given the name. for example `__is_identifier(myFunnyMethod)` will return true, whether you declared it or not. – kritzikratzi Aug 05 '17 at 11:15