Мой короткий ответ был бы:
Использовать std::function
вместо необработанного указателя функции.Это дает вам гораздо большую гибкость, так как может содержать:
- указатели на функции
- указатели на метод (с объектом, конечно)
- функторы
- лямбды (с перехватами или без них, которые фактически повторяют одно или другое выше).
Вы по-прежнему ограничены определением подписи для вызова, но вы можете указать свой контекст обратного вызова, который, вероятно, является всем, чем вынужно.
Итак, вот как это могло бы выглядеть:
#include <functional>
class Window
{
public:
Window();
~Window();
void WindowLoop(std::function<void()> f);
protected:
GLFWwindow* window;
};
void Window::WindowLoop(std::function<void()> f)
{
while(!glfwWindowShouldClose(window))
{
f();
glfwPollEvents();
}
}
(выглядит не сильно отличающимся от оригинального образца OP.)
Мышлениедважды я нашел, что стоит упомянуть наборы виджетов и какие решения они предоставляют (как решение одной и той же проблемы).
Два основных решения - это
- обработчики сигналов / сигналов ( концепция сигнального слота )
virtual
методы для обработчиков событий, которые могут быть переопределены.
Сигнал, по сути, является не чем иным, как контейнером с указателями на функции (или std::function
или что-то сопоставимое),Сигнал испускается (т.е. вызываются указатели сохраненных функций) в определенных ситуациях.Таким образом, другие объекты могут получать уведомления в этих ситуациях (путем регистрации своего обработчика сигнала в сигнале).Таким образом, сигнал фактически аналогичен приведенному выше, за исключением того, что указатель (и) функции не предоставляются временно, а хранятся в переменных-членах.
Альтернативой является вызов метода virtual
в определенных ситуациях.Чтобы добавить пользовательское поведение, соотв.базовый класс должен быть получен, а методы virtual
в квесте должны быть переопределены.
В случае OP это может выглядеть так:
class Window
{
public:
Window();
~Window();
void WindowLoop();
protected:
virtual void step();
protected:
GLFWwindow* window;
};
void Window::WindowLoop()
{
while(!glfwWindowShouldClose(window))
{
step();
glfwPollEvents();
}
}
void Window::step() { /* empty placeholder */ }
Чтобы использовать это в приложениипроизводный класс Window
является обязательным:
class GameWindow: public Window {
protected:
virtual void step() override;
};
void GameWindow::step()
{
// Do the game step stuff (e.g. rendering)
// where this (of type GameWindow) can provide the necessary context.
}
Относительно Qt , существуют сигналы для различных ситуаций и virtual
методы, например, для обработчиков событий в виджетах.В основном есть выбор либо / или - я не могу вспомнить, что оба доступны для чего-то.Например, для QPushButton::clicked()
может быть зарегистрирован обработчик сигнала, но чтобы настроить обработчики событий как mousePressEvent()
, необходимо перегрузить виджет Qt, чтобы переопределить метод обработчика событий.(Существует также концепция фильтров событий, но, по-моему, это не совсем то же самое.) Вместо этого
gtkmm (по крайней мере, в версии 2, которую я использовал в прошлом) предоставлено все, что я могупомните virtual
методы и сигналы.Таким образом, всегда был выбор: извлечь виджет gtkmm или изменить / расширить поведение виджета gtkmm, просто зарегистрировав обработчик сигнала.Это может привести к небольшим дополнительным затратам на производительность, но это было очень удобно для программирования приложений.