重要な問題は、ペイロードがどのように異なり、どのように同じであるかです。ペイロードによって決定されるタイプのオブジェクトを生成し、すべてのタイプのペイロードに共通の仮想インターフェースを介してそれらと対話するシステムは、場合によっては合理的です。
ペイロードのタイプの有限で固定されたリストがあると仮定した場合、aを返すのboost::variant
は比較的簡単です。apply_visitor
次に、それを処理するために、バリアント内のすべてのタイプを受け入れるファンクターで呼び出します。
1つのタイプのペイロードのみを異なる方法で処理する場合は、「タイプがTに一致する場合にのみラムダを呼び出して実行する」関数をこのように記述するのはそれほど難しくありません。
したがって、次のような構文を取得できます。
struct State;
struct HandlePayload
{
typedef void return_type;
State* s;
HandlePayload(State* s_):s(s_) {}
void operator()( int const& payload ) const {
// handle int here
}
void operator()( std::shared_ptr<bob> const& payload ) const {
// handle bob ptrs here
}
template<typename T>
void operator()( T const& payload ) const {
// other types, maybe ignore them
}
}
これはかわいいですが、かなり間接的であることに気付くでしょう。ただし、ペイロードを処理するために上記のジェネリック型Tを使用してテンプレートコードを記述し、状況によってはトレイトクラスなどを使用したり、他の状況では明示的な特殊化を使用したりできることにも注意してください。
ペイロードが1つの特定の種類であると予想し、その場合に特別な作業のみを実行したい場合は、に単一タイプのハンドラーを作成するのboost::variant
は簡単です。
template<typename T, typename Func>
struct Helper {
typedef bool return_type;
Func f;
Helper(Func f_):f(f_) {}
bool operator()(T const& t) {f(t); return true; }
template<typename U>
bool operator()(U const& u) { return false; }
};
template<typename T, typename Variant, typename Func>
bool ApplyFunc( Variant const& v, Func f )
{
return boost::apply_visitor( Helper<T, Func>(f), v );
}
これは、バリアントvでfを呼び出しますが、バリアントのタイプTでのみ呼び出し、タイプが一致する場合はtrueを返します。
これを使用すると、次のようなことができます。
boost::variant<int, double> v = 1.0;
boost::variant<int, double> v2 = int(1);
ApplyFunc<double>( v, [&](double d) { std::cout << "Double is " << d << "\n"; } );
ApplyFunc<double>( v2, [&](double d) { std::cout << "code is not run\n"; } );
ApplyFunc<int>( v2, [&](int i) { std::cout << "code is run\n"; } );
またはそのような変種。