このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
ホストでは、C++/CLI を使って開発をすることになるが、
既存の C++ コードと混ざる形となるため、
プログラムの見通しが悪くなりやすい。
そこで、ホストのコードを機能に合わせて分割しよう。
C++ ピュアな部分と C++/CLI の部分にだ。
そうしておけば、C++/CLI の導入を最低限で済ませられる。
1. scrnsave.lib 用の関数実装 (module.cpp, C++)
2. Application クラス(Application.cpp, C++)
3. Dispatcher クラス(Dispatcher.cpp, C++/CLI)
module.cpp には、scrnsave.lib を使うために必要な、
コールバック関数 3 つを含めて実装する。
つまり、ライブラリ仕様を満たすためだけの存在とする。
オブジェクト指向開発においては、
なるべくグローバル空間に名前を置きたくないため、
これら関数は、Application クラスのメソッドを呼ぶだけの、
単なるラッパー関数という感じである。
Application.cpp は、module.cpp から呼び出され、
scrnsave.lib のコールバックを処理するコードを記述する。
プラグインを呼び出す等、C++/CLI が必要な処理は、
このクラスに書かず、Dispatcher クラスに置く。
Dispatcher.cpp には、プラグインを呼び出す処理を置く。
C++/CLI が必要なコードはここに集約することになる。
例によって、普通はここまで分ける必要はないんだろうが、
単に、静的メソッドやグローバルを最大限排除し、
名前空間を導入するのが好きなだけだ。
では、最初に Application クラスの仕様を決める。
========== Application.hpp ==========
#ifndef z_loafer_screensaver_host_application
#define z_loafer_screensaver_host_application
#include <windows.h>
#include "Dispatcher.hpp"
namespace Loafer {
namespace ScreenSaver {
namespace Host {
class Application {
public:
Application();
virtual ~Application();
virtual BOOL RegisterClasses(HANDLE hInst);
virtual BOOL ConfigureProc(
HWND hdlg, UINT message,
WPARAM wParam, LPARAM lParam);
virtual LRESULT MainProc(
HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam);
protected:
Dispatcher dispatcher;
private:
Application(const Application &);
Application &operator =(const Application &);
};
}}} // Loafer::ScreenSaver::Host
#endif // !z_loafer_screensaver_host_application
========== end of Application.hpp ==========
何のことはない。scrnsave.lib の規約を、
単にクラスのメソッドに置き換えただけだ。
ここではとりあえずクラスにしておく。
こだわるなら、dispatcher メンバを削除し、
IApplication 等のインタフェースにするんだろうが、
そこまですると、もはやオブジェクト指向症候群だ。
次に、Dispatcher クラスの仕様を決める。
このクラスの内部では C++/CLI を使うことになるが、
外部に見せる仕様は C++ だけで書くようにしておく。
でないと、呼び出し側にも C++/CLI が必要になってしまう。
========== Dispatcher.hpp ==========
#ifndef z_loafer_screensaver_host_dispatcher
#define z_loafer_screensaver_host_dispatcher
#include <windows.h>
namespace Loafer {
namespace ScreenSaver {
namespace Host {
class Dispatcher {
public:
Dispatcher();
virtual ~Dispatcher();
virtual void Configure(HWND hdlg);
virtual void Create(HWND hwnd);
virtual void Destroy(HWND hwnd);
virtual void Paint(HWND hwnd);
private:
Dispatcher(const Dispatcher &);
Dispatcher &operator =(const Dispatcher &);
};
}}} // Loafer::ScreenSaver::Host
#endif // !z_loafer_screensaver_host_dispatcher
========== end of Dispatcher.hpp ==========
こりゃ解りやすい分け方になった。
簡単にいえば、ただのメッセージハンドラだ。
Application クラスのメソッドは、
ウィンドウプロシージャなので、
そこで拾った一部のメッセージを、
Dispatcher のメソッドに振り分ける感じだ。
メソッドの分け方は、ISaver インタフェースに似せてある。
つまり、Dispatcher クラスが、
ウィンドウプロシージャ仕様(Windows API)と、
ISaver 仕様の間を変換する Adapter となるわけだ。
これで、ホストの実装は分割され、
module.cpp, Application.cpp, Dispatcher.cpp の
3 つのソースファイルにて定義されることとなる。