にゃははー

はへらー

退職エントリ

退職エントリです。 Twitterで結構前から辞めるだのどうのこうのって騒いでたので、見てた人はわかると思いますが。

正確には本日が最終出社日で、4末の退職です。

特に現職にどうこう言うつもりも無いので淡々と報告となります。

で、次職ですが、さっき正式な通知がきた感じです。某所の某社です。

入社は5頭からになるかがわからないのでもしかしたら5月中は無職となる可能性があるけどまぁなんとかします。

Boost.Fusion 1.58 updates

Boost.Fusion 1.58は頑張ったし、Damien Buhlも頑張ったのでそれなりな更新がいくつかあります。 まだBeta出てない*1けどそろそろmasterがcloseしてbugfixのみになるので機能としてはfixした感じ。

主な新機能

boost::hashのサポート

これまではFusionシーケンスは boost::hash をサポートしていなかったので、Unordered Associative Containerに投げることができませんでした。 今回、新たに hash.hpp が導入され、Boost.UnorderedへFusionシーケンスを投げることが出来るようになりました。 これはForward Sequenceであれば受け付けるので、アダプトしたシーケンスにも使えます。

#include <boost/unordered_set.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/make_vector.hpp>
#include <boost/fusion/include/equal_to.hpp>
#include <boost/fusion/include/hash.hpp> // new

int main()
{
    boost::unordered_set<boost::fusion::vector<int>> m;
    m.insert(boost::fusion::make_vector(0));
    m.insert(boost::fusion::make_vector(1));
}

今のところ、std::hashは特殊化されていないので、std::unordered_{multi,}{map,set}には直接使えませんが、boost::hashを渡すことで同様のことが出来るようになります。

#include <unordered_set>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/make_vector.hpp>
#include <boost/fusion/include/equal_to.hpp>
#include <boost/fusion/include/hash.hpp> // new

int main()
{
    std::unordered_set<boost::fusion::vector<int>, boost::hash<boost::fusion::vector<int>>> m;
    m.insert(boost::fusion::make_vector(0));
    m.insert(boost::fusion::make_vector(1));
}

1.59には入れたいと思います。

std::reference_wrapper

boost::reference_wrapperは前々から渡されたら通常の参照に展開するようになっていたんだけど、今回std::reference_wrapperもそうなるようになった。

#include <functional>
#include <cassert>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/make_vector.hpp>
#include <boost/fusion/include/at_c.hpp>

int main()
{
    int i = 0;
    auto v = boost::fusion::make_vector(std::ref(i));
    boost::fusion::at_c<0>(v) = 1;
}

reference_wrapperのままでもええやんって思うかもだけど、reference_wrapperのまま格納されると上のコードはコンパイルできない。

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

std::tuple

std::tupleのサポートは実は前々から存在してはいたんだけど、ドキュメントが無かったり、一分実装が抜けていたりした。 今回ドキュメントを用意したし、大体実装されているはず。

std::tuple - develop

いくつかのresult_ofをSFINAE-friendlyに

いくつかというのがミソ。1.59には全部をSFINAE-friendlyにしたい

アダプタでの暗黙的な型推論をサポート

これまでは構造体をアダプトするには型を書かないといけませんでした。

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/at_c.hpp>

namespace foo
{
    namespace very_very_cool_namespace
    {
        typedef int how_quite_greatest_awesome_type;
    }

    struct bar
    {
        very_very_cool_namespace::how_quite_greatest_awesome_type hoge;
        very_very_cool_namespace::how_quite_greatest_awesome_type fuga;
    };
}

BOOST_FUSION_ADAPT_STRUCT(
    foo::bar,
    (foo::very_very_cool_namespace::how_quite_greatest_awesome_type, hoge)
    (foo::very_very_cool_namespace::how_quite_greatest_awesome_type, fuga)
)

int main()
{
    foo::bar x = { 0 };
    boost::fusion::at_c<0>(x) = 0xdeadbeef;
}

very_very_cool_namespace::how_quite_greatest_awesome_typeを書くのは非常にだるいですし、構造体の変更に対して柔軟に対応できません。 一応これまでであっても型名の代わりにBOOST_FUSION_ADAPT_AUTOを使えばBoost.TypeOfでの推論を使用できていましたが、依然として書かないといけないのは面倒です。

今回、Damienが頑張ってプリプロセッサしたところ、Variadic Macrosをサポートした環境では更に省略出来るようになりました。

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/at_c.hpp>

namespace foo
{
    namespace very_very_cool_namespace
    {
        typedef int how_quite_greatest_awesome_type;
    }

    struct bar
    {
        very_very_cool_namespace::how_quite_greatest_awesome_type hoge;
        very_very_cool_namespace::how_quite_greatest_awesome_type fuga;
    };
}

BOOST_FUSION_ADAPT_STRUCT(
    foo::bar,
    hoge,
    fuga
)

int main()
{
    foo::bar x = { 0 };
    boost::fusion::at_c<0>(x) = 0xdeadbeef;
}

C++11/14 constexprのサポート

#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/make_vector.hpp>
#include <boost/fusion/include/at_c.hpp>

int main()
{
    constexpr auto v = boost::fusion::make_vector("foo", 0xdeadbeef);
    static_assert(boost::fusion::at_c<1>(v) == 0xdeadbeef, "Sadly, beef is daed ...");
}

見たまんまですね。これで不幸にも実行ができなくなってしまった環境であってもコンパイル時に計算できるので安心ですね。

C++14なら基本的にconstexprになってるはずです。C++11だといくつかがまだ対応出来てないです。少しずつなんとかしたいですね。

*1:3/11予定

CrystaX NDKとBoost

私はAndroidの開発をするわけではないが、Googleが出すAndroid NDKとは異なったNDKがあるらしい。

CrystaX .NET

サイト名に.NETがついているがMicrosoftとは無縁の様。

で、そこの開発チームが最近Boostへ積極的に関わってきている。
いくつかのライブラリは動作が確認され、いくつかの修正も提案されているっぽい。

CrystaX NDK自体の開発とかは追って無いからわからないけど、チームはregression testの結果をコミットしているので今後徐々に修正が入るだろう。

Boost.orgが出しているregression testのsummaryはここ。

Boost.orgのページは他のプラットフォームがあったり、そもそも重かったりなので、CrystaXだけが気になるのであれば、開発チームがそこだけを抜き出して公開しているのでそちらを見るのが良い。


とはいえC++11/14の環境でAndroidの開発ができるのなら遊んでみるのも良さそう。

便利かもしれない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)));
}

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

これでnamespace下にthrow_を置いたり出来るし、もうちょっといろいろな関数を経由したりするときにはcaller_ctxを持ちまわってやれば良い。


私は更にbacktraceを乗せたりして使っているのでなにか参考になれば幸いだ。
shinano/exception.hpp at develop · Flast/shinano · GitHub
shinano/exception.cpp at develop · Flast/shinano · GitHub

あ、ちなみにclangは実装してないから。m9m9m9

*1:boost exception - 1.57.0

*2:boost/throw_exception.hpp - 1.57.0

*3:実際には4.8から実装されているが、C++への対応が不十分で使い物にならない

*4:Bug 59821 – __builtin_LINE and __builtin_FILE for new'd objects is wrong

*5:Other Builtins - Using the GNU Compiler Collection (GCC)

*6:C++のデフォルト引数はcaller contextで評価されることが規定されている

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出来る謎が解決したわけですが、冒頭にも述べた

  • GCC 4.9.0, 4.9.1 (今のところGCC 4.9.2/5も)
  • C++14 mode

の場合、parenthesisで囲まれたオブジェクトを正しくrvalueとして扱わずにdelete指定されたcopy ctorを呼ぼうとしてしまうregressionがあります。

Bug 63437 – [4.9/5 regression][C++14] Parenthesized "movable but not copyable" object doesn't compile in return statement

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.勉強会 #16 大阪 - boostjp

便利!電動歯ブラシ | 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に対する意見求む

議論の末(?)*1C++11では

  1. 配列を特別扱い
  2. class typeの場合、begin()/end()を呼べれば呼ぶ
  3. そうでなければ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:保証はしない