1
\$\begingroup\$

I would like to split my game engine in different libraries, so it is easier to maintain, however i have one problem: for example the window creation is handled by GLFW, so I encapsulated it into a class so it will be able to interact with the rest of my library. The problem is that I don't want to make the applications that use my library also depend on GLFW development headers, because otherwise when I pass user input to the application it must know about GLFW and it's data types and has to do something like this:

    //Application.cpp
    #include<mylibrary.h>

    int main()
    {
        myLibrary::window w("Title", 640, 480)
        myLibrary::event e;

        while(w.pollEvents(&e))
        {
            if(e.key == GLFW_KEY_ESCAPE) //Unnecessary GLFW dependancy
                w.close();
        }
    }

Because my header is like this:

    //mylibrary.h
    #include <glfw/glfw3.h>

    namespace myLibrary
    {
        class window
        {
        public:
            //Blah-blah-blah  
        private:
            //Blah-blah-blah
            GLFWwindow* mWindow;
            //Blah-blah-blah
        };
    }

How should I go about abstracting GLFW from the application?

And I cannot use GLFW without wrapping it, because making it interact with the rest of my library ecosystem will look very ugly and out of place, and it would mean that the other libraries not only have to know about the rendering library, but also GLFW.

TL;DR: How can I hide GLFW from the user of my library?

\$\endgroup\$
1
\$\begingroup\$

There are a few options:

  1. Opaque data types.

In your mylibrary.h file, you would declare a synonym for void* that is published publicly, but internally it would be hold the specific type you need. For example, you could have:

namespace myLibrary
{
    typedef void* MyLibWindowPtr;

    class window
    {
    public:
        //Blah-blah-blah  
    private:
        //Blah-blah-blah
        MyWindowPtr mWindow;
        //Blah-blah-blah
    };
}

In your myLibrary.cpp file, you would create a pointer of the proper type:

window::window()
{
    mWindow = glfwCreateWindow(...);
}

This has some downsides, such as removing types from the pointer. That leads to casts and potentially using the value incorrectly.

  1. Use an implementation pointer.

This is similar to the above, but works a little differently. In this case, you might expose an actual interface that users of the library can see, but its implementation is behind an opaque pointer. This allows you to have different implementations on different systems. (For example, I've used this to wrap CGL* types on macOS, but EAGL* types on iOS, so the same code can call them.) It might look something like this:

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

        virtual void ResizeWindow() = 0;
        //... any other methods you want publicly available
};

That would be public in a header included by mylibrary.h. Then you'd have a private header that defines the concrete implementation:

class GLFWImplementation : public WindowInterface
{
    public:
        GLFWImplementation();
        ~GLFWImplementation();

        virtual void ResizeWindow() override;
    // ... etc.
};

You would then have a factory method that return the concrete class. Something like this:

WindowInterface* createNewWindow()
{
    return new GLFWImplementation();
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.