クロスプラットフォームの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 ++ではないことを知っています、そしてそれがポイントです。私はこの問題を解決するために2つの可能な方法を考えました、そして私は両方に問題があります。
pImplスタイルの実装
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 ++でのオブジェクト指向プログラミングは、使用しない限り、実行時のコストがかかります。
不思議なことに繰り返されるテンプレートパターンを使用できます。
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
}
使用するauto
とき、またはtypedefと組み合わせて使用することをお勧めします。
auto window = createWindow();
window.doSomething();
Window
クラスを抽象クラスにすることができます。
class Window {
protected:
void doSomething();
public:
virtual void doSomethingElse() = 0;
};
次に、プラットフォームに依存するクラスをのサブクラスとして定義しますWindow
。次に、あなたがしなければならないのは、プリプロセッサディレクティブを1か所に置くことだけです。
std::unique_ptr<Window> createWindow() {
#ifdef WINDOWS
return new WinWindow;
#elif defined OSX
return new OSXWindow;
// etc
}
残念ながら、これには仮想関数の呼び出しによる実行時コストが発生します。CRTPバージョンは、実行時ではなくコンパイル時に「仮想関数」への呼び出しを解決します。
さらに、これWindow
にはヒープ上で宣言する必要がありますが、CRTPでは宣言しません。これはユースケースによっては問題になるかもしれませんが、一般的にはそれほど重要ではありません。
最終的には、#ifdef
どこかを使用する必要があるため、プラットフォームを決定できます(または、プラットフォームを決定するライブラリを使用できますが、おそらくそれ#ifdef
も使用します)。問題は、それをどこに隠すかです。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加