4

I have a player class which looks like this (stripped down to what is needed for this problem):

class Player { public: Player(); ~Player(); void kill(); void death(); void reset(); }; 

The kill(), death(), and reset() functions look like this:

void Player::kill() { void (*dPtr)() = &death; Game::idle(dPtr, 48); } void Player::death() { reset(); } void Player::reset() { //resets } 

The idle function is a static memeber function of Game, which takes a function pointer and an integer n, and calls the function after n tick. Here is the function, the implementation shouldn't matter:

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

This code gives me the error:

ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function. Say '&Player::death' [-fpermissive] 

So I change the line from

 void (*dPtr)() = &death; 

to

 void (Player::*dPtr)() = &Player::death; 

to solve that issue. But then my call to the idle function is incorrect, as it takes a regular function pointer, and I am passing in a member function pointer, and thus gives me the error:

no matching function for call to 'Game::idle(void (Player::*&)(), int)' 

So my question is: How can I pass the member function pointer Player::*dPtr into the idle function, which takes a void (*)() as an argument?

Or is there another way I can solve my previous error which forbids me from taking the address of an unqualified member function to form a pointer to a member function?

9
  • Take a look on boost::bind and boost::function. Commented Mar 15, 2015 at 20:45
  • Use std::bind(), no need for boost in the current standard. Commented Mar 15, 2015 at 20:48
  • @πάνταῥεῖ The object returned by std::bind is not assignment-compatible with void (*)(). Commented Mar 15, 2015 at 20:58
  • @zwol Well, so the function static void idle(void (*)(), int); needs to be changed. What's the problem? Commented Mar 15, 2015 at 21:00
  • Can you change the interface of idle? Commented Mar 15, 2015 at 21:00

4 Answers 4

5

Another answer mentions that you need two pointers. However C++ already comes with containers for doing just this, so it would make your code a lot simpler to use those. (In C++03, some of the std:: items below were std::tr1::).

Sample code:

#include <iostream> #include <functional> struct Game { static void idle( std::function<void()> func, int x ) { std::cout << "x = " << x << "\n"; func(); } }; struct Player { void death() { std::cout << "player.death\n"; } void kill() { Game::idle( std::bind(&Player::death, this), 48 ); } }; int main() { Player p; p.kill(); } 

Lifetime note: std::bind binds by value. Using *this means a copy of the Player is made and stored in the std::function object, copied around with it as necessary.

Using this means the function object stores a pointer, so if you actually store the function object in Game::idle you must take care that this Player is not destroyed before removing this function object from Game::idle's list.

Sign up to request clarification or add additional context in comments.

3 Comments

Can you say a little about lifetime issues with this approach? Specifically, suppose Game::idle puts its argument on a queue to be executed "later", well after both it and Player::kill returns. Obviously the Player object has to survive till then. Is it safe to treat std::function and the unspecified object returned by std::bind as value types, lifetime irrelevant? Is there any way to make a std::function hold onto a shared_ptr to some or all of the arguments passed to std::bind?
@zwol edited post to mention lifetime issues. It is possible to use shared_ptr with bind, however this only works if your original object is also managed by shared_ptr of course.
Sample code using delayed idle and shared_ptr . To enforce Player being created under shared_ptr you could have private constructor and friend Game, and give Game a factory function.
3

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.

4 Comments

I don't fully understand your first approach. What is the kill_player_delayed function? I'm assuming that refers to what I put as the kill function, and kill() in your refers to my death()?
@Yaxlat My apologies, I misread your original example. I thought you had not shown the complete function that calls Game::idle, and both kill and death were things that might need to get called via Game::idle. So I made up kill_player_delayed to be the thing that calls Game::idle.
This is a really good approach... so good in fact, that the Standard library already contains it, under the name std::function! (Reinventing it is perhaps not so good)
@BenVoigt Sadly, nearly all my C++ experience has been with dusty-deck codebases that don't even get to use the C++98 standard library, let alone C++11, so I only have a vague idea of what-all's in there. Post another answer using std::function and I'll upvote it ;-)
1

Because I really don't like the answer that casts void*'s to other objects (almost never necessary in C++!) and nobody has posted an answer using the suggestions in the comments I'm going to suggest this.

Use a templated type for your callback!

Like this:

class Game{ template<typename Func> static void idle(Func &&func, int i){ // game stuff func(); // other game stuff } }; 

Then you don't lose all of your type safety (casting void*) and it should be the fastest solution.


Also, where you are assigning a function pointer, you can change the code to be far more readable in this case:

void Player::kill(){ Game::idle([this](){this->death();}, 48); } 

Which is far nicer than having to write the correct function pointer type.

2 Comments

This is great as long as the callback doesn't have to be saved somewhere for later use. If it does, then std::function<void ()> is a better solution. Both lambdas and std::bind results can convert to std::function.
@BenVoigt I always avoid using virtual functions or std::function in any high-performance programming (such as game programming) because of the slowdown of the indirection. But if I needed to store the callback, you're absolutely right and reinventing the wheel wouldn't be useful to anyone!
0

You cannot do that simply because pointer to [static] function is a single pointer sizeof void*. In contrary member function need more information, e.g. two pointers: one for this and another one for the function itself so member function pointer has sizeof > sizeof(void*).

Therefore you have two options:

  1. to change signature of your idle() to this void idle(void (*)(), void*, int); so you will be able to pass this somehow.
  2. Or make static variable that will hold this pointer. But that assumes that only one death() can be at idle queue at any given moment of time.

1) is what people do usually in such cases.

1 Comment

Yes I ended up doing 1) thanks to zwol's suggestion. I incorrectly assumed c++ function pointers included a pointer to the object the belong to when they are member functions.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.