boost.variantの補助ツール
boost::variant に以下の動作をさせたい
variant<int,int> => int
variant<int,double> => double
variant<int , boost::rational<int> > => boost::rational<int>
variant<int , double , std::string> => variant<double,std::string>
しかしこうなってくれないので自分で拡張ツールを作った。
https://github.com/niitsuma/variant_shrink
(要するにdecltype(int+double)=>double みたいにまとめられる場合はまとめるけど、まとめられない場合はvariant
使用例
C++11が使えない環境でdecltypeの代用品として使う
template<typename X,typename Y> typename make_variant_shrink_over<boost::mpl::vector<X,Y> >::type //C++11ならdecltype(x+y)にできる adder(X x, Y y) {return x + y;} double r1= adder(1,1.1); boost::rational<int> x(2,3); boost::rational<int> r2 = adder(1,x);
使い方
boost::variant::make_variant_over<boost::mpl::vector<....> >::type
と同じ様に使う.
ファイル"variant_shrink.hpp"をインクルードするだけで使える
#include "variant_shrink.hpp" make_variant_shrink_over<boost::mpl::vector<int,int> >::type => int make_variant_shrink_over<boost::mpl::vector<int,boost::rational<int> >::type => boost::rational <int> make_variant_shrink_over<boost::mpl::vector<int,double > >::type => double make_variant_shrink_over<boost::mpl::vector<double,std::string> >::type => boost::variant<double,std::string> make_variant_shrink_over<boost::mpl::vector<double,int,std::string> >::type => boost::variant<double,std::string>
任意の縮小方法
デフォルトは
typedef boost::mpl::vector< int,boost::rational<int>,float,double,std::complex<double> >::type default_type_order;
となっていて
int < boost::rational<int> < float < double < std::complex<double>
の順番で型の一般化(縮約?)がされていく。この順番を変更したい場合は、一般化の順番を決める関数オブジェクトを2番めのテンプレート引数として入力する。例えば
bool < int < long < boost::rational<int> < boost::rational<long>
の順番で一般化したい場合は
typedef make_variant_shrink_over< boost::mpl::vector<float,bool,boost::rational<long> > , //2番めのテンプレート引数に順番情報を入力する generate_mpl_lambda_is_generalizable_to_from_type_order_sequence< boost::mpl::vector< bool , int , long , boost::rational<int> , boost::rational<long> //ここに自分の好きな順番を入れる > >::type >::type r8type; BOOST_MPL_ASSERT((boost::mpl::equal<r8type ,boost::variant<boost::rational<long> ,float> > ));
この2番めのテンプレート引数に入力する一般化の順番を決める関数オブジェクト
is_generalizable_to<TypeFrom,TypeTo>
は次のような意味を持つものを作ればよい
is_generalizable_to<float,double> => boost::mpl::true_ // float は double に一般化できる
is_generalizable_to<double,float> => boost::mpl::false_ //double は float に一般化できない
is_generalizable_to<std::string,float> => boost::mpl::false_ //std::string は floatと比較できない、またはfloatへの一般化はできない。
generate_mpl_lambda_is_generalizable_to_from_type_order_sequenceは一般化の順番ベクトル
generate_mpl_lambda_is_generalizable_to_from_type_order_sequence< boost::mpl::vector< bool , int , long , boost::rational<int> , boost::rational<long> > //一般化の順番ベクトル >
を入力すると、上記の反応をする関数オブジェクトを生成する。
もっと複雑な縮小順番も入力できる。
浮動小数型:
float < double < long double
整数型:
bool < char < short < int < long
の順番で縮約されて、なおかつ浮動小数と整数のunionはboost::variantにしたい場合はこうする。
template<typename T,typename TBase> struct is_generalizable_to_custom : public boost::mpl::if_< boost::is_floating_point<T> , typename boost::mpl::if_< boost::is_floating_point<TBase> ,is_less_in_orderd_mpl_sequence< T,TBase,boost::mpl::vector<float,double,long double > > //浮動小数型の順番 ,boost::mpl::false_ //浮動小数型と整数型のUnionはそのままvarinatにする >::type ,typename boost::mpl::if_< boost::is_integral<T> ,typename boost::mpl::if_< boost::is_integral<TBase> ,is_less_in_orderd_mpl_sequence< T,TBase,boost::mpl::vector<bool,char,short,int,long> > //整数型の順番 ,boost::mpl::false_ >::type ,boost::mpl::false_ //他の型(std::stringとか)はそのままにする >::type >::type {}; typedef boost::mpl::lambda<is_generalizable_to_custom< boost::mpl::_1 , boost::mpl::_2 > >::type is_generalizable_to_custom_mpl_lambda; //boost::mpl::lambdaで包まないと関数オブジェクトとして引数に渡せない typedef make_variant_shrink_over< boost::mpl::vector<double,float,bool,char> ,is_generalizable_to_custom_mpl_lambda //2つ目のtemplate引数として上記で生成した関数オブジェクトを入力 >::type r10type; BOOST_MPL_ASSERT((boost::mpl::equal<r10type ,boost::variant<double,char> >));
ここで使った補助関数
is_less_in_orderd_mpl_sequence<T1, T2,OrderVector>
は
is_less_in_orderd_mpl_sequence<int, double ,mpl::vector<bool,int,double,long double> > => mpl::true_ is_less_in_orderd_mpl_sequence<double, int ,mpl::vector<bool,int,double,long double> > => mpl::false_
のようなT1がT2よりも先に順番ベクトルに出現する時にtrue_となる関数オブジェクト
関連
- http://cpplover.blogspot.jp/2013/03/commontypedecay.html
- http://en.cppreference.com/w/cpp/types/common_type
- Boost.Fusion から Boost.Variant を定義 http://d.hatena.ne.jp/osyo-manga/20111129/1322578311
- FusionシーケンスをMPLシーケンスとして扱う http://d.hatena.ne.jp/faith_and_brave/20110113/1294894695
- http://www.kmonos.net/alang/boost/classes/variant.html
- http://stackoverflow.com/questions/6773997/using-mplvector-to-define-boostvariant-types
- http://stackoverflow.com/questions/13750838/how-to-implement-a-boostvariant-derived-class
- http://boost.2283326.n4.nabble.com/boost-users-Variant-Sequence-instantiation-td2595983.html