delegate1-boost::signalを読む
C++でC#っぽいdelegateを作ってみよう、というよくあるネタです。
というか、boost::signalですね。
ここBoost.Signals �`���[�g���A��を読むと非常に面白いです。
delegateを実装する際、戻り値の扱いをどうしようか迷っていたのですが、ここに解決策がありました。combiner(統合子)と呼ばれるものです。(ただし、ほとんどのユーザは読む必要がないと書いてある)
boost::signalもかなり規模がデカイので、どこまで実装するかわかりません。
signal/slotという概念もよく知りませんし。
目標はあくまでもdelegateです。
例によって、現状わからない部分は出来るだけカットしつつ、何となくそれっぽいものを作っていきましょう。
C#のdelegate
いろいろ端折ってありますが、基本的には以下の通りです。
namespace System { public delegate void EventHandler(object sender, EventArgs e); } public class Form { /*...*/ public event EventHandler Load; /*...*/ } public class Form1 : Form { private void InitializeComponent() { /*...*/ this.Load += new System.EventHandler(this.Form1_Load); } private void Form1_Load(object sender, EventArgs e) { //Form生成時 } }
+=を使ってEventHandlerに関数を登録して、イベント発生時にまとめて呼び出しているわけですね。
C++で実装
- functionをstd::listに登録。
- 戻り値はとりあえず最後に呼ばれたfunctionの戻り値を返す。
template< typename Signature > class delegate; //戻り値R、引数2個 template< typename R, typename A1, typename A2 > class delegate< R ( A1, A2 ) > { private: std::list< function< R ( A1, A2 ) > > func; public: template< typename F > void operator+=( F f ) { func.push_back( f ); } R operator()( A1 a1, A2 a2 ) { while( 最後の直前まで ) { ( *it )( a1, a2 ); ++ it; } return ( *it )( a1, a2 ); } };
なんかいろいろカッコ悪いので、boost::signalを見てみましょう。
ただし、boost::signalを使うためにはスタティックライブラリが必要であり、デフォルトでlibファイルがないので、自分でビルドしなければなりません。Let's Boostなどを参考にbjamというツールを使ってコマンドラインからビルドします。
※windows vistaの場合、管理者権限で実行しないとビルドが失敗します。私はこれでハマリました。。。
signalN
デフォルトでsignal0〜signal10まであり、signal
class signal_base_impl { /*...*/ public: // Slots mutable named_slot_map slots_; any combiner_; }; class signal_base : public noncopyable { /*...*/ public: shared_ptr<signal_base_impl> impl; }; // The actual signalN class template< typename R, typename A1, typename A2, typename Combiner = last_value<R>, typename Group = int, typename GroupCompare = std::less<Group>, typename SlotFunction = function2< R, A1, A2 > > class signal2 : public signal_base, // management of slot list public trackable // signals are trackable { public: R operator()( A1 a1, A2 a2 ) { /*...*/ // Let the combiner call the slots via a pair of input iterators return combiner()(slot_call_iterator(/*...*/), slot_call_iterator(/*...*/)); } };
last_value
boostでは統合子last_valueでslot_call_iterator::operator++()で登録された関数が呼ばれ、slot_call_iterator::operator*()で戻り値を取得しているようです。
template<typename T> struct last_value { typedef T result_type; template<typename InputIterator> T operator()(InputIterator first, InputIterator last) const { assert(first != last); T value = *first++; while (first != last) value = *first++; return value; } }; template<> struct last_value<void> { typedef void result_type; template<typename InputIterator> result_type operator()(InputIterator first, InputIterator last) const { while (first != last) *first++; return result_type(); } };
ふむふむ、なるほど。
次回はこれを元にdelegateを書き直しましょう。