12

I want to use SFINAE to enable a particular template if the user passes a function pointer as a parameter.

I have googled around but found nothing - I also tried looking at the <type_traits> documentation but couldn't find anything that resembled a is_function_ptr<T>.

By function pointer, I mean global function pointers, like TReturn(*)(TArgs...).

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416

2 Answers2

10

Below is a type trait determining if something is a function pointer and a couple of test cases. Note, that to test if something is a function pointer, you need to test if std::is_pointer<P>::value is true and if std::is_function<T>::value is true where T is P with the pointer removed. The code below just does that:

#include <type_traits>
#include <iostream>
#include <utility>

template <typename Fun>
struct is_fun_ptr
    : std::integral_constant<bool, std::is_pointer<Fun>::value
                            && std::is_function<
                                   typename std::remove_pointer<Fun>::type
                               >::value>
{
};

template <typename Fun>
typename std::enable_if<is_fun_ptr<Fun>::value>::type
test(Fun) {
    std::cout << "is a function pointer\n";
}

template <typename Fun>
typename std::enable_if<!is_fun_ptr<Fun>::value>::type
test(Fun) {
    std::cout << "is not a function pointer\n";
}

void f0() {}
void f1(int) {}
void f2(int, double) {}

struct s0 { void operator()() {} };
struct s1 { void operator()(int) {} };
struct s2 { void operator()(int, double) {} };

int main()
{
    int v0(0);
    int* p0(&v0);
    void (*p1)() = &f0;
    void (**p2)() = &p1;
    std::cout << "v0="; test(v0);
    std::cout << "p0="; test(p0);
    std::cout << "p1="; test(p1);
    std::cout << "p2="; test(p2);

    std::cout << "f0="; test(&f0);
    std::cout << "f1="; test(&f1);
    std::cout << "f2="; test(&f2);

    std::cout << "s0="; test(s0());
    std::cout << "s1="; test(s1());
    std::cout << "s2="; test(s2());

    std::cout << "l0="; test([](){});
    std::cout << "l1="; test([](int){});
    std::cout << "l2="; test([](int, double){});
}
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • You could make a trait just as easily out of `constexpr` function template and nm's answer, no? And it would be far more powerful (you could require a minimum number of parameters, or a particular return type). – Ben Voigt Sep 06 '13 at 22:09
  • @BenVoigt: Sure. However, the question wasn't "How can you detect if something is a function pointer based on n.m.'s solution" nor was there a question about a minimum number of arguments or a particular return type. – Dietmar Kühl Sep 06 '13 at 22:19
  • Yes, but `enable_if` doesn't care whether the trait is built out of `remove_pointer` at all or directly implemented. – Ben Voigt Sep 06 '13 at 22:24
  • This helped me resolve [this question](http://stackoverflow.com/q/43348943/1593077) of mine, thank you, +1. – einpoklum Apr 11 '17 at 17:35
4

No SFINAE is needed to accept a function pointer or a member function pointer. To distinguish function objects from non-callable stuff SFINAE is needed, there's probably no way around this.

#include <utility>
#include <iostream>

template <typename Ret, typename... Parm>
void moo (Ret (*fp)(Parm...))
{
    std::cout << "funptr" << std::endl;
}

template <typename Ret, typename Owner, typename... Parm>
void moo (Ret (Owner::*fp1)(Parm...))
{
    std::cout << "memfunptr" << std::endl;
}

template <typename Funobj, typename... Parm, 
          typename Ret = 
                   decltype((std::declval<Funobj>())
                            (std::forward(std::declval<Parm>())...))>
void moo (Funobj functor)
{
    std::cout << "funobj" << std::endl;
}

void x1() {}
struct X2 { void x2() {} };
struct X3 { void operator()(){} };


int main()
{
    moo(x1);
    moo(&X2::x2);
    moo(X3());
}
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243