Let’s say we have interface window_creator that responsible for creation of windows. For simplicity it looks like this:
struct window_creator
{
virtual ~window_creator(){}
handle create_window(size_t width, size_t height, bool fullscreen);
};
Also, we have interface for render system that is responsible for rendering in window created by window_creator. It has, for simplicity, following code:
struct render_system
{
virtual ~render_system(){}
void create(handle window_handle);
};
Also, we have 2 implementations for window_creator interface:
struct windows7_creator : public window_creator
{
handle create_window(size_t width, size_t height, bool fullscreen) override {
//calls CreateWindow and returns HWND
}
};
struct macos_creator : public window_creator
{
handle create_window(size_t width, size_t height, bool fullscreen) override {
//calls createWindow_ and returns WindowRef
}
};
And 2 implementations for render_system interface:
struct dx_render_system : public render_system
{
void create(handle window_handle) override{
//calls CreateDevice, etc...
}
};
struct opengl_render_system : public render_system
{
void create(handle window_handle) override{
//calls gl... etc...
}
};
Now on windows platform I can create dx or OpenGL renderer. And on MacOs I can create OpenGL renderer.
The question is: what type of handle should I create to support independence of window_creator and render_system interfaces?
In my current implementation I wrote typedef for handle:
typedef void* handle;
Is there any more elegant solution to this problem?
Generally this sort of thing is done with the following pattern:
You do not need to do runtime dispatching of the different NativeHandles – and in fact without sacrificing type-safety it is nearly impossible to do – as the definition of the platform-specific handles is not available on every platform.
You already know statically at compile-time what operating system you are on, so you may as well take advantage of that.