1

I am trying to design a window object whose job is to handle all teh funcitonality of a GLFW window (intilization, callbacks, input handling...)

One of the most important things is the render loop. The most naive design I can think for that is to have the renderloop method to take a function pointer with no arguments and then call it inside the loop method, as follows:

class Window
{
public:
    Window();
    ~Window();

    void WindowLoop(void (*f) (void));
protected:
    GLFWwindow* window;
};


void Window::WindowLoop(void (*f) (void))
{
    while(!glfwWindowShouldClose(window))
    {
        f();
        glfwPollEvents();
    }
}

This however imposes a lot of limitations. For one, it implies the function cannot take any arguments. Which may or may not be a problem.

I have done some research and apparently you can pass funciton pointers that take an arbitrary number of parameters, but this seems to be both difficult and advised against.

Another option would be a general functor, and then the parameters could be defined as part of the class/struct, avoiding having to handle that.

There are probably other potential designs that I am not aware of as well.

Which would be a good design in C++ for a render loop, trying to prioritize first versatility of use and second, execution speed?

Makogan
  • 8,208
  • 7
  • 44
  • 112

1 Answers1

1

My short answer would have been:

Use std::function instead of a raw function pointer. This gives you much more flexibility as it can hold:

  • function pointers
  • method pointers (with object, of course)
  • functors
  • lambdas (with or without captures which actually repeats the one or other above).

You still are constrained to define a signature for call but you can give your call-back context which is probably all what you need.

So, this is how it could look:

#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();
    }
}

(It looks not much different than the original sample of OP.)


Thinking twice, I found it worth to mention widget sets and what solutions they provide (as having the same issue to solve).

The two general solutions are

  • signals / signal handlers (signal slot concept)
  • virtual methods for event handlers which can be overridden.

A signal is basically nothing else than a container with function pointers (or std::function or something comparable). The signal is emitted (i.e. the stored function pointers are called) in certain situations. Thus, other objects can get notified in that situations (by registering their signal handler in the signal). So, a signal is actually similar like the above except that the function pointer(s) are not temporarily provided but stored in member variables.

The alternative is to call a virtual method in certain situations. To add custom behavior, the resp. base class has to be derived and the virtual methods in quest have to be overridden.

In OP's case, this could look like this:

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 */ }

To use that in an application, a derived class of Window is mandatory:

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

Concerning Qt, there are signals for various situations and virtual methods for e.g. event handlers in widgets. There is mostly the choice of either/or – I cannot remember that both is available for something. E.g. there can be a signal handler registered for a QPushButton::clicked() but to customize event handlers as mousePressEvent(), a Qt widget has to be overloaded to override the event handler method. (There is also the concept of event filters but IMHO this is not exactly the same.)

gtkmm instead (at least in version 2 I used in the past) provided for everything I can remember virtual methods and signals. So, there was always the choice to either derive a gtkmm widget or change/extend the behavior of a gtkmm widget just by registering a signal handler. This might introduce little extra performance costs but it was very convenient for application programming.

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • This was really helpful, unfortunately `std::function` uses exceptions (which I am fully avoiding with this project), so I think it will either be functor way or the derived class way – Makogan Feb 04 '19 at 15:21
  • @Makogan Regarding `std::function` and exceptions, I found this [SO: std::function with noexcept in C++17](https://stackoverflow.com/q/41293025/7478597), [Reddit: std::function doesn't support C++17 noexcept, is this a library defect?](https://www.reddit.com/r/cpp/comments/7vn17u/stdfunction_doesnt_support_c17_noexcept_is_this_a/) and [std::function move operations should be noexcept](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0771r0.pdf). Concerning the latter, you may try `std::function &&f`. I must admit I'm not yet that familiar with the universal references... – Scheff's Cat Feb 04 '19 at 15:34
  • ...and therefore didn't use it in this answer but it should work as well. – Scheff's Cat Feb 04 '19 at 15:35