In my quest to create a cross-platform GUI Framework, I have hit the following snag:
Suppose I have a central "Window" class, in the project's general, platform-independent include folder:
//include/window.hpp
class Window
{
//Public interface
}
//src/{platform}/window.hpp
class WinWindow {...}; //Windows
class OSXWindow {...}; //OSX
class X11Window {...}; //Unix
//src/window.cpp
//Suppose we're on Windows
#include "include/window.hpp"
#include "src/win/window.hpp"
class Window : private WinWindow; //Redefine Window's inheritance
class Window :
#ifdef WIN32
private WinWindow
#else ifdef X11
private X11Window //etc.
You could simply typedef the appropriate window type instead:
#ifdef WINDOWS
typedef WinWindow WindowType;
#elif defined // etc
Then your window class could be:
class Window : private WindowType {
};
This isn't a very robust solution, though. It is better to think in a more Object Oriented way, but OO programming in C++ comes at a runtime cost, unless you use the
You could use the curiously repeating template pattern:
template<class WindowType>
class WindowBase {
public:
void baseClassFunction() {
static_cast<WindowType *>(this)->doSomething();
}
};
Then you could do
class WinWindow : public WindowBase<WinWindow> {
public:
void doSomething() {
// code
}
};
And to use it:
template<class WindowType>
WindowBase<WindowType> createWindow() {
#ifdef WINDOWS
return WinWindow;
#elif defined // etc
}
You could make your Window
class be an abstract class:
class Window {
protected:
void doSomething();
public:
virtual void doSomethingElse() = 0;
};
Then define your platform-dependent classes as subclasses of Window
. Then all you'd have to do is have the preprocessor directives in one place:
std::unique_ptr<Window> createWindow() {
#ifdef WINDOWS
return new WinWindow;
#elif defined OSX
return new OSXWindow;
// etc
}
Unfortunately, this requires that the Window
s be placed on the heap, and it incurs a runtime cost through calls to the virtual function. The CRTP version resolves calls to the "virtual function" at compile time instead of at runtime.
Ultimately, you do have to use the #ifdef
somewhere, so you can determine the platform (or you could use a library that determines the platform, but it probably uses #ifdef
too), the question is just where to hide it.