3

I want to pass a member function as a call-back. The call back is a basic function pointer.

So I have something like:

h file:

void (*pRequestFunc) (int someint) = 0;
void RegisterRequestCallBack(void (*requestFunc) (int someint))
{
    pRequestFunc = requestFunc;
}

class A
{
    void callBack(int someint);
}

Cpp File:

RegisterRequestCallBack(&A::callBack); // This does not work.

Note I have tried to extract this example from my larger example and cut out all the other stuff - so it might not be perfect.

The problem, as far as I understand, is that member function pointers really (under the hood) have an extra parameter (and instance - i.e. this) and are not compatible with normal function pointers.

the RegisterRequestCallBack() is in reality not my code - and so I can't change that.

So I read that boost::bind can do what I need - and I am hoping c++11 std::bind can do the same - but I could not figure out how to use it to effectively get a standard function pointer from a member function pointer...

I was going for something like:

std::bind(&A::callBack) ... that is about as far as I got, my understanding of the examples online is poor :(

code_fodder
  • 15,263
  • 17
  • 90
  • 167
  • Yeah - it's not always obvious ... `std::bind(&A::fn, instanceOfA (can be pointer), argsTofn)` – UKMonkey Feb 28 '18 at 15:26
  • 3
    @UKMonkey but that won't produce a function pointer. In fact, it is impossible to store that into a function pointer, because it needs bound arguments. – Quentin Feb 28 '18 at 15:29
  • @Quentin You're right - I seem to automatically convert some lines in my head; like function pointer to std::function. – UKMonkey Feb 28 '18 at 15:31
  • AFAIK this cannot be done. – NathanOliver Feb 28 '18 at 15:35
  • maybe, if the A::callBack is static – Wander3r Feb 28 '18 at 15:37
  • 1
    Well, you could create a static function; that has access to the std::function that it then needs to call; but really why not just use a `std::function`? – UKMonkey Feb 28 '18 at 15:39
  • @Quentin bugger! - lol, thanks all for comments/answers. It does not look hopeful... It seems like it should be possible / something people have to deal with all the time.... but I guess in my case I am not allowed to change the `RegisterRequestCallBack()` function... don't really want to go static either....hmm... I maybe have to re-design something... or maybe Lambda's to the rescue? (somehow!) – code_fodder Feb 28 '18 at 16:47
  • @RickyL. away from my dev pc at the moment, but I presume that `auto` could be re-written as `void (*) (int someint)`? - as in I know the type I want... I sort of get lambdas, but not quite this one - can you explain a little? – code_fodder Feb 28 '18 at 16:53
  • @code_fodder the issue is that you want to store what function to call *and* on which object to call it. A function pointer can only store the former, so you need either an erased type (such as `std::function`) instead of the pointer, or an additional pointer to the parameter (typical C APIs take in a `void *` for that purpose). – Quentin Feb 28 '18 at 17:38

2 Answers2

2

NathanOliver's comment is correct, and your suspicion is mostly correct. Exactly how pointers to member functions work is not specified, but including this as a hidden argument mostly works. You just need a bit of extra work for inheritance and pointers to virtual functions (yes, you can take their address too).

Now, often callbacks include a void* parameter under your control, which you can use to pass a A*. In those cases, you can write a wrapper (static) function that casts the void* back to A* and does the actual call to &A::callback.

That's not the case here. Registration takes a single function, without data. To get this to work in real-life situations, you have to resort to drastic solutions - not portable C++. One such method is to dynamically generate assembly (!). You create - at runtime - the compiled equivalent of

void __trampoline_0x018810000 (int i)
{
   A* __this = reinterpret_cast<A*>(0x018810000);
   __this->callback(i);
}

As you can see, you have to generate one trampoline for every A* value, and managing lifetimes of these is a major pain.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Not the answer I want (obviously)... but that's good information either way +1 - don't think i'll attempt your trampoline, I can't even understand it :p .. I'll wait just a bit longer in case someone has a great idea – code_fodder Feb 28 '18 at 16:49
  • After a a few years! and more experience I have come across this trampoline pattern a few times and even had to use it once or twice myself (less so with modern c++). But now I can see this as the accepted answer (sorry for the delay) – code_fodder Mar 31 '21 at 06:03
1

To be able to bind to a member function you need to do:

std::function<void(int)> function = std::bind(&A::foo, this, std::placeholders::_1);

Or in your case:

RegisterRequestCallBack(std::bind(&A::callback, this, std::placeholders::_1));

But in my opinion the clearest way to achieve this is to use lambda functions. Here you have an example to for doing something similar that could inspire you:

#include <array>
#include <map>
#include <vector>
#include <functional>
#include <iostream>

class TaskManager {
    public:
        using task_t = std::function<void()>;

        void run();
        void addTask(task_t task);

    private:
        std::vector<task_t> _tasks;
};

void TaskManager::run() {
    for (auto& task : _tasks) {
        task();
    }
}

void TaskManager::addTask(task_t task) {
    _tasks.push_back(task);
}

class Example {
    public:
        Example(){
            taskManager.addTask([this]() {
                task1();
            });
            taskManager.addTask([this,a=int(4)](){
                task2(a);
            });
        }

    TaskManager taskManager;

    private:
        void task1(){ std::cout << "task1!\n"; }
        void task2(int a){ std::cout << "task2 says: " << a << "\n"; }
};

int main() {
    Example example;
    example.taskManager.run();
}

which outputs:

task1!
task2 says: 4
Blasco
  • 1,607
  • 16
  • 30