私が変更するいくつかのことがあります。
まず、リターンを少し無視して、それとそれがboost::any
混ざったものに注目しましょうstd::string
。これは、潜在的なランタイム タイプの不一致を許容するため、イベント システムにとっては非常に悪い考えです。この種の間違いを犯す可能性のある設計は必要ありません (最終的には間違いが発生し、修正が必要なバグが発生し、将来の時間が無駄になるためです)。
通常、本当に必要なものは次のとおりです。
void Module::trigger_event(shared_ptr<Event> const& event)
{
// Event is a base interface
// pull off event->ID() to get identifier and lookup
// dispatch to something that has the signature
// void (shared_ptr<SpecificEvent>)
}
ディスパッチャーのようなことをするには、通常、
map<IDType, shared_ptr<Dispatcher>> dispatchDictionary_;
と
template <typename SpecificEvent>
class SpecificDispatcher : public Dispatcher
{
public:
SpecificDispatcher(function<void (shared_ptr<SpecificEvent>)> handler)
handler_(handler)
{}
virtual void dispatch(shared_ptr<Event> event)
{
auto specificEvent(static_ptr_cast<SpecificEvent>(event));
handler_(specificEvent);
}
};
(明白な方法で定義されたインターフェイス クラスと登録/登録解除メソッドを使用して、マップ内のイベント タイプ ID を関連付けます)。ポイントは、ID をクラス内の EventType に関連付け、常に同じ関連付けになるようにし、ハンドラーがデータ自体を再解釈する必要がない (エラーが発生しやすい) 方法でディスパッチすることです。ライブラリ内で自動化できる作業を実行して、外部で間違って実行されないようにします。
さて、あなたのメソッドで何を返しますか? 関数ではなく、オブジェクトを返します。明示的に呼び出す必要がない場合は、別の潜在的なエラーの原因を回避できます。これはすべての RAII と同じです。削除、ロック解除、または ... 同様に、「unregister_event」(またはそれを何と呼ぶか) を呼び出すことを覚えておく必要はありません。
プログラムには、式、関数スコープ、状態、およびプログラムの 4 つの有効期間があります。これらは、返されたオブジェクトを格納できる 4 つのタイプのものに対応しています。これは、2 つの非同期遷移イベントの間に存在する State クラス (State パターン内) のメンバーであるか、終了するまで待機しているグローバル スコープ オブジェクトです。
すべてのライフタイム管理は RAII で管理する必要があります。これは一種のデストラクタのポイントであり、例外に直面してリソースのクリーンアップを管理する方法です。
編集:私は、あなたが「明白な方法で」ドットを接続することを指摘して、述べられていない断片の束をちょっと残しました. しかし、私はもっと多くの部分を埋めようと思っていました (私は OS のビルドとインストールを行っており、私のバグは現在、インストールを待っている最後の 1 つにまで下がっています...)
ポイントは、誰かがただタイプできるべきだということでした
callbackLifetimeObject = module->set_event<StartEvent>([](shared_ptr<StartEvent> event){
cout << "Module started: " << event->someInfo() << endl;
});
そのため、set_event にはこれを取得するための種類の署名が必要であり、適切なディスパッチャーを辞書に挿入する必要があります。ここで型から ID を取得するには、いくつかの方法があります。「明白な」方法は、一時的なものを作成してそれを「ID」メンバーと呼ぶことですが、これにはオブジェクト作成のオーバーヘッドがあります。別の方法は、それを「仮想静的」に変換し、静的を取得することです(これは、仮想メソッドが行うすべてのことです)。仮想静的があるときはいつでも、それらを特性に変える傾向があります-同じことですが、カプセル化がわずかに改善され、「すでに閉じられているクラス」も変更できます。次に、仮想メソッドは特性クラスも呼び出して同じものを返します。
そう...
template <typename EventType>
struct EventTrait
{
// typedef your IDType from whatever - looks like you want std::string
static IDType eventID()
{ /* default impl */ }
};
template <>
struct EventTrait<StartEvent>
{
// same as above
static IDType eventID()
{ return "Start"; }
};
その後、あなたはすることができます
template <typename EventType>
EventRegistration set_event(function<void (shared_ptr<EventType>)> handler)
{
auto id(EventTrait<EventType>::eventID());
dispatchDictionary_.insert(make_pair(id,
make_shared<SpecificDispatcher<EventType>>(handler)));
return EventRegistration(bind(& Module::unset_event, this, id));
}
これは、ピースを組み合わせる方法と、いくつかのオプションをよりよく示しているはずです。これのいくつかは、各イベントで再コード化されるボイラープレート コードを示しています。これは、おそらく自動化したいと思うタイプのものです。このような生成をメタプログラミングするための高度なテクニックは、仕様を必要とする別のビルド ステップでの作業から、C++ メタプログラミング システム内での作業まで、たくさんあります。繰り返しますが、以前のポイントと同様に、自動化できるほど、バグが少なくなります。