Passing Member Function Pointer to Member Object in C++

Passing member function pointer to member object in c++

Taking everyone's suggestions together, your final solution will look like:

#include <iostream> 
using std::cout;
usind std::endl;

class foo; // tell the compiler there's a foo out there.

class bar
{
public:
// If you want to store a pointer to each type of function you'll
// need two different pointers here:
void (*freeFunctionPointer)();
void (foo::*memberFunctionPointer)();
};

class foo
{
public:
bar myBar;
void hello(){ cout << "hello" << endl; }
};

void byebye()
{
cout << "bye" << endl;
}

int main()
{
foo testFoo;

testFoo.myBar.freeFunctionPointer = &byebye;
testFoo.myBar.memberFunctionPointer = &foo::hello;

((testFoo).*(testFoo.myBar.memberFunctionPointer))(); // calls foo::hello()
testFoo.myBar.freeFunctionPointer(); // calls byebye()
return 0;
}

The C++ FAQ Lite has some guidance on how to simplify the syntax.

Taking Chris' idea and running with it, you could get yourself something like this:

#include <iostream>
using std::cout; using std::endl;

class foo;
typedef void (*FreeFn)();
typedef void (foo::*MemberFn)();

class bar
{
public:
bar() : freeFn(NULL), memberFn(NULL) {}
void operator()(foo* other)
{
if (freeFn != NULL) { freeFn(); }
else if (memberFn != NULL) { ((other)->*(memberFn))(); }
else { cout << "No function attached!" << endl; }
}

void setFreeFn(FreeFn value) { freeFn = value; memberFn = NULL; }
void setMemberFn(MemberFn value) { memberFn = value; freeFn = NULL; }
private:
FreeFn freeFn;
MemberFn memberFn;
};

class foo
{
public:
bar myBar;
void hello() { cout << "foo::hello()" << endl; }
void operator()() { myBar(this); }
};

void bye() { cout << "bye()" << endl; }

int main()
{
foo testFoo;

testFoo();

testFoo.myBar.setMemberFn(&foo::hello);
testFoo();

testFoo.myBar.setFreeFn(&bye);
testFoo();

return 0;
}

Pass a C++ member function to a C function

You cannot do it, because C++ pointer to a non-static member function is not compatible with a non-member function pointer type. This is because member functions require an additional argument - the object on which the member function needs to be called, which becomes this pointer inside the invocation.

If you make your member functions static, your code would compile. However, it would not necessarily do what you want to achieve, because one and two have no access to other non-static members of A.

A trick to passing member functions to C functions requires passing an additional void* pointer with the "registration" record, and having C code pass it back to your static callback functions:

struct Cstruct
{
void *context; // Add this field
int (*fn1)(void*, int);
int (*fn2)(void*, int);
};

class A
{
public:
static int oneWrap(void* ptr, int x)
{
return static_cast<A*>(ptr)->one(x);
}

static int twoWrap(void* ptr, int x)
{
return static_cast<A*>(ptr)->two(x);
}

int one(int x)
{
}

int two(int x)
{
}

int three(int x)
{
struct Cstruct cstr = {this, &this->oneWrap, &this->twoWrap};
}
};

C code would need to pass the value of context to fn1 and fn2:

cs.fn1(cs.context, 123);
cs.fn2(cs.context, 456);

Passing member function pointer to the c-style function

The result of std::bind is a complicated C++ object. It has to store all the bound arguments, for example. So it is most definitely not convertible to a pointer to function.

The callback specification you're dealing with apparently doesn't allow a "user data" payload, so there's nowhere to hide a pointer to a C++ object which you could use to invoke a non-static member funtion. This means you will have to call a global or static member function, or resort to a global/static member/per-thread variable to store the object pointer.

The only 100% portable way is to create a C linkage function to use as the callback. This does so, and uses a global object pointer to call your original onError():

Application *error_handling_application;

extern "C" void errorCallback(int error, const char *description)
{
error_handling_application->onError(error, description);
}

Note that quite often, you will encounter programs which use a static member function in place of my errorCallback. This works with most compilers on most platforms, but it is not guaranteed to work. The C library expects a function with C language linkage. A static member function can only have C++ language linkage. It is possible for the calling mechanism of a C function and a C++ function to differ (depends on ABI), which would result in a malformed call to the static member function passed in.

Passing a function pointer to a member function in C++.Getting error

Let's deal with the two issues in the post.

  1. You are calling fun1 and fun2. Since their return type is void, you can't pass their result as something's value. In particular as the value of a function pointer. You also can't obtain their address by using the dot member access operator. Which brings us to the following.

  2. Member functions are not like regular functions. You cannot just take their address. Their treatment is special, because member functions can only be called on an object. So there's a special syntax for them, which involves the class they belong to.

Here's how you would do something like what you are after:

class student
{
public:
void fun1() { printf("Fun1\n"); }
void fun2() { printf("Fun2\n"); }

// A function that receives a member function
// as parameter and calls the function
void wrapper(void (student::*fun)())
{
(this->*fun)();
}
};

int main()
{ student s;

s.wrapper(&student::fun1);
s.wrapper(&student::fun2);
return 0;
}

How can I pass a member function pointer into a function that takes a regular function pointer?

To call a member function through a pointer, you need two pointers: the pointer to the function itself, and a pointer to an object to be this. Your Game::idle API does not support this usage. You need to change it so that it passes at least one argument (conventionally of type void *) to the callback. Then you can use the following pattern:

struct Player
{
// ...
void kill();

// ...
static void call_kill(void *self);
};

void Player::call_kill(void *self)
{
static_cast<Player *>(self)->kill();
}

struct Game
{
static void idle(void (*)(void *), void *, int);
};

void Game::idle(void (*callback)(void *), void *arg, int ticks)
{
// ...
callback(arg);
// ...
}

void kill_player_delayed(Player *p, int ticks)
{
Game::idle(Player::call_kill, static_cast<void *>(p), ticks);
}

You have to write a static call_X method for every instance method X you want to call.


An alternative approach, which is arguably more C++-idiomatic and flexible, and involves less explicitly written-out code, but has higher runtime costs (three indirect function calls and a heap allocate-free cycle per invocation, instead of a single indirect function call), is to have Game::idle take an object of a particular class, with a virtual callback method. That class is then given a template subclass that can call anything that implements operator(), such as the result of std::bind.

struct Runnable { virtual ~Runnable(); virtual void invoke() = 0; };

template <typename T> struct TRunnable : Runnable {
TRunnable(T target) : target(target) {}
void invoke() { target(); }
private:
T target;
};
template <typename T> TRunnable<T>* make_Runnable(T obj)
{ return new TRunnable<T>(obj); }

struct Game
{
static void idle(Runnable *, int);
};

void Game::idle(Runnable *r, int ticks)
{
// ...
r->invoke();
delete r;
// ...
}

struct Player
{
// ...
void kill();
// ...
};

void kill_player_delayed(Player *p, int ticks)
{
Game::idle(make_Runnable(std::bind(&Player::kill, p)), ticks);
}

You cannot make Game::idle take the result of std::bind directly because that object's type is unspecified (and varies depending on how you call std::bind), so it can only be used as an argument to a template function call. A virtual method call to an adapter class is the only way to keep Game::idle compiled out-of-line and still let it use bound-call objects.

In either approach, beware object lifetime issues. In particular, if Game::idle does not call its callback before returning, you need to
make sure that both the original object, and (in the second approach)
the object returned by make_Runnable survive until the callback fires. This is why make_Runnable uses new.

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

Is there then a way in C++ to pass the setValue function into myFunction given that setValue is now within the ChildClass object within MyClass?

There is a way by adding a level of indirection. That level of indirection is polymorphic function wrapper std::function. myFunction can take a std::function argument, which can be initialized with arbitrary accessor functions, including labdas. E.g.:

#include <functional>

struct ChildClass {
int value;

void setValue(int value) {
this->value = value;
}
};

struct MyClass {
ChildClass childClass;
};

void myFunction(std::function<void(MyClass&, int)> const& f, MyClass& myClass) {
f(myClass, 1);
}

int main() {
MyClass myClass;
myFunction([](MyClass& a, int b) { a.childClass.setValue(b); }, myClass);
}

Using std::function your can get rid of MyClass& argument and expect the user to provide the required object inside std::function captured by the lambda expression:

void myFunction2(std::function<void(int)> const& f) {
f(1);
}

int main() {
MyClass myClass;
myFunction2([&myClass](int b) { myClass.childClass.setValue(b); });
}

Note that std::function can store at least sizeof(void(Undefined_class::*member_pointer)()) inside the object without allocating heap memory. The C++ standard doesn't require that, however, it seems that the reason for this optimization is that using std::function for member function pointers is not much worse than using member function pointers directly. Or, in other words, if you replace member and member function pointers in your code with std::function, your application won't be penalised by extra memory allocations for that. With gcc on x86_64 that results in 16 bytes of inline storage in std::function, which can store a lambda capture object with 2 object references.

The size of std::function is still larger than that, though, and it is a non-trivial type, so it cannot be passed or returned in registers.



Related Topics



Leave a reply



Submit