Boost.AnyがBoost.MPLと友達だった方法

最近、boost :: any typeでパラメーター化された標準のSTLコンテナーを使用して、任意の型のパラメーターを渡すタスクを解決するコードを使用する必要がありました。

例:

void foo (std::vector<boost::any>& args) { // do smth. }
      
      





前の開発者はそれほど正確ではなく、関数内で、boost :: anyの内容を操作するのは元のデータ型の仮定に基づいていました。つまり、boost :: any_castの変換がパスしなかった場合、パラメーターはスキップされました。 場合によっては、この処理方法が受け入れられ、この作業技術の例はこちらにあります

ただし、データ型に関する初期の仮定を一般化したいと思いました。



std :: vectorに次のタイプのデータが含まれている状況を想像してください。

short、int、float、double、および関数があります

  double sum (std::vector<boost::any>& args) { }
      
      





コンテナーの各要素に対してboost :: any_cast <double>を呼び出して、渡されたパラメーターの値の合計を計算します。

boost :: any_castが変換されるとどうなるか見てみましょう

  template<typename ValueType> ValueType * any_cast(any * operand) { // // some code skipped // operand->type() == typeid(ValueType) ? &static_cast<any::holder<ValueType> *>(operand->content)->held : 0; }
      
      





元の型と変換が実行される型のtype_info値が一致しない場合、0を返します。しかし、通常の状況では、sum関数に戻ると、自分で書くことができます。

  double value=1+2L+3.0F+4.0;
      
      





したがって、int、long、float、doubleを合計します。 sum関数から同じ振る舞いを実現したいのです。



つまり、boost :: anyの値を処理するとき、「ちょっと、関数 'Smth cast(any)'、できればanyをSmthタイプに変換します」と言いたいです。 また、キャスト関数がどのタイプをSmthに導くかを知るために、タイプのリストが必要です。 実際、必要な型に簡単に変換できる型のリストを設定したいのです。

変換可能な型のリストを説明するために、車輪を再発明するのではなく、boost :: mplライブラリを使用して、そこで必要な型シーケンス概念を見つけます。



ここで、boost :: any_castの代わりとしての型コンバータークラスと、型コンバータークラスが必要です。

  1. ToTypeを変換したい型を受け入れます
  2. 必要なToType型への変換が可能なCompatibleTypes型のリストを受け入れます
  3. boost ::任意のオブジェクトに基づいて、適切な型コンバーターが返されます。


コンバータークラスは、必要な型に変換する関数を提供し、変換を実行できる型のtype_infoオブジェクトへのポインターを格納する必要があります。

  template<class ToType> class Converter { public: virtual ~Converter {} virtual ToType cast(const boost::any& arg) = 0; explicit Converter(const type_info* info) : m_info(info) { } const type_info& type() { return *m_info; } private: const type_info* m_info; };
      
      





キャスト関数が純粋に仮想であると宣言されている理由は、もう少し明らかになります。 次に、クラスディスパッチャについて説明します。

  template<class CompatibleTypes, class ToType> class TypesDispatcher { std::vector<Converter<ToType>* > converters; struct wrapper { //code skipped }; public: TypesDispatcher(); Converter<ToType>* getConverter(const boost::any& arg); };
      
      





ディスパッチャークラスは、一連の有効なCompatibleTypes型とToType変換が実行される型によってパラメーター化されます。 コンテナーは、Converterへのポインターのコンテナーを保持します。 CompatibleTypesにリストされているタイプの1つに各コンバーターをバインドする必要があります。 これを行うには、Converterを継承し、キャスト関数を実装します。

  template <class FromType, class ToType> class TypeConverter : public Converter<ToType> { public: TypeConverter() : Converter(&typeid(FromType)) { } virtual ToType cast(const boost::any& arg) { return static_cast<ToType>(*boost::any_cast<FromType>(&arg)); } };
      
      





これで、CompatibleTypesのすべての型のコンバーターオブジェクトを作成するだけでなく、getConverter関数を実装できます。

  //      struct wrapper { explicit wrapper(std::vector<Converter<ToType>* >& converters) : m_converters(converters) { } template<class FromType> void operator()(FromType) { m_converters.push_back(new TypeConverter<FromType, ToType>()); } std::vector<Converter<ToType>* >& m_converters; }; //    TypesDispatcher() { boost::mpl::for_each<CompatibleTypes>(wrapper(converters)); }
      
      





CompatibleTypesのタイプのリストを調べるために、boost :: mpl :: for_eachを使用しました。これは、実行時にリスト内の各タイプに適用される関数をオブジェクトに取ります。これのために、各タイプのオブジェクトをインスタンス化します。

getConverter関数の実装は簡単で明白です。

  Converter<ToType>* getConverter(const boost::any& arg) { std::vector<Converter<ToType>* >::const_iterator it = converters.begin(); while(it != converters.end() ) { Converter<ToType>* converter = *it; if(converter->type() == arg.type()) return converter; ++it; } return 0; }
      
      





以上です。 次のように書くことができます

  template<class CompatibleTypes, class ReturnType> ReturnType sum(std::vector<boost::any>& args) { TypesDispatcher<CompatibleTypes, ReturnType> dispatcher; std::vector<boost::any>::size_type i; ReturnType result = ReturnType(); for(i=0; i < args.size(); i++) { Converter<ReturnType>* converter = dispatcher.getConverter(args[i]); if(converter) result += converter->cast(args[i]); } return result; } int main(int argc, char* argv[]) { typedef boost::mpl::vector<int,long, float, double> types; std::vector<boost::any> seq; seq.push_back(boost::any( 1 )); seq.push_back(boost::any( 2L )); seq.push_back(boost::any( 3.0F )); seq.push_back(boost::any( 4.0 )); double result = sum<types, double>(seq); }
      
      







完全なコード例はこちらにあります



その結果、任意の型の引数を操作する機会が得られ、変換のルールを比較的柔軟に設定できました。



All Articles