Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically create a function pointer that calls a method on a given instance

I suspect this is impossible, but thought I'd ask. Say I have a class with a method:

class A {
public:
    void b(int c);
};

I can make a pointer to that member function:

void (A::*ptr)(int) = &A::b;
(someAInstance.*ptr)(123);

I can also abuse function pointers and make a pointer that takes the A argument directly (I don't know if this is safe, but it works on my machine):

void (*ptr2)(A*, int) = (void (*)(A*, int))&A::b;
(*ptr2)(&someAInstance, 123);

What I want is to somehow curry the A argument, and create a function pointer that just takes an int, but calls the A::b method on a particular A instance I've predefined. The A instance will stay constant for that particular function pointer, but there may be several function pointers all pointing to the same A::b method, but using different A instances. For example, I could make a separate wrapper function:

A* someConstantA = new A;
void wrapper(int c) {
    someConstantA->b(c);
}

void (*ptr3)(int) = &wrapper;

Now I can use ptr3 without knowing which particular A it's dispatching the call to, but I had to define a special function to handle it. I need a way to make pointers for any number of A instances, so I can't hardcode it like that. Is this in any way possible?


Edit: Should've mentioned, I'm trapped in C++03 land, and also can't use Boost

like image 969
Michael Mrozek Avatar asked Apr 02 '12 19:04

Michael Mrozek


2 Answers

Don't create a wrapper function, create a wrapper functor. This allows you to encapsulate whatever state you want to (e.g. an A*) in a callable object.

class A {
public:
    void b(int c) {}
};

struct wrapper {
  A* pA;
  void (A::*pF)(int);
  void operator()(int c) { (pA->*pF)(c); }
  wrapper(A* pA, void(A::*pF)(int)) : pA(pA), pF(pF) {}
};

int main () {
  A a1;
  A a2;

  wrapper w1(&a1, &A::b);
  wrapper w2(&a2, &A::b);

  w1(3);
  w2(7);
}
like image 98
Robᵩ Avatar answered Oct 11 '22 18:10

Robᵩ


If you have a sufficiently new compiler (e.g. gcc 4.2+), it should include TR1, where you could use std::tr1::bind:

#include <cstdio>
#include <tr1/functional>

class A {
public:
    void b(int c) {
        printf("%p, %d\n", (void*)this, c);
    }
};

int main() {
    A* a = new A;

    std::tr1::function<void(int)> f =
        std::tr1::bind(&A::b, a, std::tr1::placeholders::_1);  // <--
    f(4);

    delete a;

    return 0;
}

It is also doable in pure C++03 without TR1, but also much more messier:

std::binder1st<std::mem_fun1_t<void, A, int> > f =
    std::bind1st(std::mem_fun(&A::b), a);

You could also write your own function objects.

Note that, in all the above cases, you need to be very careful about the lifetime of a since that is a bare pointer. With std::tr1::bind, you could at least wrap the pointer in a std::tr1::shared_ptr, so that it can live just as long as the function object.

std::tr1::shared_ptr<A> a (new A);
std::tr1::function<void(int)> f =
    std::tr1::bind(&A::b, a, std::tr1::placeholders::_1);
like image 28
kennytm Avatar answered Oct 11 '22 17:10

kennytm