関数にenum値を与えて呼び出す関数を動的に選択する手法が従来使われている.
この手法の場合,新しいenum値が必要となった場合,enumの定義および実装の他にswitch文の変更も必要とする.
一方、関数オブジェクトを使えば定義と実装のみでよい.
単純な関数オブジェクトを呼び出す場合,すなわちoperator()を呼び出す場合は何らかの形でコンセプトチェックを導入したほうがよい.
関数オブジェクトはoperator()をもっているため,想定外の関数オブジェクトが指定された場合どこでエラーが生じるかわからないからである.
上記のような単純なものでも,コンセプト違反があればコンパイル時に直ちにエラーとなる.
しかし,上記例では構造体Hoge, Piyo, Fugaの内部にtypdefする必要があった.これは,変更を許されない場合に問題となる.
そのような場合はtraits(アダプター)を用いる.
typedef enum { hoge, piyo, fuga, } MetaVariableType; void Hoge(); void Piyo(); void Fuga(); void Func( MetaVariableType _type ) { switch(_type) { case hoge: Hoge(); break; case piyo: Piyo(); break; case fuga: Fuga(); break; } }
この手法の場合,新しいenum値が必要となった場合,enumの定義および実装の他にswitch文の変更も必要とする.
一方、関数オブジェクトを使えば定義と実装のみでよい.
struct MetaVariableType{}; // for concept_check struct Hoge { typedef MetaVariableType is_metaval; void operator()(); // instead of function Hoge() }; struct Piyo { typedef MetaVariableType is_metaval; void operator()(); // instead of function Piyo() }; struct Fuga { typedef MetaVariableType is_metaval; void operator()(); // instead of function Fuga() }; template<typename MetaVariableType> void Func( MetaVariableType _type ) { typedef MetaVariableType::is_metaval concept_check; _type(); } //example Func( Hoge() ); Func( Piyo() ); Func( Fuga() );
単純な関数オブジェクトを呼び出す場合,すなわちoperator()を呼び出す場合は何らかの形でコンセプトチェックを導入したほうがよい.
関数オブジェクトはoperator()をもっているため,想定外の関数オブジェクトが指定された場合どこでエラーが生じるかわからないからである.
上記のような単純なものでも,コンセプト違反があればコンパイル時に直ちにエラーとなる.
しかし,上記例では構造体Hoge, Piyo, Fugaの内部にtypdefする必要があった.これは,変更を許されない場合に問題となる.
そのような場合はtraits(アダプター)を用いる.
template<class T> struct metaval_traits; template<> struct metaval_traits<Hoge> { static void execute(Hoge& h) { h(); } }; template<typename MetaVariableType> void Func( MetaVariableType _type ) { metaval_traits::execute(_type); }
STLのコンテナクラスは基本クラスとすることを想定していない(関数がvirtualではない).
そのためメンバー変数とするのが一般的であるが,要素へのアクセスを外部に公開する場合,多くの決まりきったインターフェイスを記述する必要がある.
もしくはコンテナ自体を公開してしまう方法があるが,データ保持の観点から望ましくはないだろう.
そこでCRTPによるアダプターを作成してみた.ここではその一部を紹介する.
container_adapterから派生クラスへのアクセスはstatic_cast<Derived*>(this)で可能である.この変換はコンパイル時に行われる.
条件として,container_adapterから派生クラスのコンテナにアクセスできなければならない.
これにはコンテナの名前がcであるならば下記例のように決め打ちしてしまっても構わない.(なおcはSTLでコンテナのインスタンスに使われる標準的な名前である)
そうでない場合は,protected GetContainer()関数でも用意するのがいいだろう.
container_adapterから派生クラスのprotected メンバやメンバ関数にアクセスすることはできないため,friendとしておく必要がある.
そのためメンバー変数とするのが一般的であるが,要素へのアクセスを外部に公開する場合,多くの決まりきったインターフェイスを記述する必要がある.
もしくはコンテナ自体を公開してしまう方法があるが,データ保持の観点から望ましくはないだろう.
そこでCRTPによるアダプターを作成してみた.ここではその一部を紹介する.
container_adapterから派生クラスへのアクセスはstatic_cast<Derived*>(this)で可能である.この変換はコンパイル時に行われる.
条件として,container_adapterから派生クラスのコンテナにアクセスできなければならない.
これにはコンテナの名前がcであるならば下記例のように決め打ちしてしまっても構わない.(なおcはSTLでコンテナのインスタンスに使われる標準的な名前である)
そうでない場合は,protected GetContainer()関数でも用意するのがいいだろう.
container_adapterから派生クラスのprotected メンバやメンバ関数にアクセスすることはできないため,friendとしておく必要がある.
template<class Derived, class Value, class Container = std::vector<Value> > class container_adapter { typedef Container container_type; typedef container_type::reference reference; typedef container_type::const_reference const_reference; typedef container_type::iterator iterator; typedef container_type::const_iterator const_iterator; public: iterator begin() { return static_cast<Derived*>(this)->c.begin(); } const_iterator begin() const { return static_cast<const Derived*>(this)->c.begin(); } reference at(size_t _Pos) { return static_cast<Derived*>(this)->c.at(_Pos); } //以下,略. }; //Smaple class Points3D : public container_adapter<Points3D, std::array<double, 3> > { friend class container_adapter<Points3D, std::array<double, 3> >; public: protected: std::vector< std::array<double, 3> > c; }
コメントをかく