Pass member function pointer to parent class yields compiler error

Carpetfizz

I'd like to have child classes register callbacks to their parent class so that users of the parent class can call methods of the child with a known function signature.

typedef int(*Func)(int);

class A
{
  public:
    void registerFunc(Func f)
    {}
};

class B : public A
{

  public:
    B()
    {
      A::registerFunc(&B::myF);
    }
    int myF(int x) {
       // do stuff with member variables
       return 3; 
    }
};

But I get this compiler error

main.cpp:18:23: error: cannot initialize a parameter of type 'Func' (aka 'int (*)(int)') with an rvalue of type 'int (B::*)(int)'
      A::registerFunc(&B::myF);
                      ^~~~~~~
main.cpp:8:28: note: passing argument to parameter 'f' here
    void registerFunc(Func f)

Here's a Repl illustrating the error in a concise example.

https://replit.com/@Carpetfizz/RudeSmoothComments#main.cpp

The accepted answer in a related thread suggested to override a virtual function declared in A but my use case actually requires dynamic callback registrations.

WhozCraig

If I understand the goal (and believe me, that's a sketchy 'if'), you want to specify some member of some A derivation to invoke from some A member as a dispatched 'callback' mechanic. If that is the case, then to answer your question in comment, yes, a function and bind can do this. It can even be semi-protected with a little help from sfinae:

Example

#include <iostream>
#include <type_traits>
#include <functional>
#include <memory>

struct A
{
    virtual ~A() = default;

    std::function<void(int)> callback = [](int){};

    template<class Derived>
    std::enable_if_t<std::is_base_of<A, Derived>::value>
    registerCallback(void (Derived::*pfn)(int))
    {
        using namespace std::placeholders;
        callback = std::bind(pfn, dynamic_cast<Derived*>(this), _1);
    }

    void fire(int arg)
    {
        callback(arg);
    }
};

struct B : public A
{
    void memberfn(int arg)
    {
        std::cout << __PRETTY_FUNCTION__ << ':' << arg << '\n';
    }
};

struct Foo
{
    void memberfn(int arg)
    {
        std::cout << __PRETTY_FUNCTION__ << ':' << arg << '\n';
    }
};

int main()
{
    std::unique_ptr<A> ptr = std::make_unique<B>();
    ptr->registerCallback(&B::memberfn);
    // ptr->registerCallback(&Foo::memberfn); // WILL NOT WORK
    ptr->fire(42);
}

Output

void B::memberfn(int):42

The Parts

The first part is straight forward. We declare a member variable callback to be a std::function<void(int)> instance. This is where we'll eventually bind our callable object point. The default value is a lambda that does nothing.


The second part is... a little more complicated:

template<class Derived>
std::enable_if_t<std::is_base_of<A, Derived>::value>
registerCallback(void (Derived::*pfn)(int))

This declares registerCallback as an available member function that accepts a non-static member function pointer taking one int as an argument, but only if the class hosting that member function, or a derivative therein, is a derivation of A (or A itself). Some non-A derivative Foo with a member void foo(int) will not compile.


Next, the setup to the callback itself.

using namespace std::placeholders;
callback = std::bind(pfn, dymamic_cast<Derived*>(this), _1);

This just binds the pointer-to-member to this dynamic-cast to the derivation type (which had better work or we're in trouble, see final warning at the end of this diatribe), and sets the call-time placeholder. The _1 you see comes from the std::placeholders namespace, and is used to delay providing an argument to the callback until such time as we actually invoke it (where it will be required,and you'll see that later). See std::placehholders for more information.


Finally, the fire member, which does this:

void fire(int arg)
{
    callback(arg);
}

This invokes the registered function object with the provided argument. Both the member function and this are already wired into the object. The argument arg is used to fill in the placeholder we mentioned earlier.


The test driver for this is straightforward:

int main()
{
    std::unique_ptr<A> ptr = std::make_unique<B>();
    ptr->registerCallback(&B::memberfn);
    // ptr->registerCallback(&Foo::memberfn); // WILL NOT WORK
    ptr->fire(42);
}

This creates a new B, hosting it in a dynamic A pointer (so you know there is no funny business going on). Even with that, because B derived from A the registerCallback sfinae filtering passes inspection and the callback is registered successfully. We then invoke the fire method, passing our int argument 42, which will be sent to the callback, etc.


Warning: With great power comes great responsibility

Even those there is protection from passing non-A derived member functions, there is absolutely none from the casting itself. It would be trivial to craft a basic A, pass a B member (which will work since A is its base), but there is no B actually present.

You can catch this at runtime via that dynamic_cast, which we're currently not error checking. For example:

registerCallback(void (Derived::*pfn)(int))
{
    using namespace std::placeholders;
    Derived *p = dynamic_cast<Derived*>(this);
    if (p)
        callback = std::bind(pfn, p, _1);
}

You can choose the road more risky. Personally, i'd detect the null case and throw an exception just to be safe(er)

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

how to pass pointer to member function of a template class?

Pass Member Function Pointers to parent class

Type conversion error in pointer to class member function

'Creating pointer to member of non-class type 'T*'' compiler error

Avoid the class scope so as to pass a member function as a function pointer

Pointer to class member function

pointer to a member function of a class

Pointer to Parent Class Member Method

C++ How to pass member function pointer to another class?

Pointer to member function error

Pass member function as parameter to a parent class in a different header

std thread call template member function of template class: compiler error

function pointer in c++ class throws compiler error

Function pointer to class template member

Pointer to Member Function Class Type

Member function pointer on derived class

Does C++ have a defined way to pass a pointer to a member function of a member object of a class?

Pass a function pointer of an overloaded member function?

Error in calling a pointer to member function

cast a pointer to member function in derived class to a pointer to abstract member function

compiler error on calling derived class function using base class pointer which is not in base class

Return a function pointer from a class member function

Difficulty in passing function pointer of a class member function

Difference in accessing member function via pointer of parent type or directly from derived class

How to pass a struct member pointer to a function?

Pass a function pointer to a class object

pass name of member of PARENT class as template argument

changing a value of a class pointer in const Class function gives an error in some compiler but not others

C++ pointer to member function getting an error : not a function or function pointer