ここでの初期データは、 前の記事と同じです。
非表示のテキスト
struct Base1 {}; struct Base2 {}; struct Base3 {}; struct Derived12: public Base1, public Base2 {}; struct Derived23: public Base2, public Base3 {};
実際には、それらに基づいて作成されたはるかに基本的な構造とメッセージがあります。 最も単純なケースでは、boost :: mpl :: for_eachは、型のリストをテンプレートパラメーターとして指定し、演算子()(T)メソッドを引数として持つクラスを指定する必要があります(Tはリストの型です)。 メソッドの定型文を作成できますが、これは私たちが必要とするものではありません。 したがって、すべての基本構造に対してoperator()をオーバーロードします。 これまでのところ、タイプのリストは各メッセージで手動で宣言されています。 最初の近似では、次のようになります。
struct Derived12: public Base1, public Base2 { boost::mpl::vector<Base1, Base2> types; }; struct Derived23: public Base2, public Base3 { boost::mpl::vector<Base2, Base3> types; }; class Describer { public: void operator()(Base1) { std::cout << " Base1\n"; } void operator()(Base2) { std::cout << " Base2\n"; } void operator()(Base3) { std::cout << " Base3\n"; } }; void main() { Derived12 d12; boost::for_each<Derived12::types>(Describer()); }
実行の結果として
繁殖
Base1からの情報の取得 Base2から情報を取得する
これには2つの問題があります。
- d12の値は決して使用されません。
- boost :: mpl :: for_each関数は、基本構造をインスタンス化します。
最初の問題は単純です。値をDescriberコンストラクターに渡して保存すると、クラス自体がテンプレートになります。 2番目の問題はより深刻です。オブジェクトを作成するコストに加えて、構造体に制限が追加で課せられるためです。構造体にはパラメーターのないコンストラクターが必要であり、抽象化できません。 演算子()をポインターでオーバーロードすることにしました。 この場合、型のリストには型へのポインターが含まれている必要があります。または、変換用のテンプレートで2番目のfor_eachオプションを使用できます。このオプションを使用します。 boost :: add_pointerを変換のテンプレートとして使用します。 すべての基本構造が処理されていることを確認するには、BOOST_STATIC_ASSERT(false)を含むテンプレート演算子()を追加します。 新しい基本構造が表示される場合、これによりコンパイルエラーが発生します。 その結果、以下が得られます。
template<typename T> class Describer { public: Describer(const T& v): v(v) {} void operator()(Base1*) { std::cout << " Base1\n"; } void operator()(Base2*) { std::cout << " Base2\n"; } void operator()(Base3*) { std::cout << " Base3\n"; } template<typename U> void operator()(U*) { BOOST_STATIC_ASSERT(false); } private: const T& v; }; void main() { Derived12 d12; boost::for_each< Derived12::types, boost::add_pointer<boost::mpl::_> > ( Describer<Derived12>(d12) ); }
次に、継承に関係する型のリストの確立を単純化してみましょう。 基本構造のタイプの完全なリストを宣言し、 boost :: mpl :: copy_ifアルゴリズムを使用します 。 これは、指定された条件を満たすすべての要素を新しいリストにコピーします。 条件として、継承チェックboost :: is_base_ofを使用します。
typedef boost::mpl::vector<Base1, Base2, Base3> FullTypesList; template<typename T, typename BaseList> struct MakeTypesList { typedef typename boost::mpl::copy_if< BaseList, boost::is_base_of< boost::mpl::_, T > >::type TypesList; };
便宜上、パラメーターなしで演算子()をDescriberに追加すると、for_eachが呼び出されます。
void Describer::operator()() { boost::mpl::for_each< typename MakeTypesList<T,FullTypesList>::TypesList, add_pointer<boost::mpl::_> >( boost::ref(*this) ); }
boost :: refラッパーは、Describerのコピーステートメントが呼び出されないようにするために必要です。
最終版
struct Base1 {}; struct Base2 {}; struct Base3 {}; typedef boost::mpl::vector<Base1, Base2, Base3> FullTypesList; template<typename T, typename BaseList> struct MakeTypesList { typedef typename boost::mpl::copy_if< BaseList, boost::is_base_of< boost::mpl::_, T > >::type TypesList; }; template<typename T> class Describer { public: Describer(const T& v): v(v) {} void operator()() { boost::mpl::for_each< typename MakeTypesList<T,FullTypesList>::TypesList, add_pointer<boost::mpl::_> >( boost::ref(*this) ); } void operator()(Base1*) { std::cout << " Base1\n"; } void operator()(Base2*) { std::cout << " Base2\n"; } void operator()(Base3*) { std::cout << " Base3\n"; } template<typename U> void operator()(U*) { BOOST_STATIC_ASSERT(false); } private: const T& v; }; // Derived12 Derived23 . struct Derived12: public Base1, public Base2 {}; struct Derived23: public Base2, public Base3 {}; void main() { Derived12 mes12; ( Describer<Derived12>(mes12) )(); }
このように処理構造のクラスが多数ある場合は、メッセージの基本クラスのリストを個別に宣言する方が合理的です。 メッセージ構造は単独では使用されないことがわかりました。これは、すべてのメッセージの共通インターフェースを実装するテンプレートクラスの基本クラスであり、基本クラスのリストを定義します。 このリストは、for_eachを呼び出すときにアクセスされます。 ラッパーテンプレートを作成して使用できます。 シンプルなオプション
ラッパーテンプレート
template<typename T> struct Wrapper: public T { typedef typename MakeTypesList<T, FullTypesList>::TypesList TypesList; } void Describer::operator() { boost::mpl::for_each< typename T::TypesList, add_pointer<boost::mpl::_> >( boost::ref(*this) ); }
更新:
テンプレート化された記述子のBOOST_STATIC_ASSERTに関する注意::演算子()
この例では、G ++はBOOST_STATIC_ASSERTでコンパイルエラーをスローします(false)。 G ++は、MS Visual C ++とは異なり、インスタンス化されていない場合でもテンプレートの本体をチェックします。 すべてのテンプレートに依存しない名前は、テンプレートの定義時に認識されている必要があります。 何らかの構築がコンパイルエラーを引き起こし、テンプレートパラメータに依存しない場合、コンパイルエラーが発生します。 次のことができます。
template <typename T> struct Describer { template<typename U> void operator() { BOOST_STATIC_ASSERT(sizeof(U) == 0); } }
Update2:基本クラスのリストを自動的に生成するオプションを備えた興味深いコメントをお寄せいただきありがとうございます。