在我创建跨平台GUI框架的过程中,我遇到了以下障碍:
假设我有一个中心的“窗口”类,在项目的一般,平台无关的包含文件夹:
假设我有一个中心的“窗口”类,在项目的一般,平台无关的包含文件夹:
//include/window.hpp class Window { //Public interface }
那么我有几个平台依赖的实现类,像这样:
//src/{platform}/window.hpp class WinWindow {...}; //Windows class OSXWindow {...}; //OSX class X11Window {...}; //Unix
最后,有一个原始的Window类’.cpp文件,其中我想把实现类绑定到一般类.纯粹在概念上,这就是我想要做的:
//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
我知道这不是有效的C,这就是重点.我已经想到了解决这个问题的两种可能的方式,而且我有两个问题.
pImpl样式实现
Make Window保存一个指向实现类的void指针,并将其分配给每个平台的不同窗口类.但是,每次我想执行平台依赖操作时,我都必须重写指针,更不用说包含依赖于平台的文件了.
预处理指令
class Window : #ifdef WIN32 private WinWindow #else ifdef X11 private X11Window //etc.
该怎么办?我应该完全改变我的设计吗?我的任何一种可能的解决方案都能持有一点点水?
解决方法
使用typedef隐藏预处理器
您可以简单地键入相应的窗口类型:
#ifdef WINDOWS typedef WinWindow WindowType; #elif defined // etc
那么你的窗口类可以是:
class Window : private WindowType { };
这不是一个非常强大的解决方案.最好是以更加面向对象的方式思考,但C中的OO编程是运行时代价,除非你使用
好奇地重复模板模式
您可以使用好奇的重复模板模式:
template<class WindowType> class WindowBase { public: void doSomething() { static_cast<WindowType *>(this)->doSomethingElse(); } };
那你可以做
class WinWindow : public WindowBase<WinWindow> { public: void doSomethingElse() { // code } };
并使用它(假设C 14支持):
auto createWindow() { #ifdef WINDOWS return WinWindow{}; #elif UNIX return X11Window{}; #endif }
仅限C 11:
auto createWindow() -> #ifdef WINDOWS WinWindow #elif defined UNIX X11Window #endif { #ifdef WINDOWS return WinWindow{}; #elif defined UNIX return X11Window{}; #endif }
我建议您使用自动,或使用它与typedef结合使用:
auto window = createWindow(); window.doSomething();
面向对象风格
你可以使你的Window类成为一个抽象类:
class Window { protected: void doSomething(); public: virtual void doSomethingElse() = 0; };
然后将依赖于平台的类定义为Window的子类.那么你所要做的就是在一个地方有预处理指令:
std::unique_ptr<Window> createWindow() { #ifdef WINDOWS return new WinWindow; #elif defined OSX return new OSXWindow; // etc }
不幸的是,这通过调用虚拟函数来产生运行时代价. CRTP版本在编译时解决了对“虚拟函数”的调用,而不是在运行时调用.
此外,这需要在堆上声明窗口,而CRTP不会;这可能是一个问题,取决于用例,但一般来说,这并不重要.
最终,您必须在某个地方使用#ifdef,因此您可以确定平台(或者您可以使用确定平台的库,但也可能使用#ifdef),该问题只是隐藏它的位置.