Search code examples
c++templatesdependenciespolicy

how to design policy-class-implementations that depends on each other


I am trying to learn policy-based class design. Now I have the case, that 2 different policy-class-implementations depends on each other. That means, that the second policy implementation (GLFWInputHandler) depends on specific internals of the first implementation (GLFWVideoModeSetter). That is, because the video-mode-setting AND the input-handling is internally realized with the GLFW-Framework.The InputHandler needs a concrete glfwWindow, which is created by the VideoModeSetter.

At first, here is a minimal, compilable example with NO dependencies between the both policy-class-implementations. All is fine here.

struct GLFWVideoModeSetter { void setVideoMode() {} };

template <class VideoModeSettingPolicy>
struct VideoModeManager : public VideoModeSettingPolicy {};

struct GLFWInputHandler { bool handleKeys() { return true; } };

template <class InputHandlerPolicy>
struct InputHandlerManager : public InputHandlerPolicy { };

int main()
{
    VideoModeManager<GLFWVideoModeSetter> oVideoManager;
    oVideoManager.setVideoMode();

    InputHandlerManager<GLFWInputHandler> oInputHandlerManager;
    oInputHandlerManager.handleKeys();
    return 0;
}

Now I am searching for a solution to extend the above code without loosing the policy-based flexibility, so that the GLFWInputHandler and GLFWVideoModeSetter are connected in a way, that the GLFWInputHandler is able to get the glfwWindow. My first solution was, to template the GLFWInputHandler and then specialize the InputHandlerManager, but it feels not correct to do it this way. How would you handle this dependency?

struct glfWindow {};

struct GLFWVideoModeSetter
{
    void setVideoMode() {}
    glfWindow *getGLFWWindow() { return new glfWindow(); }; //GLFWInputHandler depends on this -> glfWindow
};

template <class VideoModeSettingPolicy>
struct VideoModeManager : public VideoModeSettingPolicy {};

template <class T_GLFW_WINDOW_GETTER>
struct GLFWInputHandler
{
    GLFWInputHandler(T_GLFW_WINDOW_GETTER &refWindowGetter) : ptrWindowGetter(&refWindowGetter) {}
    bool handleKeys() { return true; }
private:
    T_GLFW_WINDOW_GETTER *ptrWindowGetter;
};

template <class InputHandlerPolicy>
struct InputHandlerManager : public InputHandlerPolicy {};

template <>
struct InputHandlerManager<GLFWInputHandler<VideoModeManager<GLFWVideoModeSetter>>> : public GLFWInputHandler<VideoModeManager<GLFWVideoModeSetter>>
{
    InputHandlerManager(VideoModeManager<GLFWVideoModeSetter> &refWGType) : GLFWInputHandler<VideoModeManager<GLFWVideoModeSetter>>(refWGType) {}
};


int main()
{
    VideoModeManager<GLFWVideoModeSetter> oVideoManager;
    oVideoManager.setVideoMode();

    InputHandlerManager<GLFWInputHandler<VideoModeManager<GLFWVideoModeSetter>>> oInputHandlerManager(oVideoManager);
    oInputHandlerManager.handleKeys();

    return 0;
}

Solution

  • Wouldn't it be the case that any input handler would need a specific window to post the input to? If so, I'd rather recommend the following:

    At first, keep the input handler as normal class. We are within GLFW framework anyway, there is no issue with getting a GLFWVideoModeSetter directly:

    struct GLFWInputHandler
    {
        GLFWInputHandler(GLFWVideoModeSetter& windowGetter) : windowGetter(windowGetter) {}
        bool handleKeys() { return true; }
    private:
        GLFWVideoModeSetter& windowGetter; // why a pointer, by the way???
    };
    

    Problem now is, you need an appropriate constructor for your InputHandlerManager. First variant: We assume that any input handler needs a specific video mode setter (at least some kind of display getter):

    template <class InputHandlerPolicy>
    struct InputHandlerManager : public InputHandlerPolicy
    {
        template<class VideoModeGetter>
        InputHandlerManager(VideoModeGetter& vmg)
            : InputHandlerPolicy(vmg)
        { };
    };
    

    Usage:

    VideoModeManager<GLFWVideoModeSetter> vm;
    InputHandlerManager<GLFWInputHandler> ihm(vm); // can still pass directly as VMM inherits from the policy...
    

    If you fear you might need more flexibility, you can have a more generic constructor using a variadic template approach:

    template <class InputHandlerPolicy>
    struct InputHandlerManager : public InputHandlerPolicy
    {
        template<typename ... Parameters>
        InputHandlerManager(Parameters ... parameters)
            : InputHandlerPolicy(parameters ...)
        { };
    };
    

    Usage in the concrete case remains the same, but with some different policies, you could provide different parameters:

    VideoModeManager<XVideoModeSetter> vm;
    InputHandlerManager<XInputHandler> ihm; // does not need a parameter at all
    
    VideoModeManager<YVideoModeSetter> vm;
    InputHandlerManager<YInputHandler> ihm(vm, 7); // needs an additional parameter
    

    Not sure, though, if this follows the spirit of the policy based design: PBD should normally allow you to use any combination of different policies, but a GLFWInputHandler always relies on a GLFW window and thus on a GLFW VMS, so you always build pairs of policies in this case...

    It might be more appropriate to move the part of getting the keys from the input handler to the video mode setter and pass the keys to the input handler as parameter, possibly additionally providing some callback(s/-object) to the IH so it can provoke appropriate reactions at the VMS. Possibly, InputHandler does not need to have any policies at all any more. If you need policies, you might want to be able to change the reactions on different keys, but independently from which framework you used to get them...

    Counter example where you might want to have different policies: Imagine you have different data provider policies (from file, database, user input, ...) and want to calculate a signature for - so you have another policy family (md5, sha, ...) and you could combine any input with any hash algorithm.