geza geza - 22 days ago 9
C++ Question

Is it possible to specialize a function with member function pointers with convenient syntax for calling?

Look at this non-compiling snippet:

struct Object {
template <RETURN (OBJECT::*MEMFN)(PARAMETERS...), typename RETURN, typename OBJECT, typename ...PARAMETERS>
void call() {
}
};

struct Foo {
void fn();
};

int main() {
Object o;
o.call<&Foo::fn>();
}


Basically, what I want to achieve is to have a function (
Object::call
), which is specialized with any kind of member function pointers, with convenient syntax to call.

The nearest solution I found is this, which is very ugly:

struct Object {
};

template <typename MEMFNTYPE, MEMFNTYPE MEMFN>
struct Caller;

template <typename RETURN, typename OBJECT, typename ...PARAMETERS, RETURN (OBJECT::*MEMFN)(PARAMETERS...)>
struct Caller<RETURN (OBJECT::*)(PARAMETERS...), MEMFN> {
Object *object;

Caller(Object &o) : object(&o) { }

void call() {
// I have the necessary information here: the member function pointer as template parameter, and a pointer to Object
}
};

struct Foo {
void fn();
};

int main() {
Object o;

Caller<decltype(&Foo::fn), &Foo::fn>(o).call();
}


Is there a better solution for this problem?

(The reason is I'm trying to do this is that I'd like to create wrapper functions (
call
) for other member functions)




Kerrek SB suggested using
auto
, I've tried this:

struct Object {
template <auto MEMFN>
void call();

template <auto MEMFN, typename RETURN, typename OBJECT, typename ...PARAMETERS>
void call<RETURN (OBJECT::*MEMFN)(PARAMETERS...)>() {
}
};

struct Foo {
void fn();
};

int main() {
Object o;
o.call<&Foo::fn>();
}


However, this doesn't compile (Do I need to add
MEMFN
differently to the template parameter list (instead of
auto MEMFN
?):

t2.cpp:6:7: error: parse error in template argument list
void call<RETURN (OBJECT::*MEMFN)(PARAMETERS...)>() {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
t2.cpp:6:52: error: non-class, non-variable partial specialization ‘call<<expression error> >’ is not allowed
void call<RETURN (OBJECT::*MEMFN)(PARAMETERS...)>() {

Answer Source

As was suggested in the comments, if you accept C++17 solutions, then auto is your friend. And so are <type_traits> and if constexpr:

#include <type_traits>
#include <iostream>

struct Object {
    template <auto MEMFN>
    void call() {
        if constexpr (std::is_member_function_pointer_v<decltype(MEMFN)>)
            std::cout << "Is member\n";
    }
};

struct Foo {
    void fn();
};

int main() {
    Object o;
    o.call<&Foo::fn>();
}

The function body will only emit code that treats the parameter as a member function pointer if the type trait says it is indeed that. No specialization is required.