第2回闇鍋プログラミング勉強会に行ってきた part1
ので、帰ってきた話。
全ては妥協から始まった
懇親会会場を後にして、つくばに戻ろうと神保町駅に入った。さて、このあと東京駅からバスで帰るか、いや、850円の差はでかいぞ...じゃぁつくばからいつもどおり歩くか。
そう決めて 神保町-大手町-北千住 と進んだ。北千住でつくばエクスプレスの改札を通り、構内の電光掲示板を眺めるとそこには燦然と輝く 守谷 の文字が。
ちくしょう、また守谷か...次のつくば行きはいつだ?
無かった...
ウッ...
仕方ない、今から東京駅に行こう。しかし一度改札を通ってしまった*1のでちょうど来た秋葉原行きに乗るしかない。秋葉原で山手線だ。
秋葉原につくと目の前で山手線のドアが閉まるし非常に面白かった。しかしそこは大都会東京。2分もしないうちに次の電車が来た。大丈夫か?
目の前で山手線のドア閉まる辺り面白い
— にゃははー仙人 (@Flast_RO) 2012年9月15日
すごい日付変わる前後面白すぎてハマる
— にゃははー仙人 (@Flast_RO) 2012年9月15日
旅の始まり東京駅
まぁわりかし簡単に東京駅には着くわけで、0:30最終のミッドナイトつくば号というぼったくり非常に重要な高速バスがあるわけで、これに乗るしかない。
時は既に0:26。券買ってちょうどぐらいか、とカウンターに向かうと、満席でキャンセル待ちとのこと。10人ぐらい並んでるし明らかに無理だと判断したのでこの時点でつくばに帰るのは諦めた。
満席だと言うのにキャンセル待ちで列を成す人たち精神力すごいと思うし何が人をそこまで掻き立てるんだろう
— にゃははー仙人 (@Flast_RO) 2012年9月15日
ところであの並んでた人達、無理だとわかって悲しみに暮れる瞬間を眺めてれば面白そうだったのにとあとで後悔した。
そして希望を持って並んでる人たちの無理だと確定した瞬間の絶望する姿みてきても良かったかもしれない
— にゃははー仙人 (@Flast_RO) 2012年9月15日
つくばに帰れないということは東京で朝まで何かしてないといけないわけで、まぁTXとつくばからの関鉄バスが良い感じに走り始める時間に秋葉原にいるように時間を潰すことを考えた。
まぁ結論としてこうなった。
ミッドナイトさん最終も満席なので山手線周回するかぁ
— にゃははー仙人 (@Flast_RO) 2012年9月15日
山手線沿線一周徒歩でどれぐらいかかるんだろう
— にゃははー仙人 (@Flast_RO) 2012年9月15日
よくわからない。
よくわからないついでによくわからない予想まで立てている。
でも4時間程度な気もするしなぁ
— にゃははー仙人 (@Flast_RO) 2012年9月15日
2週すれば多分ちょうどいい時間になるだろうしそのあとなのは観て帰るのもわんちゃんあるな
— にゃははー仙人 (@Flast_RO) 2012年9月15日
まぁそんなこんなで山手線を1周するという旅が始まったわけだ。
part2移行は暇があったら書く。いま卒論の中間提出が迫っているので...
Boost.Build v2の誰てめ
ところで君はだれだい?
numbers.log10
名前の通り常用対数を取るruleです。
import numbers ; ECHO [ numbers.log10 1 ] ; # => 0 ECHO [ numbers.log10 100 ] ; # => 2
注意なのが、bjamは全て文字列で処理するのでパラメータと戻り値は全て正数です。悲しいことに。
誰が使うのでしょうか。
container.vector
std::vectorとかstd::listをリスペクトしたものらしいです。sequenceあるのでいらない気もしますが。
vectorの要素は全てclass nodeから派生している必要があって、vectorもnodeから派生しています。
実際はクラスの派生関係は見ていないのでインターフェースが合えば問題無いです。
container.vector.get-at ruleは複数の引数を投げることができて、vectorを入れ子にしているときに多次元っぽくアクセスできる程度のありがたみがあります。
結局内部はsequenceなのでindexは1-origです。
import "class" ; import container ; v = [ class.new vector [ class.new vector [ class.new node 1 ] 2 3 4 ] ] ; ECHO [ $(v).get-at 1 : 1 ] ; # => 1 ECHO [ $(v).get-at 1 ] ; # => object(node)#659 2 3 4 #ECHO [ $(v).get-at 1 : 2 ] ; # => error 2はnodeの派生オブジェクトではない
最後のエラーがわかりづらいですが、C++っぽく書くと
vecotr<vector<int>> v = { { node(1), 2 3 4 } }; std::cout << v[0][0].get() << std::endl; std::cout << v[0].get() << std::endl; std::cout << v[0][1].get() << std::endl;
的な感じになるので(get-atの最後はget ruleが呼ばれる)無理ですねハイって感じです。
nodeのget ruleはnodeの値そのものを返しますが、vectorのget ruleはsequenceを返します。
超絶使いづらいです。
あ、でもいま使ってます。
message.jam
toolsetにはmessage.jamというファイルがあります。toolset以下にありますがusingではなくimportを使うtoolsetです。
で、こいつは何をするかというと文字通り文字列を表示するだけです。
import message ; message.message hoge : fuga ;
$ bjam fuga ...found 1 target...
ECHOとの違いは表示されるタイミングですが、説明が面倒なので省きます。
ちなみにBoostのビルドで最初に文字列が出ますが、あれはmessageを使ってます。
mc.jam / midl.jam / rc.jam
Win32でつかうあれなものをコンパイルするときに使うtoolset達。
GUIもちゃんとビルドできるんですね。使わないけど。
気がついた
もっとあるかと思ったけどよくよく眺めると微妙に使いそうな物が多い。
そしてそもそもBoost.Buildは小さいのでなんというかあややー?
Boost 1.50.0で追加されたライブラリ達
皆さんご存知の通りBoost 1.50.0がリリースされました。
このリリースで皆さん当然user-config.jamに
まぁ今回は変更ではなくて新しく追加されたライブラリについて。
Boost.Algorithmさんには若干失望してるので扱いません。
今回紹介する3ライブラリの作者はLorenzo Caminitiです。
Boost.Utility/IdentityType
Boost.Utility/IdentityType 1.0.0 - Boost 1.50.0
このライブラリはマクロを多用する人向けのライブラリです。これ自体は簡単なラッパーとなっています。
例えば特殊化をマクロを使って生成したりするパターン。以下の様なゴミコードが例。
template <typename T> struct is_fuga : public boost::false_type {}; #define IS_FUGA_SPEC(type_) \ template <> struct is_fuga<type_> : public boost::true_type {} IS_FUGA_SPEC(int); IS_FUGA_SPEC(std::pair<int, int>);
このコードがどのような問題があるかわからないようだったら実際にコンパイルしてみて下さい。というかプリプロセスですが。
とか言いながら結論を言うと2つ目のstd::pair<int, int>を投げたほうがエラーになります。
というのも IS_FUGA_SPEC(std::pair<int, int>) は std::pair<int という引数と int> という引数が渡されたと解釈されIS_FUGA_SPECには2引数渡せねぇよって怒られます。
Boost.Utility/IdentityTypeはこのような問題を回避することができます。
使ってみましょう。IS_FUGA_SPECは先の定義を使い回すとします。
#include <boost/utility/identity_type.hpp> IS_FUGA_SPEC(BOOST_IDENTITY_TYPE((std::pair<int, int>)));
若干長くなった感ありますがこれで意図した通り std::pair<int, int> の特殊化をマクロを使って定義できます。
こいつは何をやってるのでしょうか。2重になっている括弧がミソです。
実際のとは違いますが中身を書いてみます。
template <typename> struct identity_type_impl; template <typename T> struct identity_type_impl<void(T)> { typedef T type; } #define BOOST_IDENTITY_TYPE(parenthesized_type_) \ identity_type_impl<void parenthesized_type_>::type
たったこれだけです。
さっきの例を上の定義に当てはめて展開してみて下さい。よくできてることが分かると思います。
ちなみにプリプロセッサは ( から ) までを1つのトークン列として扱います。
この2重括弧は見覚えがあるのではないでしょうか。
BOOST_MPL_ASSERT((boost::is_same<int, void>)); BOOST_CONCEPT_ASSERT((boost_consepts::RandomAccessTraversal<RandomAccessIterator>));
まぁどれも同じ理由で2重になってるんですね。
あっ、あとdepending nameの場合は
BOOST_IDENTITY_TYPE((typename remove_reference<T>::type)) typename BOOST_IDENTITY_TYPE((remove_reference<T>))::type
好きな方でokです。(remove_refernece程度ならそもそもいらないですがそこはアレですよ。察せ
Boost.LocalFunction
Chapter 1. Boost.LocalFunction 1.0.0 - Boost 1.50.0
これはみなさん待ち望んだ感じのローカル関数です。
単なるラッパーだと思ってたのですがこないだドキュメント眺めてめちゃくちゃ強力なセマンティクスを提供してるのを知って衝撃受けてます。そして思ってた設計と全然違って実現方法が全然わからないです。。。あとで細かく追わないと...
わからないので特に説明しないです。
#include <iostream> #include <boost/local_function.hpp> template <typename F> void exec(F f) { f(1); } int main() { void BOOST_LOCAL_FUNCTION(int i) { std::cout << i << std::endl; } BOOST_LOCAL_FUNCTION_NAME(f) exec(f); }
なんでtemplate parameterに突っ込めるんだろう...
Boost.Functional/OverloadedFunction
Chapter 1. Boost.Functional/OverloadedFunction 1.0.0 - Boost 1.50.0
これはC11の_Genericsに近いです。
複数の関数を束ねて1つのオーバーロードされた関数に見せかけるものです。
特に例も思いつかないのでサンプルそのまま転載です。手抜きです。
const std::string& identity_s(const std::string& x) // Function (as pointer). { return x; } int identity_i_impl(int x) { return x; } int (&identity_i)(int) = identity_i_impl; // Function reference. double identity_d_impl(double x) { return x; } boost::function<double (double)> identity_d = identity_d_impl; // Functor. boost::overloaded_function< const std::string& (const std::string&) , int (int) , double (double) > identity(identity_s, identity_i, identity_d); // All calls via single `identity` function. BOOST_TEST(identity("abc") == "abc"); BOOST_TEST(identity(123) == 123); BOOST_TEST(identity(1.23) == 1.23);
で、こいつと先のBoost.LocalFunctionは組み合わせることでローカルなのにオーバーロードされた関数を作ることができます。
まぁわかると思うので例は出さないですが。
conclusion
全体的に地味なのが多いので一般ウケはしないだろうけどそれなりなものが来てます。使いましょう。
そして1.55.0にとっととならないですかね。
Boost.Phoenix V3でのmove
例えば common.hpp を次のように書いたとする。
#include <boost/move/move.hpp> struct S { BOOST_MOVABLE_BUT_NOT_COPYABLE(S) public: S() {} S(BOOST_RV_REF(S)) {} }; void f(S) {}
で、このSをfにmoveしてみる。
#include "common.hpp" #include <boost/move/move.hpp> int main() { S s; f(boost::move(s)); }
まぁここまでは当然動く。
で、ここから本題。上のコードをBoost.Phoenix化したくて下のように書いてみても当然エラーになる。
#include "common.hpp" #include <boost/phoenix/core.hpp> #include <boost/phoenix/bind.hpp> int main() { S s; boost::phoenix::bind(f, boost::move(s))(); }
まぁこれは何となく分かるわけで、例えばreference_wrapperみたいに
#include "common.hpp" #include <boost/phoenix/core.hpp> #include <boost/phoenix/bind.hpp> #include <boost/ref.hpp> template <typename T> struct rvref { rvref(T &value) : value(&value) {} operator BOOST_RV_REF(T)() const { return boost::move(*value); } public: T *value; }; int main() { S s; rvref<S> rvs(s); boost::phoenix::bind(f, boost::ref(rvs))(); }
って適当に書いてもよくわからないがC++11でしか通らない。
C++11だけで通っても結局は
@Flast_RO いえ、単に Boost.Phoenix だとコンパイルが Boooost してしまうので lambda を使っているだけですね。 Boost.Phoenix と C++11 の相性に関してはよく知りません。
2012-06-28 11:38:17 via web to @Flast_RO
なわけで、、、、
まぁMotivationとしては同じコードで03/11で動いて欲しいと。ライブラリコードだとそうなりますね。まぁBOOST_NO_LAMBDASでdispatchしてもいいですけど。
どちらにせよmoveっぽい何かは欲しいわけで、仕方ないから書いた。
https://github.com/Flast/boost.mmm/blob/master/boost/mmm/detail/phoenix/move.hpp
そうするとこうなるわけで、
#include "common.hpp" #include <boost/phoenix/core.hpp> #include <boost/phoenix/bind.hpp> #include <boost/mmm/detail/phoenix/move.hpp> #include <boost/ref.hpp> int main() { S s; boost::phoenix::bind(f, boost::mmm::detail::phoenix::move(boost::ref(s)))(); }
結局BOOST_NO_RVALUE_REFERENCEで分岐するぐらいだったらlambdaで分岐しても良かったかもしれないと今更ながら思ってる。
とはいえrv-refあってもlambdaないclang 3.0とかあるわけで、結局必要か。
Boost.Contextのこととか話した
Boost.勉強会 #9 つくば - boostjp
Boost9 session
並行と並列とスレッド
前半はなんか概念的なことばっかり話したと思う。しかも後半でほとんど関係なくなったし。
とりあえずユーザースレッドってものを導入したかったのだと思う。多分。
カーネルスレッドとユーザースレッドを別物みたいに語ってるけど本質は同じなわけで、特権命令か一般命令かとかそういう話をしっかりできたほうが良かったのだろうか。
けどそうするとリングプロテクションがとかなって時間が爆発してしまうことになる...
Boost.Context
一点補足するの忘れてた。boost::ctx::stack_allocatorを普通に使って初期化してましたけど、stack_allocator::allocateはスタックのtopのアドレスを返すので注意です。
つまり普通のアロケータと違って確保した領域の末尾のアドレスを返します。なので帰ってきたアドレスをそのまま使おうとすると悲しくなります。
あと他のアロケータがほとんどdeallocateの第2引数を捨ててるからといってstack_allocator::deallocateの第2引数を適当に渡すと、確保領域の先頭アドレスを計算できなくて死ぬので正しい値を渡すようにしてください。
あとスライド中の幾つかの例は簡単のため型が正しくないです。
ctx::make_fcontextに渡せるコンテキストのエントリーポイントは全て void(intptr_t) となる関数へのポインタのみとなりますし、ctx::jump_fcontextの第3引数に投げられる値もintptr_tです。ctx::jump_fcontextが返す型もintptr_tです。
なのでキャストとか結構しないとうまくいかないです。
一方、Boost.Coroutineはシグネチャさえ合えば任意のfunctorを投げられます。特別な理由がない限りBoost.Coroutineを使うのが正しいと思います。
exitは呼ばれないですし例外は拾ってくれるしdtorは呼ばれますし。
C++標準委員会のくだり
紹介したpaperは基本的に”並行並列実行”自体を扱うものでした。実際は、concurrent queueとかも既に提案されてるのでやはり並行並列の関心は高いようです。
まとめ
就職先を考えねばならない
Boost.勉強会 #9 つくばを主催した
主催っていうのは存外難しい
まず、会場の確保。これは何度も言っているけど新城靖准教授にしていただいたことで実現できた。
実際確保だけはできるけど教室機材は使ってはいけないとか言われる。教員が借りると問題ないらしい。イミワカラン...
まぁそんなこんなでなんとか会場を抑えたわけだけど、ともなると次は会場ネットワークの提供がある。
今回程の規模の人数を捌くには適当なブロードバンドルーター的なの1台では到底不可能なわけでCisco SystemsのAironetという業務用レベルのものを2台用意した。
APは用意できたけどその先、大学の回線から出ていくわけにもいかないわけで、WIDEの線から出したい。
そうすると我々は大学内にL2の接続性を持っているわけではないのでPacketiX VPNでL2をブリッジしてWIDE経由で出れるようにした。
しかし会場とWIDEの間が170Mbpsしか出ていないのは如何ともし難かった。遅い。
当日は先輩方に最寄りのバス停から会場までの案内をお願いした。
流石に主催が会場にいないというのも変な話なわけで、まぁ仕方ないと私の中で勝手に納得した。
つまるところ企画は1人でもできるが運営ともなると根本的に1人では無理。いや、当然だけど。
改めてつきつけられた感。特にこれだけの人数だと。
ust
ゆーすとのliveもやったんだけどすごい不安定だった。ust参加の皆様には申し訳なかったです。
ちなみにustでは録画してないです。録画してたとしても見れたもんじゃないと思うのです...
で、ローカルのカメラ自身に録画させてたので後日それをどこかにうpします。
ustの録画はSDですけどこっちはHDです。ようつべだと時間制限あるしニコ動だと画質落とされるかもしれないので単にファイルを置くだけになるかもしれないです...
懇親会
個人的にこちらのほうが問題が大きかったと思う。当日まで人数の増減があるってのはちょっと困る。(迫真
大阪勢
懇親会の時に大阪勢が一切合切来れない理由を知った。なんか申し訳ないことをしてしまったと思ってしまう...
まとめ
就職先を考えねばならない
Ubuntu 11.04以降で自前でgccをビルドした場合のアーキテクチャ依存ファイルについて
Ubuntu 11.04以降で幾つかのアーキテクチャ依存ファイルのパスが変更されてしまい、upstream GCCを自前コンパイルするとインクルードやリンクが通らないという問題があります。
通常、/etc/environmentに
CPATH=/usr/include/x86_64-linux-gnu LIBRARY_PATH=/usr/lib/x86_64-linux-gnu
などと書いておくと問題ないのですが、悲しいことにsudo経由等うまく環境変数を引き継げない場合にやはりエラーとなってしまいます。
ということでオープンソースなのでデフォルトで見るようにソースを書き換えましょう。
Ubuntu 12.04 amd64環境で、GCC 4.7.1 20120428(prerelease)時点でのdiffが以下のようになります。i686環境の人は追加部分のx86_64-linux-gnuをi386にすれば問題ないはずです。
--- gcc/collect2.c +++ gcc/collect2.c @@ -1363,6 +1363,7 @@ AIX loader always searches for libraries. */ add_prefix (&libpath_lib_dirs, "/lib"); add_prefix (&libpath_lib_dirs, "/usr/lib"); + add_prefix (&libpath_lib_dirs, "/usr/lib/x86_64-linux-gnu"); #endif /* Get any options that the upper GCC wants to pass to the sub-GCC. --- gcc/gcc.c +++ gcc/gcc.c @@ -1100,6 +1100,9 @@ #ifndef STANDARD_STARTFILE_PREFIX_2 #define STANDARD_STARTFILE_PREFIX_2 "/usr/lib/" #endif +#ifndef STANDARD_STARTFILE_PREFIX_3 +#define STANDARD_STARTFILE_PREFIX_3 "/usr/lib/x86_64-linux-gnu/" +#endif #ifdef CROSS_DIRECTORY_STRUCTURE /* Don't use these prefixes for a cross compiler. */ #undef MD_EXEC_PREFIX @@ -1139,6 +1142,8 @@ = STANDARD_STARTFILE_PREFIX_1; static const char *const standard_startfile_prefix_2 = STANDARD_STARTFILE_PREFIX_2; +static const char *const standard_startfile_prefix_3 + = STANDARD_STARTFILE_PREFIX_3; /* A relative path to be used in finding the location of tools relative to the driver. */ @@ -6381,6 +6386,10 @@ add_sysrooted_prefix (&startfile_prefixes, standard_startfile_prefix_2, "BINUTILS", PREFIX_PRIORITY_LAST, 0, 1); + if (*standard_startfile_prefix_3) + add_sysrooted_prefix (&startfile_prefixes, + standard_startfile_prefix_3, "BINUTILS", + PREFIX_PRIORITY_LAST, 0, 1); } /* Process any user specified specs in the order given on the command --- gcc/incpath.c +++ gcc/incpath.c @@ -459,7 +459,10 @@ /* Finally chain on the standard directories. */ if (stdinc) - add_standard_paths (sysroot, iprefix, imultilib, cxx_stdinc); + { + add_standard_paths (sysroot, iprefix, imultilib, cxx_stdinc); + add_path ("/usr/include/x86_64-linux-gnu", SYSTEM, 1, false); + } target_c_incpath.extra_includes (sysroot, iprefix, stdinc);
ハードコーディングバンザイ