C2664 error in an attempt to do some OpenGl in c++ - 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

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.

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++;
}
}

Related

C++: Object Stores Lambda into a Struct and Later Calls that Function

What's the proper syntax to accomplish this? The idea is that some object of any class could store a lambda expression in class GuiButton, and then later that call that lambda expression with access to its own local variables.
It should be noted that my platform (Arduino) does NOT support the functional header.
The code I've written to try to express this idea (which does not compile due to the lambda expressions not having access to members of ExampleScreen):
struct GuiButton {
uint8_t x; //coordinates for displaying this GUI element
uint8_t y;
GuiButton(uint8_t _x, uint8_t _y, void (*_callback)()) :
x(_x),
y(_y),
callback(_callback)
{};
virtual void draw(bool _highlight);
public:
void (*callback)(); //to be executed BY THE PARENT OBJECT when this element is clicked
};
struct GuiTextButton: public GuiButton {
char* text; //text to display in this GUI element
GuiTextButton(uint8_t _x, uint8_t _y, char* _text, void (*_callback)()) :
GuiButton(_x, _y, _callback),
text(_text)
{};
void draw(bool _highlight);
};
class ExampleScreen{
private:
GuiButton** buttonPtr;
uint8_t buttonCount;
uint8_t selectedButton;
bool proc1Active;
bool proc2Active;
public:
ExampleScreen() :
buttonPtr(NULL),
buttonCount(0),
selectedButton(0),
proc1Active(false),
proc2Active(false)
{
//different derived classes of GuiScreen shall have different constructors to define
//their visual and functional elements
buttonPtr = new GuiButton* [2];
buttonCount = 2;
{
char text[] = "Button1";
GuiButton *_thisPtr = new GuiTextButton(5,0,text, []() {
proc1Active = ~proc1Active;
});
buttonPtr[0] = _thisPtr;
}
{
char text[] = "Button2";
GuiButton *_thisPtr = new GuiTextButton(5,0,text, []() {
proc2Active = ~proc2Active;
});
buttonPtr[2] = _thisPtr;
}
};
void click() {
void (*callback)() = (buttonPtr[selectedButton]->callback);
callback();
};
};
int main() {
ExampleScreen gui;
gui.click();
};
Something along these lines:
class GuiButton {
GuiButton(void (*_callback)(void*), void* _context)
: callback(_callback), context(_context) {}
// Invoke the callback as callback(context)
void (*callback)(void*);
void* context;
};
// In ExampleScreen
new GuiButton([](void* context) {
auto self = static_cast<ExampleScreen*>(context);
self->proc1Active = ~self->proc1Active;
}, this);
Per the comments on your discussion you can't use the functional header which rules out the easy solutions (namely having the callback be a std::function and either capturing the context or using std::bind to bind it.
However, I think you can still do what you want. Make the type of callback be a struct like:
struct CallbackData {
void (*callback)(ExampleScreen*);
ExampleScreen* context;
// obvious constructor here...
}
Then you can call the callback like so:
callback_data.callback(callback_data.context);
And you pass it to the GuiButton constructor like:
new GuiTextButton(5,0,text,CallbackData([](ExampleScreen* e) { ... }, this));
Perhaps a nicer option is to use a functor. To do that you'd create a class like so:
class GuiButtonCallback {
public:
GuiButtonCallback(ExampleScreen* context) : context_(context) {}
void operator() {
context->proc1Active = ~context->proc1Active;
}
private:
ExampleScreen* context_;
};
And then you can construct things like so:
new GuiTextButton(5 , 0, text, GuiButtonCallback(this));

How do I call a function in one class from another class? (C++)

I'll start out with some context.
Making a simple game.
I have two classes, one called BouncingBall, and the other called ASCIIRenderer.
I have a list of instructions to follow, but some of the instructions aren't entirely clear.
First instruction was to create a pointer in BouncingBall called m_pRenderer, and have it point to a member variable in ASCIIRenderer. It wasn't specified which member variable I had to point to, and both existing member variables in there were private, so I made my own and called it Renderer.
Second instruction (the one I need help with) is when I'm writing a function for the BouncingBall class to call SetPixel using the m_pRenderer, and with three variables as parameters.
SetPixel is the name of a public function in the ASCIIRenderer class, and the instruction states I have to call it by using the pointer somehow.
Summary: I need to call a class' function from within the function of a separate class using a pointer.
Could someone explain to me what syntax I would use to accomplish this?
Based on the details you provided this is what I gathered. Assuming the BouncingBall class would get the X and Y pos and call the function foo with
the X and Y values to have the Renderer set. Also, I don't know how you will
initialize the pointer as it's not detailed above. Hope this helps.
class BouncingBall
{
public:
void foo( int posX, int posY)
{
m_pRenderer->setPixel( posX, posY);
}
private:
ASCIIRenderer* m_pRenderer;
};
The code I have included below shows an example of a class, in this case Ball, having a pointer to another class, in this case Renderer, injected and stored for later use.
The Ball class can then call public functions on the Renderer class using the 'arrow' syntax pointer->MemberFunction(arg1, arg2);
#include <iostream>
class Renderer
{
public:
/* Constructor and other functions omitted */
void SetPixel(const int x, const int y) const;
};
void Renderer::SetPixel(const int x, const int y) const
{
std::cout << "Set pixel: (" << x << ", " << y << ")" << std::endl;
}
class Ball
{
public:
Ball(const Renderer *const renderer, const int posX, const int posY);
void Render() const;
private:
const Renderer* _renderer;
int _posX;
int _posY;
};
Ball::Ball(const Renderer *const renderer, const int posX, const int posY)
: _renderer(renderer), _posX(posX), _posY(posY)
{}
void Ball::Render() const
{
_renderer->SetPixel(_posX, _posY);
}
int main()
{
const Renderer renderer;
const Ball ball(&renderer, 10, 20);
ball.Render();
return 0;
}
The output of the program is: Set pixel: (10, 20)

C++ event handling interface with function pointers

I want to implement a simple event interface with function pointers for a simple game in C++. This will be done to improve the event interface of the allegro lib. Therefore I've written the following code but it doesn't work.
typedef void (*event_handler)(int); //type for the event handler
const int TESTKEY_A = 1; // example Key as event arg
class Game
{
private:
bool is_running ;
protected:
event_handler on_key_down[2];
public:
void run();
void do_events(int e) ;
void stop() {is_running = false;}
};
void Game::run()
{
is_running=true;
while(is_running)
do_events(1);
}
void Game::do_events(int e)
{
if(e==1)
{
for(int i = 0; i < 2 ;i++)
on_key_down[i](TESTKEY_A);
}
}
class Pong_Game : public Game
{
public:
Pong_Game();
void On_Key_Down_Player1(int key) { return;}
void On_Key_Down_Player2(int key) { return;}
};
Pong_Game::Pong_Game()
{
on_key_down[0] = &this->On_Key_Down_Player1;
on_key_down[1] = &this->On_Key_Down_Player2;
}
int main()
{
Game *my_game = new Pong_Game();
my_game->run();
return 0;
}
Compiler log:
Compiler: Default compiler
Executing g++.exe...
g++.exe "U:\Eigene Dateien\eventhandler.cpp" -o "U:\Eigene Dateien\eventhandler.exe" -I"C:\Dev-Cpp\lib\gcc\mingw32\3.4.2\include" -I"C:\Dev-Cpp\include\c++\3.4.2\backward" -I"C:\Dev-Cpp\include\c++\3.4.2\mingw32" -I"C:\Dev-Cpp\include\c++\3.4.2" -I"C:\Dev-Cpp\include" -L"C:\Dev-Cpp\lib"
U:\Eigene Dateien\eventhandler.cpp: In constructor `Pong_Game::Pong_Game()':
U:\Eigene Dateien\eventhandler.cpp:45: error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function. Say `&Pong_Game::On_Key_Down_Player1'
U:\Eigene Dateien\eventhandler.cpp:45: error: cannot convert `void (Pong_Game::*)(int)' to `void (*)(int)' in assignment
U:\Eigene Dateien\eventhandler.cpp:46: error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function. Say `&Pong_Game::On_Key_Down_Player2'
U:\Eigene Dateien\eventhandler.cpp:46: error: cannot convert `void (Pong_Game::*)(int)' to `void (*)(int)' in assignment
Execution terminated
EDIT:
- changed code
- added compiler log
Thank you!
You are using a member function to initialize a "plain" function pointer. Member functions are different from "plain" functions in that they have a hidden T *this passed along with the function.
This seems like something that would be MUCH better solved by having a "event handler" interface, implemented by inheritance, than using function pointers.
Something like this:
class Game
{
private:
bool is_running ;
public:
void run(){is_running=true; while(is_running) do_events(1);}
void do_events(int e) {if(e==1) On_Key_Down(TESTKEY_A);}
void stop() {is_running = false;}
virtual void On_Key_Down(int key) = 0;
... other event handlers here ...
};
class Pong_Game : public Game
{
public:
void Pong_Game() {}
void On_Key_Down(int key) { // handle key event...}
... other event handlers here ...
};
As per comments:
To cover multiple players, you may want to implement a "per player event handling":
class Player
{
public:
enum moves { move_up,
move_down,
move_left,
move_right,
move_jump,
move_shoot, ... };
...
virtual void On_Key_Down(int key) = 0;
...
};
class Player_A
{
public:
...
virtual moves On_Key_Down(int key) { if (key == 'W') return move_up; ... }
}
class Player_B
{
public:
...
virtual moves On_Key_Down(int key) { if (key == 'I') return move_up; ... }
}
class Pong_Game : public Game
{
private:
vector<Player *> players;
public:
void Pong_Game() {}
void On_Key_Down(int key)
{
for(p : players) {
Player::moves m = p->On_key_down();
...
}
}
... other event handlers here ...
};
(This is just quickly hacked up - the "moves" may perhaps be better placed somewhere else, and the exact structure is just "what I could think of right now" - you probably need a class Player_A_Pong : public Player_A or some such to deal with the specifics of "player A's racket is here, player B's racket is here..." - although there are probably other ways to deal with that too).

OpenGL with c++ classes

I've been trying to write something that will let me easily manage OpenGL using classes.
I took an approach of having a Drawable class [which shapes/etc would inherit from, and override a draw function], and then using a Controller class to iterate through a table of Drawable classes and draw them all.
The only issue that I've noticed that the draw() method is being called from the Drawable class, instead of the Rectangle class. ??
class Drawable {
public:
void draw();
};
class Rectangle : public Drawable {
public:
void draw();
};
class Controller {
public:
Drawable ents[200];
int ent_count;
void frame();
void append(Drawable item); // this will add an object onto the list
Controller();
};
void Drawable::draw() {
// this is the default drawing function, which should be overridden
}
void Rectangle::draw() {
// gl functions here
}
void Controller::frame() {
for(int i=0;i<ent_count,i++) {
ents[i].draw(); // draw all entities on the list
}
// here, a timer would loop back the frame() function
}
void Controller::append(Drawable item) {
ents[ent_count++]=item;
}
int main(void) {
Controller main_controller; // create a controller
Rectangle rect; // create a rectangle
main_controller.append(rect); // insert rectangle into controller list
main_controller.frame(); // start the frame loop
}
[if there are minor typing errors in that, it is because it was written as a summary of the method.]
This method that I've tried to use has not been very successful, and I'm pretty sure it has to do with inheritance.
Any ideas?
Entire source code:
#include <iostream>
#include <GL/glfw.h>
#include <GL/gl.h>
class Drawable {
public:
int x,y;
void draw();
void frame();
void create();
void destroy();
};
void Drawable::create() {
}
void Drawable::draw() {
}
class Rectangle : public Drawable {
public:
int w,h;
unsigned short r,g,b;
Rectangle(int x,int y, int w, int h, unsigned short r, unsigned short g, unsigned short b);
void draw();
};
void Rectangle::draw() {
glColor3ub(r,g,b);
glBegin(GL_QUADS);
glVertex2i(x,y);
glVertex2i(x+w,y);
glVertex2i(x+w,y+h);
glVertex2i(x,y+h);
glEnd();
}
Rectangle::Rectangle(int x,int y, int w, int h, unsigned short r, unsigned short g, unsigned short b) {
this->x=x;
this->y=y;
this->w=w;
this->r=r;
this->g=g;
this->b=b;
}
class Controller {
public:
Controller(int w,int h,int fsaa,bool fs,bool vsync,const char* title);
bool running;
int frame_limit;
Drawable entity[200];
int entity_count;
void fev();
void begin();
void bind();
void append(Drawable item);
};
Controller::Controller(int w,int h,int fsaa,bool fs,bool vsync,const char* title) {
int fullscreen= (fs ? GLFW_FULLSCREEN : GLFW_WINDOW);
bool window=glfwOpenWindow(w,h,0,0,0,0,10,10,fullscreen);
glfwSetWindowTitle(title);
frame_limit=120;
entity_count=0;
std::cout << (window ? "Successfully initialized a window.\n" : "Error initializing window!\n");
}
void Controller::begin() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0,640,480,0,0,5);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(0.4f,0.4f,0.4f,1.0f);
running=true;
fev();
}
void Controller::append(Drawable item) {
entity[entity_count++]=item;
}
void Controller::fev() {
glClear(GL_COLOR_BUFFER_BIT);
for (int i=0;i<entity_count;++i) {
entity[i].draw();
}
glfwSwapBuffers();
if (frame_limit>0) {
glfwSleep(1000/frame_limit*0.001);
}
if (running) {
if (glfwGetKey(GLFW_KEY_ESC) || !glfwGetWindowParam(GLFW_OPENED)) {
running=false;
}
fev();
} else {
std::cout << "terminated!";
}
}
int main(void) {
glfwInit();
Controller main(640,480,0,false,false,"WindTitle");
Rectangle rect(50,50,50,50,50,50,50);
main.append(rect);
main.begin();
}
I've been trying to write something that will let me easily manage OpenGL using classes.
Newbies often try this. But OpenGL really doesn't translate well into OOP. The problem is, that it's a finite state machine and to map properly to OOP you'd have to to a lot of state tracking between the different classes and instances.
I myself tried at least 3 times to abstract OpenGL into a OOP scheme. It always broke in some way.
Which is not to say that you can not use OOP with OpenGL. You can't just map OpenGL concepts 1:1 into classes.
Regarding your actual problem: Use virtual functions.
As others have mentioned, it would be best to try to use some of the existing wrappers.
That said, you need to use pointers for your list of entities. You are having issues with slicing.
As some of the comments mentioned, you need to make Drawable::draw() virtual so calling draw() on a Drawable will call through to the child implementation. That said, because you are adding your Drawables to a an list of objects, instead of a list of pointers to objects, the objects are being sliced. This means that they are being converted from the child type back into Drawable objects, removing the extra information about specific types.
So instead of this:
Drawable ents[200];
You should do something like this:
std::vector<Drawable*> ents;
or if you have C++11 or Boost:
std::vector<std::shared_ptr<Drawable>> ents;
And your append method would take a reference.
void Controller::append(Drawable &item) {
ents[ent_count++] = &item;
}
or
void Controller::append(Drawable &item) {
ents.push_back(&item);
}
or
void Controller::append(std::shared_ptr<Drawable> item) {
ents.push_back(item);
}
And your render loop would be like this:
for (int i = 0; i < ents.size(); ++i) {
ents[i]->draw();
}
This loop could also be cleaned up to use iterator.
You might want to have a look at Coin3D. It is a large rich set of C++ class wrappers around OpenGL. You clould either look at how they do things of just use it. There are libraries for integration with various windowing systems (Qt, Windows, Cocoa, ...).

Pointing to a function that is a class member - GLFW setKeycallback

I'm writing a GLFW app, in which I've wrapped the function calls into a simple class. I'm having trouble setting the key callback.
My class is defined as:
class GAME
{
private:
bool running;
public:
GAME();
int execute();
void events(int, int);
int loop();
int render();
};
The execute function is:
int GAME::execute()
{
glfwOpenWindow(640, 320, 8, 8, 8, 8, 0, 0, GLFW_WINDOW);
glfwSetWindowTitle("Viraj");
glfwSetKeyCallback(events);
running = true;
while(glfwGetWindowParam(GLFW_OPENED))
{
glfwPollEvents();
loop();
render();
}
return 0;
}
Compiling the following code on Visual Studio 2010 gives the error:
error C3867: 'GAME::events': function call missing argument list; use '&GAME::events' to create a pointer to member
Using &GAME::events gives:
error C2664: 'glfwSetKeyCallback' : cannot convert parameter 1 from 'void (__thiscall GAME::* )(int,int)' to 'GLFWkeyfun' 1> There is no context in which this conversion is possible
The code examples provided in the other answers don't describe how to redirect your callback to a per-object member function, with possibly any number of objects. Making your class a singleton will constrain your design and will not scale to multiple glfw windows.
The scalable solution is to set the glfw window user pointer to your object and then fetch it in the callback, and call the member function :
class MyGlWindow
{
public:
void mouseButtonPressed();
};
void makeWindow()
{
GLFWwindow* glfwWindow;
MyGlWindow* myWindow;
/* ... Initialize everything here ... */
glfwSetWindowUserPointer(glfwWindow, myWindow);
auto func = [](GLFWwindow* w, int, int, int)
{
static_cast<MyGlWindow*>(glfwGetWindowUserPointer(w))->mouseButtonPressed( /* ... */ );
}
glfwSetMouseButtonCallback(glfwWindow, func);
}
This solution is shorter and will work for any number of windows.
I also ran into this problem with another glfw callback function, but I didn't want to declare my class method as static, because I needed to access the member variables within. So I tried std::function and std::bind for giving me the ability to bind an instance method as the callback function, but unfortunately it's not an option when working with C callbacks.
The answer to this problem is also stated in the GLFW FAQ "How do I use C++ methods as callbacks":
You cannot use regular methods as callbacks, as GLFW is a C library
and doesn’t know about objects and this pointers. If you wish to
receive callbacks to a C++ object, use static methods or regular functions as callbacks, store the pointer to the object you wish to
call in some location reachable from the callbacks and use it to call
methods on your object.
However, this encouraged me to apply the Singleton pattern for my callback class and integrate it as following:
the callback method of my class is still static, so it can be specified/used as glfw callback
this static callback method makes use of the singleton and passes the callback parameters to an instance method
this instance method actually handles the callback parameters, with the benefit of being able to access the member variables
This is what it looks like:
// Input.h (the actual callback class for glfwSetMouseButtonCallback)
class Input
{
public:
static Input& getInstance() // Singleton is accessed via getInstance()
{
static Input instance; // lazy singleton, instantiated on first use
return instance;
}
static void mouseButtonCallback(int key, int action) // this method is specified as glfw callback
{
//here we access the instance via the singleton pattern and forward the callback to the instance method
getInstance().mouseButtonCallbackImpl(key, action);
}
void mouseButtonCallbackImpl(int key, int action) //this is the actual implementation of the callback method
{
//the callback is handled in this instance method
//... [CODE here]
}
private:
Input(void) // private constructor necessary to allow only 1 instance
{
}
Input(Input const&); // prevent copies
void operator=(Input const&); // prevent assignments
};
and in my main.cpp:
Input &hexmap = Input::getInstance(); // initialize the singleton
//The glfw callback is set up as follows:
glfwSetMouseButtonCallback( &Input::mouseButtonCallback); // specifying the static callback method, which internally forwards it to the instance method
There is a C++ syntax for pointing to class member methods but you cannot pass them to a C style API. C understands function calls and every non-static object method, taking your events as an example, looks like this thinking in C terms: void events(void* this, int, int); meaning that every method apart from the standard arguments also gets a this pointer silently passed.
To make your events C compatible make it static void events(int, int);. This way it will follow the C calling semantics - it will not require a this pointer getting passed. You have to also somehow pass your object to this callback in some other manner (if you need this object's data in the callback).
I had the same problem and after reading this thread I came up with a similar solution. I think it is a bit cleaner this way. It's based on static function but it is nested inside the class where we set all things.
Header looks like this:
class Application
{
public:
...
private:
...
void MousePositionCallback(GLFWwindow* window, double positionX, double positionY);
void KeyboardCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
...
class GLFWCallbackWrapper
{
public:
GLFWCallbackWrapper() = delete;
GLFWCallbackWrapper(const GLFWCallbackWrapper&) = delete;
GLFWCallbackWrapper(GLFWCallbackWrapper&&) = delete;
~GLFWCallbackWrapper() = delete;
static void MousePositionCallback(GLFWwindow* window, double positionX, double positionY);
static void KeyboardCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
static void SetApplication(Application *application);
private:
static Application* s_application;
};
};
And the source code:
void Application::GLFWCallbackWrapper::MousePositionCallback(GLFWwindow* window, double positionX, double positionY)
{
s_application->MousePositionCallback(window, positionX, positionY);
}
void Application::GLFWCallbackWrapper::KeyboardCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
s_application->KeyboardCallback(window, key, scancode, action, mods);
}
void Application::GLFWCallbackWrapper::SetApplication(Application* application)
{
GLFWCallbackWrapper::s_application = application;
}
Application* Application::GLFWCallbackWrapper::s_application = nullptr;
void Application::MousePositionCallback(GLFWwindow* window, double positionX, double positionY)
{
...
}
void Application::KeyboardCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
...
}
void Application::SetCallbackFunctions()
{
GLFWCallbackWrapper::SetApplication(this);
glfwSetCursorPosCallback(m_window, GLFWCallbackWrapper::MousePositionCallback);
glfwSetKeyCallback(m_window, GLFWCallbackWrapper::KeyboardCallback);
}
Inspired by N0vember's answer, I present you even more generic and dynamic solution:
class MyGlWindow {
public:
std::function<void(MyGlWindow*)> onClose;
std::function<void(MyGlWindow*, int, int, int)> onMouseClick = [](auto self, int, int, int) { /*some default behavior*/ };
};
void makeWindow() {
GLFWwindow* glfwWindow;
MyGlWindow* myWindow;
/* ... Initialize everything here ... */
glfwSetWindowUserPointer(glfwWindow, myWindow);
#define genericCallback(functionName)\
[](GLFWwindow* window, auto... args) {\
auto pointer = static_cast<MyGlWindow*>(glfwGetWindowUserPointer(window));\
if (pointer->functionName) pointer->functionName(pointer, args...);\
}
glfwSetWindowCloseCallback(glfwWindow, genericCallback(onClose));
glfwSetMouseButtonCallback(glfwWindow, genericCallback(onMouseClick));
myWindow->onMouseClick = [](auto self, int, int, int) {
std::cout << "I'm such a rebel" << std::endl;
self->onClose = [](auto self) {
std::cout << "I'm such a rebellion" << std::endl;
};
};
}
In the header file make the events(int, int) to a static method. That solved the issue for me.
class GAME
{
private:
bool running;
public:
GAME();
int execute();
static void events(int, int); //Changed here to static void
int loop();
int render();
};
This is a useful discussion of possible solutions that helped me with the same problem, and I'm adding my solution in case it proves useful.
Problem Statement
My scenario is more general than the ones addressed by BIC, L.Senionis, and N0vember. In particular, my use case requires:
Generally, instance's data must be accessible to the callback
Many applications can be created using a common set of response handlers
In an application, any number of windows may be created
The set of callbacks attached to each window should be mixed and matched from a certain library of possible responders.
Proposed Solution Usage
The simple singleton design no longer solves the problem. Instead, I provide a GLFWResponder superclass that handles all of the setup complexity. In order to use the class and attach response to a window, here is what is required.
// Implement custom responder
class MyResponder : public GLFWResponder {
public:
virtual void cursor_position_callback(GLFWwindow* w, double x, double y) {...}
... override relevant callbacks ...
};
// in main ************************************************
// Assuming initialized GLFWwindow* my_window and my_other_window
MyResponder resp;
MyResponder resp2; // Can be another subclass of GLFWResponder
// Two responders can respond to same window
resp.respond_to(my_window, GLFWResponder::CURSOR_POSITION);
resp2.respond_to(my_window, GLFWResponder::CURSOR_POSITION);
// One responder can respond to multiple windows
resp2.respond_to(my_other_window, GLFWResponder::CURSOR_POSITION);
// One window can have different handlers for different events
resp.respond_to(my_other_window, GLFWResponder::CURSOR_ENTER);
Proposed Solution Implementation
Here is the sketch of the GLFWResponder implementation, fully functional, but with some TODO's. There may be some implications on performance, which I have not yet investigated.
// GLFWResponder.h ************************************************
/**
* Responder superclass that allows subclasses to handle events from multiple
* GLFW windows (which have only C API for callbacks).
* Callbacks are automatically cleaned up when responder goes out of scope.
*/
class GLFWResponder {
public:
virtual ~GLFWResponder();
// Interface -----------------------------------
enum GLFWEventType {
CURSOR_POSITION = 0,
CURSOR_ENTER = 1
// TODO: add support for other callbacks
};
void respond_to(GLFWwindow* window, GLFWEventType event);
bool does_respond_to(GLFWwindow* window, GLFWEventType event) const;
// Subclasses implement ------------------------
virtual void cursor_position_callback(GLFWwindow* window, double xpos, double ypos);
virtual void cursor_enter_callback(GLFWwindow* window, int entered);
// TODO: add support for other callbacks
// Under the hood ------------------------------
static std::set<GLFWResponder*> getResponders(GLFWwindow* windo, GLFWEventType event);
private:
// Windows and events that this instance responds to
std::set<std::pair<GLFWwindow*, GLFWEventType> > enabled_events_;
// Global responders keyed by events they respond to
// (each responder knows which windows it responds to)
static std::map<GLFWEventType, std::set<GLFWResponder*> > responders_;
};
// GLFWResponder.cpp **************************************************
namespace {
void cursor_position_callback_private(GLFWwindow* window, double xpos, double ypos) {
for (GLFWResponder* r : GLFWResponder::getResponders(window, GLFWResponder::CURSOR_POSITION)) {
r->cursor_position_callback(window, xpos, ypos);
}
}
void cursor_enter_callback_private(GLFWwindow* window, int entered) {
for (GLFWResponder* r : GLFWResponder::getResponders(window, GLFWResponder::CURSOR_ENTER)) {
r->cursor_enter_callback(window, entered);
}
}
} // namespace
std::map<GLFWResponder::GLFWEventType, std::set<GLFWResponder*> > GLFWResponder::responders_;
GLFWResponder::~GLFWResponder() {
for (auto& pr : responders_) {
pr.second.erase(this);
}
// TODO: also clean up window's callbacks
}
void GLFWResponder::respond_to(GLFWwindow* window, GLFWResponder::GLFWEventType event) {
enabled_events_.insert(std::make_pair(window, event));
responders_[event].insert(this);
if (event == CURSOR_POSITION) {
glfwSetCursorPosCallback(window, cursor_position_callback_private);
} else if (event == CURSOR_ENTER) {
glfwSetCursorEnterCallback(window, cursor_enter_callback_private);
} else {
// TODO: add support for other callbacks
LOG(FATAL) << "Unknown GLFWResponder event: " << event;
}
}
bool GLFWResponder::does_respond_to(GLFWwindow* window, GLFWEventType event) const {
return enabled_events_.find(std::make_pair(window, event)) != enabled_events_.end();
}
std::set<GLFWResponder*> GLFWResponder::getResponders(
GLFWwindow* window, GLFWEventType event) {
std::set<GLFWResponder*> result;
auto it = responders_.find(event);
if (it != responders_.end()) {
for (GLFWResponder* resp : it->second) {
if (resp->does_respond_to(window, event)) {
result.insert(resp);
}
}
}
return result;
}
void GLFWResponder::cursor_position_callback(
GLFWwindow* window, double xpos, double ypos) {
// TODO: fail with message "GLFWResponder::do_respond called on a subclass that does not implement a handler for that event"
}
void GLFWResponder::cursor_enter_callback(GLFWwindow* window, int entered) {
// TODO: fail with message "GLFWResponder::do_respond called on a subclass that does not implement a handler for that event"
}