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_となる関数オブジェクト