Search code examples
c++openglglutc2664

C2664 error in an attempt to do some OpenGl in c++


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

Solution

  • 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.