Search code examples
c++classc++11function-pointerspointer-to-member

Why can't I reference a pointer to a function of an instantiated object?


EDIT: std::function + lambda to the rescue. See checked answer.

EDIT: I added more detail, I want a generic solution, not one bound to Class B's definition.

In the example below, I try to set a function pointer in one class to a function (of the same function pointer type) from an instance of another class, and Microsoft C++ is telling me:

C++ a pointer to a bound function may only be used to call the function

Is it because the object containing the callback might be destructed before the first object? I'm translating some Python to C++ and this pattern doesn't seem to be a problem in Python.

I find the workaround is to make the function static (and the member data), but I'm just hacking until the compiler shuts up and the code works, not my favourite mode of programming.

If this pattern isn't allowed, any suggestions on how to do this dynamically without static objects/functions?

#include <iostream>
#include <string>

typedef void (*callback_function_a)(int);

struct A 
{
    A() {
        cbk = nullptr;
    };
    void print_int(void) 
    {
        if (cbk != nullptr) {
            cbk(10);
        }
    }
    void set_callback(callback_function_a _cbk)
    {
        cbk = _cbk;
    };
    callback_function_a cbk;
};

struct B 
{
    B(std::string _header) 
    {
        header = _header;
    }
    void print(int x) {
        std::cout << header << x << std::endl;
    }
    std::string header;
};

void print(int x)
{
    std::cout << "Non-member func: " << x << std::endl;
}

int main() 
{
    A classA;
    B classB(std::string("MessageFrom classB:"));
    /// How do I make both of these work? I want to be able to pass in any 
    /// function of type callback_function_a?
    classA.set_callback(print); // OK
    classA.set_callback(classB.print); // <--- error here
}

Solution

  • A function pointer to a free function and a function pointer to a member-function are two different things. To work around that we need some kind of wrapper.

    This is one of the good use cases for std::function. It's a type-erased callable with a given signature.

    To make it more generic you should make set_callback a template so you can accept any kind of callable object that is assignable to a std::function.

    #include <iostream>
    #include <string>
    #include <functional>
    
    struct A {
        A() {
            cbk = nullptr;
        };
        void print_int(void) {
            if (cbk != nullptr) {
                cbk(10);
            }
        }
        template <typename Func>
        void set_callback(Func _cbk) {
            cbk = _cbk;
        };
        std::function<void(int)> cbk;
    };
    
    struct B {
        B(std::string _header) {
            header = _header;
        }
        void print(int x) {
            std::cout << header << x << std::endl;
        }
        std::string header;
    };
    
    void print(int x) {
        std::cout << "Non-member func: " << x << std::endl;
    }
    
    int
    main() {
        A classA;
        B classB(std::string("MessageFrom classB:"));
    /// How do I make both of these work? I want to be able to pass in any 
    /// function of type callback_function_a?
        classA.set_callback(print); // OK
        classA.set_callback([&](int i) { classB.print(i); }); // <--- now works as well
    }
    

    If you're not familiar with lambdas like [&](int i) { classB.print(i); } have a look here.