CrystaX NDKとBoost
私はAndroidの開発をするわけではないが、Googleが出すAndroid NDKとは異なったNDKがあるらしい。
サイト名に.NETがついているがMicrosoftとは無縁の様。
で、そこの開発チームが最近Boostへ積極的に関わってきている。
いくつかのライブラリは動作が確認され、いくつかの修正も提案されているっぽい。
CrystaX NDK自体の開発とかは追って無いからわからないけど、チームはregression testの結果をコミットしているので今後徐々に修正が入るだろう。
Boost.orgが出しているregression testのsummaryはここ。
Boost.orgのページは他のプラットフォームがあったり、そもそも重かったりなので、CrystaXだけが気になるのであれば、開発チームがそこだけを抜き出して公開しているのでそちらを見るのが良い。
便利かもしれないGCC builtin
これは恐らくC++ Advent Calendar 2014の15日目です。
古来[要出典]より人々はいかに例外を投げた奴をトレースするかに命をかけてきた[要出典]。
投げられた例外自体や、そのメッセージを確認することはできるが、一体誰がその例外を投げたかという情報は一切乗らないからだ。
いっそのことsegfaultでもしてくれたほうがデバッガでスタックトレースを出力できる。
ある人は考えるだろう。
template <typename Base> struct exception_info : Base { explicit exception_info(const Base &base, const char *file, int line) : Base(base), file(file), line(line) {} virtual ~exception_info() noexcept {} const char *file; int line; }; template <typename E> [[noreturn]] inline void throw_(const E &e, const char *file = __FILE__, int line = __LINE__) { throw exception_info<E>(e, file, line); } { throw_(std::runtime_error("hogefuga")); }
残念。__FILE__と__LINE__はプリプロセッサで展開されてしまうので、throw_がある行とファイルが例外に乗って飛んでくる。
正解は
template <typename E> [[noreturn]] inline void throw_(const E &e, const char *file, int line) { throw exception_info<E>(e, file, line); } #define THROW(e) throw_(e, __FILE__, __LINE__) { THROW(std::runtime_error("hogefuga")); }
さて、関数名もほしい。C++11なら__func__が使える。GCCなら__FUNCTION__(場合によっては__PRETTY_FUNCTION__かもしれない)だ。
catchする側は今度受け取った例外オブジェクトがexception_infoから派生しているか確認して、fileとか取れるなら表示したい。RTTIで分岐するしかない。
とまぁ、ここまで来ると正直Boost使いましょう*1*2ってなる。なれ。
#include <boost/throw_exception.hpp> #include <boost/exception/diagnostic_information.hpp> { BOOST_THROW_EXCEPTION(std::runtime_error("Boooooooooooooost")); } catch (std::exception &e) { std::cout << boost::diagnostic_information(e) << std::endl; }
まぁでも、結局Boostもだいたい同じようなことをやっているので、エラー食らった後にいろいろやって、結果、例外を投げる的な共通関数をつくった時にはいい感じにBoostも使えない(そのいい感じの共通関数を呼んだ奴が結局誰かわからない)。
関数呼び出しもマクロで囲めば良いんだけど何でもかんでもマクロなのはほげふが〜ってなってしまうので、もうちょっとスマートな方法を探したい。
そんな時にはGCC 4.9以降*3*4で使える次のbuiltin関数*5を使うと良いかもしれない。
- __builtin_LINE()
- __builtin_FILE()
- __builtin_FUNCTION()
それぞれ、__LINE__、__FILE__と__func__に対応している。
これらは関数であるのでプリプロセッサで展開はされないし、デフォルト引数として使えば呼んだ奴のコンテキストで評価される*6。
実際に使ってみるとこうなる。
[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ
しかし自分でlineとか扱うとなると、先のBOOST_THROW_EXCEPTIONは使えないので、boost::throw_exceptionを呼ぶようにちょっと加工してやる。
template <typename E> [[noreturn]] inline void throw_(E &&ex, std::tuple<const char *, int, const char *> caller_ctx = std::make_tuple(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION())) { boost::throw_exception( boost::enable_error_info(ex) << boost::throw_function(std::get<2>(caller_ctx)) << boost::throw_file(std::get<0>(caller_ctx)) << boost::throw_line(std::get<1>(caller_ctx))); }
これでnamespace下にthrow_を置いたり出来るし、もうちょっといろいろな関数を経由したりするときにはcaller_ctxを持ちまわってやれば良い。
私は更にbacktraceを乗せたりして使っているのでなにか参考になれば幸いだ。
shinano/exception.hpp at develop · Flast/shinano · GitHub
shinano/exception.cpp at develop · Flast/shinano · GitHub
あ、ちなみにclangは実装してないから。m9m9m9
returnとmoveと() ~ GCCを添えて
Boost.Move 1.56 で追加されたBOOST_MOVE_RET*1*2というマクロがあるのですが、GCC 4.9のC++14モードだけなぜかコンパイルできないと言う事象にぶつかってしまい、そういえばここらへんの暗黙のmoveとかって最後にドタバタしてたなぁと思いつつあんまりしっかり見ていなかったのですが、これを気に調べたのでまとめます。
基本的にコードを書いたりする上では気にする必要のない部分です。
BOOST_MOVE_RETマクロ自体はそれほど使い方が難しいわけではないですが、一応例示しておきます。
#include <boost/move/core.hpp> struct movable { BOOST_COPYABLE_AND_MOVABLE(movable) // copyもmoveもできる public: movable() { } movable(BOOST_RV_REF(movable)) { } // move ctor movable(const movable &) { } // copy ctor }; movable foo() { movable m; return BOOST_MOVE_RET(movable, m); }
上記コードは、C++03ではヘルパーを介しつつBoost.Moveを使ってmove ctorを呼びます。
一方、C++11以降では単に
movable foo()
{
movable m;
return (m);
}
へと置き換えられます。ところで、parenthesisで囲まれた式の型はdecltypeで調べれば解りますが、以下のようになります。
int i; decltype(i) // int decltype((i)) // int &
これは(おそらく)割と知られていると思いますが、これをふまえて、parenthesisで囲まれている先のreturn文はmove ctorが呼ばれるでしょうか。それともcopy ctorが呼ばれるでしょうか。
もしあなたが、答えに窮し以下の様な式でmoveとcopyどちらが行われるか疑問に思った場合、いい線をいってます。
movable foo()
{
movable m;
return m;
}
return文に渡された`m`という式の型は`movable &`ではなく`movable`ですが、これは確かにlvalueです。lvalueを渡しているのにmove ctorが呼ばれるでしょうか。
結論から言うとparenthesisで囲っていようがいまいがmove ctorが呼ばれます。そして、意外にもmove ctorが呼ばれる理由にはNRVOが関係してきます。
このreturn文での挙動はn3376 12.8 [class.copy] paragraph 31と32に記載があります。
paragraph 31ではcopy/move ctorを"実際"に呼ばなくていい条件(つまり、どういう時にNRVOを行ってよいか)を列挙していて、paragraph 32では、先の条件を満たした場合にはその式をどう評価するかと言うことが書かれています。
paragraph 31によると、return文では
- non-volatile automatic object
- オブジェクトの型とreturn typeが同じcv-unqualified type
の場合、copy/move ctorを省略出来ると書いてあります。
そしてparagraph 32では、paragraph 31の条件を満たしたオブジェクトがlvalueで渡されたとき、copyのために呼ばれるctorは、
- オブジェクトをrvalueとして扱った上でoverload resolutionする
- 先のoverload resolutionが失敗、若しくはctorの第一引数がrv-refでなかった場合は、オブジェクトをlvalueとして扱い再度overload resolutionする
- copy/move ctor呼び出しを"実際に"行わなくてもこの二段階のoverload resolutionは必ず実行される
とあります。
ちなみにこの文言だけではparenthesisで囲まれた場合moveできないと解釈できてしまうのですが、CWG DR1579でこの問題が解消されています。
ということで、return文でmove出来る謎が解決したわけですが、冒頭にも述べた
の場合、parenthesisで囲まれたオブジェクトを正しくrvalueとして扱わずにdelete指定されたcopy ctorを呼ぼうとしてしまうregressionがあります。
C++11 modeではコンパイル出来るので今のところあまり大きい問題では無いと思いますが、1.56.0のBOOST_MOVE_RETをC++14 modeのGCC 4.9で使用した場合はこの問題にぶつかってしまいます。
とてもひどい方法ですが、この問題にぶつかってしまった場合には
#define BOOST_MOVE_MSVC_AUTO_MOVE_RETURN_BUG #include <boost/move/core.hpp>
とすると回避できます。
GCCの修正かBoost 1.57のcloseのどちらか早いタイミングに合わせて修正を投げるつもりでいるので、1.57では改善されるはずです。
Boost.勉強会 #16で発表してきた
この発表したあとにBoostの開発のこと聞かれたり色々したので、やっぱりキチガイキワモノ達の発表もいいけど、こういう簡単なセッションもあったほうがいいんだろうなぁと思った。数回前まではアキラさん(id:faith_and_brave)が一周の旅とかやってたけど、ああいうの。
それはそうとして、30分あれば十分やろとか思って、じゃぁ30分でって言ったら、実は60分喋ってたらしいし、そういうところはもうちょっとしっかりしたほうが良かったかなぁと思った。
それはそうとして、タイトル、あーメール送らんとなぁと思いながら何話すかその時は具体的に決めてなかったので、適当に適当を重ねて送った結果、割と混乱というか変な期待をさせていた感じあったので、それも考えたほうが良かったなぁと思った。
まぁ色々反省はあるけど、アリスSOSは見れたのでトータルとしてOKだった。
range-based forとstd名前空間
Boost.Coroutineに関してBoost users MLでrange-based forに関する面白い話が流れたので紹介したい。
この話は私も初めて聞いて驚いた。
C++11で新規追加されたrange-based forはconceptが削除されたタイミングでその挙動をどうするかで議論していた。なつかしい。
本の虫: range-based forに対する意見求む
- 配列を特別扱い
- class typeの場合、begin()/end()を呼べれば呼ぶ
- そうでなければstd名前空間をassociated namespacesに加えた上でADL
となりました。
しかし、ADLは挙動が非常にわかりづらく、特に問題なのがunqualified lookupするのかしないのかはADLの定義には書かれていません。
どちらかと言えば、unqualified lookupした結果、更にADLを加えるというのが本来意図された挙動でした。
さて、range-based forは最終的にADLするということになったわけですが、ここでunqualified lookupするかしないかという問題が出てきました。
結局この問題はDR1442*2となり、ADL時にはunqualified lookupをしないとなりました。
実際、GCCやClangといったコンパイラは、range-based for実装当初からunqualified lookupを行いません。
struct X { }; int main() { struct X x; int * begin(X &); int * end(X &); begin(x); // unqualified lookup for (auto _ : x) ; // エラー: begin/endが見つからない }
ところでこのDR1442ですが、よく見るとADLの挙動の明確化(unqualified lookupしないことの明確化)だけかと思うと、そうではありませんでした。
なんと、std名前空間をassociated namespaceに加えるという文言を削除しています。
つまり、これまでwell-formed*3だった以下のコードがill-formedとなるわけです。
namespace mylib { struct my_very_very_awesome_container { }; } namespace std { int * begin(mylib::my_very_very_awesome_container &) { return nullptr; } int * end(mylib::my_very_very_awesome_container &) { return nullptr; } } int main() { mylib::my_very_very_awesome_container c; for (auto x : c) (void)x; // std名前空間はassociated namespaceなのでbegin/endが呼ばれるはず }
なにせstd名前空間をlookup対象にするという文言を消しているので。
このassociated namespaceに関する挙動については、melpon氏謹製のwandboxによると
- clang 3.0 ~ 3.1
- gcc 4.6 ~ 4.8
の間は、よく知られた(と思う)std名前空間をlookupに含める挙動を示すようだ。
C++11として発行された規格ではstd名前空間を含めるはずだが、C++14ではなく、DRとして取り込まれたのでこれはもはやC++11の挙動だといって過言ではないだろう*4。
この挙動で頭を悩ませるのはboostのようなライブラリを書いている場合だろうから多分一般的には問題とはならないだろう。
Boost.Coroutineはstd名前空間にbegin/endを置いたので今回その対象となってしまった。
元議論:
Boost - Users - Cannot use Boost.Croutines with range based for
DR1442:
C++ Standard Core Language Defect Reports and Accepted Issues
*1:私はWGの人間ではないので積極的に議論されたかは知らない
*2:http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1442
*3:これがwell-definedだと言い切る自信がない。言い切る自信がある方、ぜひコメント頂きたい
*4:保証はしない
Debian GNU/Hurd 2013のインストールでパーティションを切りたい
Debianのインストールから、カーネルビルドまで...はいかない - にゃははー のハマリポイント4をなんとか回避できた。
結論から言うとprocfsがmountsを提供しないのが原因で、手動で切ったパーティションがマウントされているかを確認できていなかったのが原因でした。
具体的にはDebian GNU/Hurd 2013のインストールメディアは僅差でGNU Mach 1.3.99/Hurd 0.3という古いバージョンで収録されているが、手元のGNU Mach 1.4/Hurd 0.5では/proc/mountsがあったので、頑張って手動でこのファイルを作ればよかった。
手順としては、パーティション切ったところで何とかしてshellをとって
# settrans -g /proc # cat > /proc/mounts /dev/hd0s8 /target ext2fs defaults 0 0 /dev/hd0s3 /target/boot ext2fs defaults 0 0
ってする。
catめんどかったらnanoで。vimもviも無いです。
あとdpkgもないので、hurd 0.5を頑張って持ってきても手で頑張ってやらないといけないので普通にproc書いたほうが楽だと思う。
/proc/mounts 以外のprocfsなものはどうやら使わないみたいなので、/procにmountsしかなくても良かったです。
expert modeでしか確認してないので他のインストール方法でどうやってshell取るかは知らないです。多分出来ないです。
ほげ〜