プログラミング楽しい
おひさしぶりです。
今回はstd::bindを使ってネストしたクラスのメンバ変数を束縛してみます。
以下サンプル
#include <iostream> #include <vector> #include <functional> // std::bind std::placeholders::_1 #include <algorithm> // std::for_each struct Hoge { Hoge() : i(1) {} int i; }; struct Hogehoge { Hogehoge() : hoge(), i(2) {} Hoge hoge; int i; }; struct Hogehogehoge { Hogehogehoge() : hogehoge(), i(3) {} Hogehoge hogehoge; int i; }; int main() { using std::bind; using std::placeholders::_1; std::vector<std::function<int(const Hogehogehoge&)>> get_all_member; get_all_member.push_back(bind(&Hogehogehoge::i, _1)); get_all_member.push_back(bind(&Hogehoge::i, bind(&Hogehogehoge::hogehoge, _1))); get_all_member.push_back(bind(&Hoge::i, bind(&Hogehoge::hoge, bind(&Hogehogehoge::hogehoge, _1)))); Hogehogehoge h; std::for_each(get_all_member.begin(), get_all_member.end(), [&h](const std::function<int(const Hogehogehoge&)>& get_member) { std::cout << get_member(h) << std::endl; }); }
get_memberは一回目のループで、h.iを返し
get_memberは二回目のループで、h.hogehoge.iを返し
get_memberは三回目のループで、h.hogehoge.hoge.iを返します。
bindがネストしてると理解し辛くなりが、このくらいなら読みやすいと思えるようになってきました。
Boost.勉強会感想とか
遅ればせながらBoost.勉強会の発表資料をアップしました。
http://cid-31a4fb569979bef3.skydrive.live.com/self.aspx/.Public/BoostAsio.ppt
※以下TLにあった質問ぽいものに対して解答
Q:AsioってCOMポート扱えるの?
A:はい、発表資料に書いたとおり扱えちゃいます。
Q:Asioって聞くとDTMのあれ?
A:おしいけどちがうw
Q:もっとコードにはコメントが欲しかったです
A:ごめんなさい。コメント入れることでコードのフォントサイズが小さくなって見えなくなるのが怖かったので削ってしまいました><
Q:v4を「ぶいよん」って読むの?
A:「ぶいよん」って言うのは癖です。「ぶいふぉー」が一般的なのかな?
Q:deadline_timerの開始はposix_time::seconds のコンストラクタが評価されてからじゃないの?
A:えっと、調べてないのでわかりません。
でも良いたかったのはdeadline_timerのコンストラクタにposix_timeを渡す場合は
そこから既にタイマーが始まってるよってことでした。
ちなみにタイマーをかける時間をコンストラクタで取らない場合は
deadline_timerのメンバ関数expires_from_nowを使えばできますね。
こんな感じ
deadline_timer timer(io_service); timer.expires_from_now(posix_time::seconds(5)); // ここから5秒後にタイマが満期を迎える。 timer.wait();
Q:ちょっと疲れた・・・
A:はい、疲れちゃったんです。
Q:asioのtcp socketってnon-blockingなんかな?
A:メンバ関数で同期、非同期どちらも扱えます。
非同期メンバ関数
async_connect
async_read_some
async_receive
async_send
async_write_some
同期メンバ関数
connect
read_some
receive
send
write_some
※ここは調べてないのでちょっと不正確なので参考程度に
ただし、tcp::iostreamは単体では同期しか扱えなかったはず。
Q:asioでいろんなプロトコルのライブラリを作ろうってサブプロジェクトがあったような気がする・・・
A:その話はしらなかったですが、プロトコルの拡張はProtocol requirementsを満たせば可能だとドキュメントにはあります。
参考:http://www.boost.org/doc/libs/1_40_0/doc/html/boost_asio/reference/Protocol.html
Q:けっこうよさそうだなー #boostjp http://www.boost.org/doc/libs/1_37_0/doc/html/boost_asio/tutorial/tuttimer5.html
A:これ!!ほんとはすっごく紹介したかったコードです!!ただ、一部理解不足なところがあるので理解できたらどこかで話したいですね。
Q:あのyieldマクロは怖かった
A:僕も最初見たときはこんなことできるのかって思いました。
Q:overwrapped I/O にはイヤな思い出が
A:Asioがラップしてくれてるので怖くないですよー。
Q:SSLのとこ微妙に見逃した。。。中身はopen SSL?
A:はい、スライドにも書きましたが、Open SSLを使ってます。
Q&Aじゃないのもいっぱい混ざったような気がしますが、気にしない。
■ここから感想
ことの発端は僕がTL上でBoost勉強会とかあったら楽しそうだなってつぶやいたところからで、
id:fath_and_braveさんが主催、段取りをしてくださいました。
本当に感謝です、ありがとうございました。
さすが、発表者のみなさんすごい方ばかりでセッションはどれも面白かったです。
特にmoriyoshiさんのタイピング速度が神でしたw
補間使ってるのかと思いきや使ってなかったそうです。
指が勝手に補間するんですって仰ってました。すごすぎるw
■反省
自分は今回がスピーカ担当は初で、gdgdになってしまって申し訳なかったです。
Asioのお話を楽しみに来てくださった方には申し訳なかったです><。
正直今回は失敗したなーと言う感想。
もっとタイマーのお話とかできればよかったですが、色々飛んでしまいました。orz
次は関西ってうわさが流れてましたがまたぜひ参加したいですね!
と言うわけで皆様お疲れ様でした。ホントに楽しかったー。
C++でPropertyつづき
前のエントリーに上書きしちゃったみたいです。汗
Boost.Propertyの実装案みたいなもの->http://codepad.org/A4vaAMPZ
これみて簡単にならないかやってみました。
#include <boost/typeof/typeof.hpp> #include <iostream> template<typename T> struct property_get_type; template<typename ReturnType,typename Class> struct property_get_type<ReturnType (Class::*)() const> { typedef ReturnType type; }; template<typename T> struct property_set_type; template<typename ReturnType,typename Class,typename Argument> struct property_set_type<ReturnType (Class::*)(Argument)> { typedef Argument type; }; template <class Signeture> class signeture { typedef BOOST_TYPEOF_TPL(&Signeture::get) get_signeture_type; typedef BOOST_TYPEOF_TPL(&Signeture::set) set_signeture_type; public: typedef typename property_get_type<get_signeture_type>::type get_return_type; typedef typename property_set_type<set_signeture_type>::type set_argument_type; }; #define MY_PROPERTY(Property, Class) \ struct Property## _ ##Class : public signeture<Property>, public Property \ { \ typedef Property base_type; \ get_return_type get() const { return static_cast<const base_type*>(this)->get(); } \ void set(set_argument_type v) { static_cast<base_type*>(this)->set(v); } \ Property## _ ##Class & operator=(set_argument_type v) { set(v); return *this; } \ operator get_return_type () { return get(); } \ } \ class my_class { struct my_property { double get() const { return arg_; } void set(double v) { arg_ = v; } private: double arg_; }; public: MY_PROPERTY(my_property, my_class) value; }; int main() { my_class m; m.value = 5; double xx = m.value; return 0; }
self()で自分のポインタ取り出せなくなりました。orz
boost恐るべし・・・。
C++でPropertyつづき1
self()呼び出しもできた。http://codepad.org/eKAdqttP
以下に同じコードを
#include <boost/typeof/typeof.hpp> #include <iostream> template<typename T> struct property_get_type; template<typename ReturnType,typename Class> struct property_get_type<ReturnType (Class::*)() const> { typedef ReturnType type; }; template<typename T> struct property_set_type; template<typename ReturnType,typename Class,typename Argument> struct property_set_type<ReturnType (Class::*)(Argument)> { typedef Argument type; }; template <class Signature> class signature { typedef BOOST_TYPEOF_TPL(&Signature::get) get_signature_type; typedef BOOST_TYPEOF_TPL(&Signature::set) set_signature_type; public: typedef typename property_get_type<get_signature_type>::type get_return_type; typedef typename property_set_type<set_signature_type>::type set_argument_type; }; template <class Class> struct get_derived { virtual const Class* self() const { return 0; } }; #define MY_PROPERTY(Property, Class, Name) \ struct Property##_##Class : public signature<Property<get_derived<Class> > >, public Property<get_derived<Class> >\ {\ typedef Property<get_derived<Class> > base_type;\ get_return_type get() const { return static_cast<const base_type*>(this)->get(); }\ void set(set_argument_type v) { static_cast<base_type*>(this)->set(v); }\ const Class* self() const { return reinterpret_cast<Class*>(size_t(this)-offset()); }\ size_t offset() const {\ union {\ Class* class_pointer;\ size_t rep;\ } c;\ c.rep=0;\ return size_t(&c.class_pointer->Name);\ }\ Property##_##Class & operator=(set_argument_type v) { set(v); return *this; }\ operator get_return_type () const { return get(); }\ } Name class my_class { template <class Base> struct my_property : Base { double get() const { return arg_; } void set(double v) { arg_ = v; this->self()->setter_info(); } private: double arg_; }; void setter_info() const { std::cout << "call setter" << value << std::endl; } public: MY_PROPERTY(my_property, my_class, value); }; int main() { my_class m; m.value = 5; double xx = m.value; return 0; }
以下の関数内でvalueを出力するような処理を入れるとgetが永遠呼ばれてタイムアウトします・・・。
double get() const { return arg_; }
追記:
id:eldesh さんからtypoの指摘受けたので修正。
Boost.Propertyのほうはgetのみとかsetのみのにも対応するためにあんなに複雑になってるんだと分かりました。納得。
もしもBoost.Threadにinterruptが無かったら。
VC限定なコードですけが、こんな感じで追加できるんじゃないかと。
元ネタはtwitterでid:melponさんがstd::threadにinterruptが無いので欲しいってとこからです。
id:melponさんがつぶやいてたコードの大部分借りました。
#include "stdafx.h" #include <iostream> #pragma warning(push) #pragma warning(disable: 4819) #include <boost/thread.hpp> #include <boost/bind.hpp> #include <boost/scoped_ptr.hpp> #pragma warning(pop) namespace thread { namespace cancellation { class interrupted_exception {}; class interruptible_thread; template <class F> void thread_proxy(interruptible_thread* p, F f); class interruptible_thread { __declspec(thread) struct tls { interruptible_thread* this_thread; }; public: // publicなのは手抜きです。すみません。 static __declspec(thread) tls thread_info; private: boost::scoped_ptr<boost::thread> th; mutable boost::mutex mutex; bool interrupted_; public: template <class F> explicit interruptible_thread(F f) : th() , mutex() , interrupted_(false) { // threadの開始thread化したい関数fは // detail::thread_proxy<F>でcallする th.reset( new boost::thread( boost::bind(&detail::thread_proxy<F>, this, f))); } void join() { th->join(); } // boost::threadへの転送関数を定義 void interrupt() { boost::unique_lock<boost::mutex> lock(mutex); interrupted_ = true; } bool interrupted() const { boost::unique_lock<boost::mutex> lock(mutex); return interrupted_; } }; interruptible_thread::tls interruptible_thread::thread_info; namespace detail { template <class F> void thread_proxy(interruptible_thread* p, F f) { interruptible_thread::thread_info.this_thread = p; // thread-local try { f(); } catch (interrupted_exception&) {} } } // namecpace detail } // namecpace cancellation } // namecpace thread // ここからテスト用のコード void worker_thread() { namespace tc = thread::cancellation; while(1) { if (tc::interruptible_thread::thread_info.this_thread->interrupted()) { throw tc::interrupted_exception(); } } } int _tmain(int argc, _TCHAR* argv[]) { namespace tc = thread::cancellation; tc::interruptible_thread t0(&worker_thread); tc::interruptible_thread t1(&worker_thread); t0.interrupt(); t0.join(); t1.interrupt(); t1.join(); std::cout << "cancel" << std::endl; return 0; }
C++0xではthread_localキーワードが追加されるので
TlsAlloc(Windows) pthread_key_create(POSIX)とか環境依存なコードは書かなくても済みそうです
でも作るのは面倒なので最初から付けてください・・・。
ちなみにg++では
__declspec(thread) struct tls { interruptible_thread* this_thread; }; public: // publicなのは手抜きです。すみません。 static __declspec(thread) tls thread_info;
な部分はどう書けば良いんですかね。
struct tls { interruptible_thread* this_thread; }; public: // publicなのは手抜きです。すみません。 static __thread tls thread_info;
もはやBoost.ScopeExit・・・
#include <iostream> #include <boost/typeof/typeof.hpp> #include <boost/preprocessor/seq/cat.hpp> #include <boost/preprocessor/seq/for_each_i.hpp> // Steven Watanabe's trick namespace closure { namespace args { template<int> struct declare; typedef void* declared; struct undeclared { declared dummy[2]; }; template<> struct declare<sizeof(undeclared)> { template<int> struct apply { declared value; friend void operator>(bool, const apply&) {} }; }; template<> struct declare<sizeof(declared)> { static const int apply = 0; }; } } extern closure::args::undeclared closure_args; // undefined #define PP_SEQ_TYPE(r, id, i, var)\ BOOST_PP_SEQ_CAT( (closure_args_t_)(i)(_)(id) ) & var; #define PP_SEQ_CLOSURE_TAG_DECL(r, id, i, var) \ typedef BOOST_TYPEOF(var) BOOST_PP_SEQ_CAT( (closure_args_t_)(i)(_)(id) ) ; #define PP_SEQ_CAPUTURE(r, idty, i, var)\ BOOST_PP_COMMA_IF(i) BOOST_PP_TUPLE_ELEM(2,1,idty) var #define PP_SEQ_CAPUTURE_DECL_T(r, id, i, var)\ BOOST_PP_COMMA_IF(i) BOOST_PP_TUPLE_ELEM(2,1,(id,BOOST_PP_EMPTY()))\ BOOST_PP_SEQ_CAT( (closure_args_t_)(i)(_)(id) ) & BOOST_PP_CAT(var,i) #define PP_SEQ_CAPUTURE_DECL(r, idty, i, var)\ BOOST_PP_COMMA_IF(i) BOOST_PP_TUPLE_ELEM(2,1,idty) var( BOOST_PP_CAT(var,i) ) #define PP_SEQ_CAPUTURE_T_DECL(r, idty, i, var)\ BOOST_PP_COMMA_IF(i) BOOST_PP_TUPLE_ELEM(2,1,idty) var(args_ptr->var) #define CLOSURE_START(seq) CLOSURE_START_FORWARD(seq,__LINE__, BOOST_PP_EMPTY()) #define CLOSURE_START_FORWARD(seq,id,ty) CLOSURE_START_T(seq,id,ty) #define CLOSURE_START_T(seq,id,ty) \ BOOST_PP_SEQ_FOR_EACH_I( PP_SEQ_CLOSURE_TAG_DECL,id,seq ) \ struct BOOST_PP_CAT(start_closure_args_t_,id) { \ BOOST_PP_SEQ_FOR_EACH_I( PP_SEQ_TYPE,id,seq ) \ BOOST_PP_CAT(start_closure_args_t_,id)( \ BOOST_PP_SEQ_FOR_EACH_I(PP_SEQ_CAPUTURE_DECL_T, id, seq) \ ) \ : BOOST_PP_SEQ_FOR_EACH_I(PP_SEQ_CAPUTURE_DECL, (id,ty), seq) {} \ } BOOST_PP_CAT(closure_args_value_,id) ( \ BOOST_PP_SEQ_FOR_EACH_I(PP_SEQ_CAPUTURE, (id,ty), seq) \ ); \ closure::args::declare<sizeof(closure_args)> \ ::apply<0> closure_args; \ closure_args.value = &BOOST_PP_CAT(closure_args_value_,id); \ struct start_closure_t_##id { \ BOOST_PP_CAT(start_closure_args_t_,id)* args_ptr; \ BOOST_PP_SEQ_FOR_EACH_I( PP_SEQ_TYPE,id,seq ) \ start_closure_t_##id( void* args ) \ : args_ptr((BOOST_PP_CAT(start_closure_args_t_,id)*)args) \ , BOOST_PP_SEQ_FOR_EACH_I(PP_SEQ_CAPUTURE_T_DECL, (id,ty), seq) {} #define CLOSURE_END(name) } name( closure_args.value ) #define CLOSURE_DEF operator() int main() { int i = 1,j = 2, k = 2; CLOSURE_START((i)(j)(k)) int CLOSURE_DEF (int x) const { return (i + j + k) * x; } CLOSURE_END(f0); std::cout << f0(5) << std::endl; int l = 10; CLOSURE_START((i)(j)(k)(l)) void CLOSURE_DEF(int x) { i += x; j += x; k += x; l += x; } CLOSURE_END(f1); f1(10); std::cout << i << std::endl; std::cout << j << std::endl; std::cout << k << std::endl; std::cout << l << std::endl; return 0; }
出力結果
25
11
12
12
20
多相に振舞うとか無理でした。orz
ローカルクラスにtemplateメンバー関数が持てないのでローカルクラス使ってる間は無理。
あとはきっと、どこかの変態さん成し遂げてくれるはず!