Boost.Variant が特定の型の値を保持していた時だけ処理を実行する
boost::variant
が保持している値の型が特定の型の場合だけ○○するという処理が多いの,でラムダ式で処理を書けるようにした.
apply<{期待する variant が保持する値の型}>({variant な変数}, {適用したい関数})
という感じに使う.
#include <boost/variant/variant.hpp> #include <iostream> int main() { boost::variant<int, double> v = 42; std::cout << *apply<int>(v, [](auto i){ return i * 3; }) << std::endl; // 126 std::cout << *apply<double>(v, [](auto i){ return i * 3; }) << std::endl; // do nothing apply<int>(v, [](auto i){ std::cout << i << std::endl; }); // 42 apply<double>(v, [](auto i){ std::cout << i << std::endl; }); // do nothing return 0; }
戻り値は boost::optional
で,boost::variant
が保持する値の型が apply()
で指定した通りなら指定した関数を適用した戻り値を返し,そうでなければ boost::none
を返す.
Boost.Variant に型指定でアクセスしたい の get()
関数テンプレートを使って次のように書ける.
#include <boost/optional.hpp> template<class T, class Func, class RetType = decltype(std::declval<Func>()(std::declval<T>()))> struct apply_impl{ template<class Variant> static boost::optional<RetType> call(Variant const& v, Func const& f) { if(auto opt = get<T>(v)) { return f(*opt); } else { return boost::none; } } }; template<class T, class Func> struct apply_impl<T, Func, void>{ template<class Variant> static bool call(Variant const& v, Func const& f) { if(auto opt = get<T>(v)) { f(*opt); return true; } else { return false; } } }; template<class T, class Func, class Variant> inline auto apply(Variant const& v, Func const& f) -> decltype(apply_impl<T, Func>::call(v, f)) { return apply_impl<T, Func>::call(v, f); }
戻り値が void
かどうかで分岐したかったので,実装にクラステンプレートを使っている.void
の参照型は作れないので,boost::optional<void>
が定義できないのが原因で,Boost.MPL の eval_if
メタ関数で条件分岐しても良かったけれど,面倒そうなのでやめた.
指定した関数の戻り値型が void
だったときの apply()
の戻り値型は boost::optional<boost::none_t>
が良かったけれど,定義できないので bool
で妥協した.(たいていは auto
で受けるので問題ないと思う)