今後のコースワークでは、仮想を避けて、パフォーマンス/メモリ効率をさらに絞り出せるかどうかを確認しようとしています。StateManager を実装したかったときに問題が発生しました (コースワーク プロトタイプは小さなゲームです)。
それを可能にするために、現在の状態のメンバー関数呼び出しを「状態化」するためのテンプレートがあります。
状態の例は次のとおりです。
struct TestState {
bool update(float delta) {
return true;
}
};
これで、状態マネージャーは任意の状態クラスを取り、更新機能をバインドできるようになります。これはboost::bindとboost::functionで簡単に実現できましたが、virtualに比べて性能低下があるので、boostなしでできるか見てみたいと思いました。
理論的には、State が静的関数のみを含むクラス/構造体であれば簡単ですが、それによって柔軟性がなくなり、さらに静的な初期化が混乱します。
StateManager の現在のリビジョンは次のとおりです。
class StateManager
{
public:
template <typename S> static void set(S* state)
{
#define UPDATE_ID 0
StateFunctionHandler<UPDATE_ID, S, bool, float>::bind(std::mem_fun1(&S::update), state);
m_funcUpdate = StateFunctionHandler<UPDATE_ID, S, bool, float>::exec;
}
static bool update(float delta)
{
return m_funcUpdate(delta);
}
typedef bool (*funcUpdate)(float);
private:
static funcUpdate m_funcUpdate;
};
StateFunctionHandler は、メンバー関数を StateFunctionHandler の静的メンバーにラップする役割を持つテンプレートです。テンプレートの最初のパラメーターは、同じ戻り値の型と引数を持つ関数に対して複数のテンプレートを強制するための ID です。
コードでは次のようになります。
template <int ID, typename S, typename R, typename A>
struct StateFunctionHandler
{
static void bind(std::mem_fun1_t<R, S, A> f, S* s)
{
func = f;
pState = s;
}
static R exec(A arg)
{
return func(pState, arg);
}
static std::mem_fun1_t<R, S, A> func;
static S* pState;
};
ここでの問題は、使用されるケースの StateFunctionHandler のテンプレート化された静的メンバーを初期化することです。これは、コンパイラーに強制的に初期化させる必要があるためです。
これらのクラスに関連付けられた cpp ファイルでは、静的メンバーが初期化されます。
template <int ID, typename S, typename R, typename A> std::mem_fun1_t<R, S, A> StateFunctionHandler<ID, S, R, A>::func;
template <int ID, typename S, typename R, typename A> S* StateFunctionHandler<ID, S, R, A>::pState = NULL;
StateManager::funcUpdate StateManager::m_funcUpdate = NULL;
しかし、StateFunctionHandler::func と ::pState のリンク エラー (LNK2001) が発生するため、コンパイラは StateFunctionHandler の静的メンバーを初期化していないようです。
使用例:
TestState* t = new TestState();
StateManager::set(t);
StateManager::update(0.1f);
明確にするために、私はブーストを使用したくありません。これは、このユースケースで仮想関数を回避し、パフォーマンスも向上させることができるかどうかを確認するための実験です。