views:

162

answers:

2

Here is an abstract of my code. I'm trying to use glutSpecialFunc to tell glut to use my KeyPress function

class Car : public WorldObject
{
public:
 void KeyPress(int key, int x, int y)
 {
 }

 Car()
 {
  glutSpecialFunc(&Car::KeyPress); // C2664 error
 }
}

The error message I get is:

Error 1 error C2664: 'glutSpecialFunc' : cannot convert parameter 1 from 'void (__thiscall Car::* )(int,int,int)' to 'void (__cdecl *)(int,int,int)' c:\users\thorgeir\desktop\programmingproject1\quickness\quickness\car.cpp 18 Quickness
+2  A: 

Your function is a member of a class. When you do something like Car c; c.drive(), that drive() function needs a car to work with. That is the this pointer. So glut can't call that function if it doesn't have a car to work on, it's expecting a free function.

You could make your function static, which would mean the function does not operate on a car. glut will then be able to call it, however I assume you want to manipulate a car. The solution is to make the function pass it's call onto an object, like this:

void key_press(int key, int x, int y)
{
    activeCar->KeyPress(key, x, y);
}

Where activeCar is some globally accessible pointer to car. You can do this with some sort of CarManager singleton.

CarManager keeps track of the active car being controlled, so when a key is pressed you can pass it on: CarManager::reference().active_car().KeyPress(key, x, y).

A singleton is an object that has only one globally accessible instance. It is outside the scope of the answer, but you can Google for various resources on creating one. Look up Meyers Singleton for a simple singleton solution.

A different approach is to have a sort of InputManager singleton, and this manager will keep track of a list of objects it should notify of key presses. This can be done in a few ways, the easiest would be something like this:

class InputListener;

class InputManager
{
public:
    // ...

    void register_listener(InputListener *listener)
    {
        _listeners.push_back(listener);
    }

    void unregister_listener(InputListener *listener)
    {
        _listeners.erase(std::find(_listeners.begin(), _listeners.end(), listener));
    }

   // ...

private:
    // types
    typedef std::vector<InputListener*> container;        

    // global KeyPress function, you can register this in the constructor
    // of InputManager, by calling glutSpecialFunc
    static void KeyPress(int key, int x, int y)
    {
        // singleton method to get a reference to the instance
        reference().handle_key_press(key, x, y);
    }

    void handle_key_press(int key, int x, int y) const
    {
        for (container::const_iterator iter = _listeners.begin();
             iter != _listenders.end(), ++iter)
        {
            iter->KeyPress(key, x, y);
        }
    }

    container _listeners;
};

class InputListener
{
public:
    // creation
    InputListener(void)
    {
        // automatically add to manager
        InputManager::reference().register_listener(this);
    }

    virtual ~InputListener(void)
    {
        // unregister
        InputManager::reference().unregister_listener(this);
    }

    // this will be implemented to handle input
    virtual void KeyPress(int key, int x, int y) = 0;
};

class Car : public InputListener
{
    // implement input handler
    void KeyPress(int key, int x, int y)
    {
        // ...
    }
};

Of course feel free to ask more questions if this doesn't make sense.

GMan
Wow great answer and very quick!. Explained everything.
Thorgeir
A: 

What I ended up doing was

Adding:

virtual void KeyPress(int key, int x, int y) {};

to the WorldObject class

And bubble the event to the Car.

 void KeyPressed(int key, int x, int y)
 {
  KeyPress(key,x,y);

  list<WorldObject*>::iterator iterator = ChildObjects.begin();
  while(iterator != ChildObjects.end())
  {
   (*iterator)->KeyPressed(key,x,y);
   iterator++;
  }
 }
Thorgeir