今回の記事は以前の記事で一瞬触れた、テンプレートクラスでコールバックを引数付きで保持する例です。
件の記事のスレッド処理のクラスと組み合わせれば、スレッドに引数を与えて呼び出すような運用も可能です。
それだけでなく、C++で設計開発を行っているとコールバックは死ぬほど多用します。
基本的にちょっとしたことであればstd::functionクラス等を使ってさくっと組んでしまえるのですが、以前のスレッド処理や各種デザインパターンに合わせてやっていこうと考えた場合、パターンによってはジェネリックでカスタマイズ可能なコールバックの呼び出し方法を基底クラスとして持っていると何かと便利な場合が出てきます。
今日はそんな基底クラスとして持っているやつの一部技術を抜粋です。
本日のソースコード
テンプレート引数リスト
template < class T_Function, class T_return, class... T_Arguments > class FunctionHolder前回のCallableなオブジェクトを受け取らせるテンプレート型引数に加えて、
戻り値型と引数型を追加で定義しています。
実際の呼び出しでは長いので適宜部分特殊化して使っていますが、今回の例では直接使います。
引数の保持
FunctionHolder(T_Function&& managed_function, T_Arguments... arguments): managed_function_(managed_function) { arguments_holder = std::make_tuple(arguments...); }オブジェクトとして定義したときに引数を保持して使い回したい場合があるので、引数リストはタプルを利用して保持します。
呼び出し方
T_return operator()( T_Arguments... arguments ) { return managed_function_( arguments... ); } //------------------------------------------------------- //オブジェクト自体を関数のように使う lambda_driver(200,300);
T_return call_function() { return std::apply(managed_function_, arguments_holder); } //------------------------------------------------------- //オブジェクトが保持した値を使う lambda_driver.call_function();
保持した引数を使う場合と、通常の関数のように引数を与えて呼び出す場合の呼び口を用意しています。
と言ってもこれは例で、実際にはoperator()などは virtual にして継承先でオーバーライドする場合が多いです。
パラメータパックを保持するためにtupleを使用しているので、展開して呼び出すためにはapplyを使う必要があります。
実際の呼び出しと実行結果
print_and_sum obj; FunctionHolder<print_and_sum, uint32_t, uint32_t>print_and_sum_caller2(std::move(obj), 200); std::cout << print_and_sum_caller2.call_function() << std::endl; print_and_sum_caller2(100);あとはただ定義して呼び出すだけです。
Sum from 0 to 200 = 20100 ←実行された関数内での表示 20100 ←coutによる戻り値の表示 Sum from 0 to 100 = 5050 ←引数を新たに与えて実行された関数内での表示戻り値も受け取れるようにしているので、柔軟に使用することが出来ます。
さいごに
このクラス自体は単体では使い勝手が悪いです。ただ最初にも述べたとおり派生や特殊化を行って使うと、途端に便利な場面が出てきます。
例えば設定値ごと処理を保持した孫クラスを、今回紹介したクラスを親に持つ子クラスに受け取らせて処理を分岐させることも出来ます。
実際にこの手のクラスは多用するクラスの一つかなぁと思いますので今更という感じの記事ですが、開発経験が浅いメンバーへ教育する際には細かく要素技術や動作を説明しないといけません。
同時にそうした草の根から教えていく行為が自社の技術の発展にはつながるのだろうなと思い社内にもノウハウを残している毎日です。
0 件のコメント:
コメントを投稿