Hatena::ブログ(Diary)

にっき(pseudo)

2012/12/20(木)

な〜にがStrongTypedefじゃ

この記事はC++ Advent Calendar 2012の20日目の記事です.

導入

ある型に別名を付けるtypedefに対して,ある型を元に新しい型を作る機能をstrong typedefと言います.もちろんC++の仕様としてそのようなものがあるわけではなく,Boost.Serializationにマクロとして実装されているほか,同様のマクロによる実装がいくつかあります.

なぜこのようなものが必要かという話ですが,世の中にはグーグル=センセイという実際博識なセンセイがいらっしゃいますので,氏にお尋ねいただくのがよろしいかと思います.

ところで,Boost.Serializationにあるstrong typedefには制限があり,適用範囲が組み込み型に限られるのです.その他の実装でも,元となる型との間で暗黙変換を許可する/しないを指定する術がなく,実装によって決められてしまいます(知ってる限りの実装においては).私は変換だけでなく,ありとあらゆる元の型のメンバについて公開する,しないの指定をしたいと思いました.

思ったので作りました.

できました.ここにあります.

https://github.com/dechimal/desalt/

おおざっぱな機能の紹介

はじめに

以下のコードは全て

#include <vector>
#include <iostream>
#include <desalt/newtype.hpp>

があるものとします.

最小の定義

試しにvector<int>を元に新しい型を作ります.

DESALT_NEWTYPE(ivector, std::vector<int>,
    as_base
);

これでstd::vector<int>を元にivectorという新しい型を定義しました.この型には次の操作ができます.

  • std::vector<int>からivectorの新しいオブジェクトを作る
  • コピーする
  • ivectorオブジェクトをas_baseメンバ関数によって明示的にvector<int>として扱えるようにする
  • 破棄する
元のクラスのメンバをそのまま使う

これだけやったら別にマクロいらんやろ!!!と思ったので,もっと機能を付け足しました.

DESALT_NEWTYPE(ivector, std::vector<int>,
    as_base,
    begin,
    end
);

これでbeginとendが呼べるようになりました.本当ですか?試してみましょう.

int main() {
    ivector iv(std::vector<int>{1,2,3});
    for (auto i : iv) {
        std::cout << i << '\n';
    }
    std::vector<int> & v = iv.as_base();
}

range based for使うために必要なものは,iteratorを返すbeginとendです.もしこれがコンパイルできないなら,今すぐそのようなザコなコンパイラは捨てましょう.

コンストラクタもそのまま使う

上のマクロを見れば分かるように,メンバの名前を書けば,元のクラスのメンバを呼び出せるようになります.ところで,やはり元のクラスのコンストラクタもそのまま使いたいと思うこともあるでしょう.やりましょう.

DESALT_NEWTYPE(ivector, std::vector<int>,
    as_base,
    begin,
    end,
    this
);

int main() {
     ivector iv1{1,2,3};
     ivector iv2(iv1.begin(), iv2.end());
}

this とだけ書けばstd::vector<int>の全てのコンストラクタがivectorでも余裕のよっちゃんで使えるようになります.

テンプレート

ところでさいきんtemplate aliasesってのがあるじゃないですか.template<typename T> using vec = std::vector<T>; みたいなの.これみたいにtemplateに対しても別名宣言できたりは…するんですねこれが.

template<typename T>
DESALT_NEWTYPE(dynamic_array, std::vector<T>,
    as_base,
    begin,
    end,
    this
);

int main() {
    dynamic_array<int> di{1,2,3};
    for (auto i : di) {
        std::cout << i << '\n';
    }
}
手動で機能を追加する

最後の手段として,自分で勝手にメンバとかを付け加えたりもできます.

template<typename T>
DESALT_NEWTYPE(dynamic_array, std::vector<T>,
    as_base,
    begin,
    end,
    this,
    new (
        void f() const { std::cout << "hoge\n"; }
    )
);

int main() {
    dynamic_array<int> di{1,2,3};
    for (auto i : di) {
        std::cout << i << '\n';
    }
    di.f();
}
その他

で,本当なら他にも機能あったんですが,まだほとんどの場合正しく動かないので使えません(実装を読んで挙動を理解できるなら使ってもよい).

書式

DESALT_NEWTYPE(identifier, underlying_type, convert_to_base_function_name);
DESALT_NEWTYPE(identifier, underlying_type, convert_to_base_function_name, ...);
  • identifier : 新しい型の名前
  • underlying_type : 元になる型の名前
  • converto_to_base_function_name : 元の型の値を取り出す用の関数の名前

... は以下の要素のどれかをカンマで区切って並べたN個の列 (N ≦ 64)

this // using underlying_type::underlying_type;
member_name // using underlying_type::member_name;
friend function_name // friend function_name;
friend class class_name // friend class class_name;
typename type // using typename underlying_type::type;
new ( ... ) // ... をそのまま定義に付け加える

2011/12/05(月)

Boost.Preprocessor の可変長マクロ引数対応

これはBoost Advent Calendar 2011の5日目の記事です。

さて、Boostも徐々にC++11対応が進んでいまして、その中には当然、Boost内で最も利用されている、超有名かつ超重要、某社のサーバでも利用されているという純粋関数型言語C/C++プリプロセッサ用拡張ライブラリBoost.Preprocessorも入っているわけです。そこで今回はそのBoost.PPのC++11対応についてのお話をしたいと思います。

C++11におけるプリプロセッサの新機能といえばなんと言っても可変長マクロ引数、これしかありません。念のために例を示しますと、

#define ROTATE(...) ROTATE_I(__VA_ARGS__)
#define ROTATE_I(x, ...) __VA_ARGS__, x

ROTATE(1, 2) // 2, 1
ROTATE(ROTATE(1, 2, 3)) // 3, 1, 2

というように、関数形式のマクロで任意個の引数を取ることができるようになる機能です。詳しくはC++11 Advent Calendar 2011 4で書きます(BoostではなくC++11であることに注意、この記事が最初に公開された時点ではなぜか存在しません)。完璧な流れですね。

さてここからが本題です。Boost.PPの可変長マクロ引数への対応としては、タプル操作系の関数形式マクロの機能拡張と、新しい関数形式マクロがあります(なお、ここではプリプロセッサの話しかしないので、今後はいちいち「関数形式マクロ」と言わず単に「関数」と言います)。

まずはタプル操作系関数から紹介します。

タプル操作系関数の機能拡張

機能拡張の説明の前にひとつ。普段から実務でプリプロセッサを使っていらっしゃるみなさまにそんな野暮な説明など不要であるとは思いますが、念のためごく簡単に申し上げておきますと、Boost.PPで言う「タプル」とは、バランスの取れたカッコで囲まれたカンマ区切りのトークン列です。

拡張されたタプル操作関数は、操作対象のタプルの長さを与えてやる必要がなくなりました。BOOST_PP_TUPLE_ELEMを例にとって見てみます。

// (a, b, c) の1要素目(つまりb)を取得する
BOOST_PP_TUPLE_ELEM(3, 1, (a, b, c)) // 可変長マクロ引数のサポート無し
BOOST_PP_TUPLE_ELEM(1, (a, b, c))    // サポート有り

サポート無し版の最初の引数が要素数です。タプルの長さを書く必要がなくなりました。なお、サポートが有る場合であっても、依然として長さを与えることもでき、その場合はサポート無しのときと同様に振る舞います。つまり長さが合っていないと実行中にエラーとなります。

なお、いくつかのタプル操作関数は「F(size) tuple」という形式を持ちますが、これらの関数では「F() tuple」という形式でサポートされます。以下、例です。

TUPLE_EAT(3) (a, b, c)
TUPLE_EAT() (a, b, c)

追加された関数

実のところタプル操作系関数の拡張部分は、この追加された関数を利用して実装されています。ここでは主要な関数について説明していきます。

BOOST_PP_VARIADIC_ELEM(i, ...)

この関数は、最初の引数を除いた引数全体の展開後のトークン列から、i番目の引数を取得する関数です。ただし実装の都合上、iは64未満です。

BOOST_PP_VARIADIC_ELEM(1, a, b, c) // b

#define ARGS1 a, b
#define ARGS2 , c, d, e
BOOST_PP_VARIADIC_ELEM(3, ARGS1 ARGS2) // d

この関数には注意があり、GCCとClangでは-pedantic-errorsを付けないと

BOOST_PP_VARIADIC_ELEM(0)
BOOST_PP_VARIADIC_ELEM(1,)
BOOST_PP_VARIADIC_ELEM(2,,)
…

は全てvalidとなってしまいます。これは、GCCのデフォルトでは

#define F(a, ...)

なるFについて、

F(x)

を許すことに由来する挙動です(規格では許されていません)。VCやその他のコンパイラのプリプロセッサではどうなのか知りませんが、ご存知の方がいれば教えてください。

なお、これはその他の可変長マクロ引数に対応している関数全体についても言えます。

BOOST_PP_VARIADIC_SIZE(...)

...を全て展開した後のトークン列に対して、カンマ区切りの要素数を数える関数です。

BOOST_PP_VARIADIC_SIZE(a, b, c) // 3
BOOST_PP_VARIADIC_SIZE(,,,,) // 5

この関数にも注意があり、

BOOST_PP_VARIADIC_SIZE()

は1です。これについてはC++11 Advent Calendar 4を参照してください。

BOOST_PP_VARIADIC_TO_*(...)

以下の4つの関数

  • BOOST_PP_VARIADIC_TO_ARRAY
  • BOOST_PP_VARIADIC_TO_LIST
  • BOOST_PP_VARIADIC_TO_SEQ
  • BOOST_PP_VARIADIC_TO_TUPLE

は、...を展開してできたカンマ区切りトークン列を、名前にある通りのデータ構造を持つトークン列に変換する関数です。釈迦に説法ではございますが念のために例を挙げますと、

BOOST_PP_VARIADIC_TO_ARRAY(a, b, c) // (3, (a, b, c))
BOOST_PP_VARIADIC_TO_LIST(a, b, c) // (a, (b, (c, BOOST_PP_NIL)))
BOOST_PP_VARIADIC_TO_SEQ(a, b, c) // (a)(b)(c)
BOOST_PP_VARIADIC_TO_TUPLE(a, b, c) // (a, b, c)

となります。

これらの関数でも、0引数での呼び出し(例:BOOST_PP_ARRAY()など)では0要素目が空トークン列である1要素の各構造を持つトークン列を作ります。

BOOST_PP_OVERLOAD

この関数は、引数の数によって関数を擬似的にオーバーロードするための関数です。あるいは引数の数に応じて別の関数へとディスパッチするための関数と言ったほうが分かりやすいと思う方もいらっしゃるでしょう。次の例は、引数を全てトークン連結する関数です。

#define CAT(...) BOOST_PP_OVERLOAD(CAT_, __VA_ARGS__)(__VA_ARGS__)

#define CAT_1(a0) a0
#define CAT_2(a0, a1) BOOST_PP_CAT(a0, CAT_1(a1))
#define CAT_3(a0, a1, a2) BOOST_PP_CAT(a0, CAT_2(a1, a2))
#define CAT_4(a0, a1, a2, a3) BOOST_PP_CAT(a0, CAT_3(a1, a2, a3))

早い話、BOOST_PP_OVARLOAD(F, トークン列) でトークン列の長さを調べてからその長さをFに連結するだけの関数です。これによってBOOST_PP_TUPLE_*はタプルのサイズが与えられているかどうかを認識して挙動を変えているのです。

その他

使用中のコンパイラが可変長マクロ引数に対応しているかどうかを調べるために、BOOST_PP_VARIADICSという識別子が定義されています。これが1である場合、この記事で紹介している各種新機能が利用できます。

ただしこの識別子、Clangについてはガン無視なので自分で定義する必要があります。

まとめ

いかがでしたでしょうか?今まで以上に便利になったカンマ区切りトークン列でより快適なプログラミングが可能になるでしょう。

また、カンマ区切りのトークン列というのはC++プログラマにとって大変なじみ深い見た目であるため、これを自由に使えるようになるということは、よりC++になじんだ見た目の擬似的拡張構文を作る際にも役立ちます。

それでは次のBoost Advent Calendar 2011, id:osyo-mangaをお楽しみください。

可変長マクロ引数の話

これはC++11 Advent Calendar 20114日目の記事です。

C++11ではプリプロセッサが可変長マクロ引数に対応しました。例を挙げましょう。

#define ROTATE(...) ROTATE_I(__VA_ARGS__)
#define ROTATE_I(x, ...) __VA_ARGS__, x

ROTATE(1, 2) // 2, 1
ROTATE(ROTATE(1, 2, 3)) // 3, 1, 2

というように、関数形式のマクロで任意個の引数を取ることができるようになる機能です。

それではこの新機能について詳しく見ていきましょう。

(なお、ここではプリプロセッサの話しかしないので、今後はいちいち「関数形式のマクロ」と言わず単に「関数」と言うことにします。また同様に、「識別子」と言えば「プリプロセス時の識別子」のことです。)

まずは関数の定義です。

#define ROTATE(...) ROTATE_I(__VA_ARGS__)
#define ROTATE_I(x, ...) __VA_ARGS__, x

"..." が可変長なパラメータリストの部分を表します。対して、"..." で捕えた引数リストは__VA_ARGS__という識別子で利用します。ROTATEにおいては全ての引数が__VA_ARGS__によって捕捉されますが、ROTATE_Iでは最初の引数だけxに捕捉され、残りの引数が __VA_ARGS__ に捕捉されます。

この識別子は、単一の引数ではなく引数リストを捕捉するという性質以外に、その他の関数のパラメータとの違いはありません。これが意味するところはまたの機会ということにして、今はとりあえず「F(__VA_ARGS__)とか書いたらちゃんと__VA_ARGS__内に含まれるカンマは引数の区切りとして認識される」程度に思っておいてください。

ところで、上のROTATEの実装では、一部望ましくない挙動を示します。次のコード

ROTATE(1)

はエラーとなります。これはROTATE_Iが原因で、

#define F(x, ...) …

なる関数Fにおいて、

F(a)

という呼び出しが規格では許されておらず、空でもいいのでxの後にもう一つ引数を要求します。つまり、少なくともF(x,)みたいに書く必要があります。1パラメータ + "..."の場合に限らず、一般にn>=1パラメータ + "..."で定義された関数に対してn引数での呼び出しは許されていません。個人的には可変長テンプレート引数の挙動と合わせてほしいのですがまぁそうなっていないものはなっていないので仕方ありません(ただしGCCとClangでは、-pedantic-errorsを付けない限りnパラメータ + "..." に対するn引数での呼び出しを許可します)。

であるため、ROTATEについてのより好ましい実装は次のようになります。

#include "boost/preprocessor/variadic/size.hpp"
#include "boost/preprocessor/comparison/equal.hpp"
#include "boost/preprocessor/control/iif.hpp"
#include "boost/preprocessor/tuple/rem.hpp"

#define ROTATE(...) BOOST_PP_IIF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1), BOOST_PP_TUPLE_REM(), ROTATE_I)(__VA_ARGS__)
#define ROTATE_I(x, ...) __VA_ARGS__, x
ROTATE(1)
ROTATE(1, 2)
ROTATE(ROTATE(1, 2, 3))

Boost.Preprocessorを利用して、引数の数が1つである場合だけ、何もしないようにする、という判定を入れました。もしかしたら「0引数の場合は?」と思われる方もいらっしゃるかもしれませんが、その場合__VA_ARGS__は空のトークン列を捕捉しているので結局1引数と同様に処理されます。

いかがでしたでしょうか?今まで以上に便利になったカンマ区切りトークン列でより快適なプログラミングが可能になるでしょう。

また、カンマ区切りのトークン列というのはC++プログラマにとって大変なじみ深い見た目であるため、これを自由に使えるようになるということは、よりC++になじんだ見た目の擬似的拡張構文を作る際にも役立ちます。

それでは次のC++ Advent Calendar 2011, id:bolerosをお楽しみください(遅いわボケが)。

2011/11/24(木)

Boostのドキュメントを個別にビルドするためのメモ

必要なもの:

  • xsltproc
  • docbook-xml
  • docbook-xslt
  • doxygen

Boost.Geometry

  • python
  • rapidxml (Boost.Geometryのドキュメントビルドするためのツールのビルドに)

ROOTをJamrootのあるディレクトリとします。

  1. cd $ROOT
  2. ./bootstrap.sh
  3. cp bjam $パスの通ってるディレクトリ
  4. 次の設定を$HOME/user-config.jamに書く

    using xsltproc;
    using boostbook
        : /usr/share/xml/docbook/stylesheet/
        : /usr/share/xml/docbook/4.2/;
    using doxygen;
    using quickbook;
    
  5. cd libs/some-library/doc
  6. 普通のライブラリの場合
    1. bjam
    2. できあがり
  7. Boost.Geometry の場合
    1. cd src/docutil/tools/doxygen_xml2qbk
    2. bjam dist-bin
    3. cd ../../../..
    4. PATH=$ROOT/dist/bin:$PATH ./make_qbk.py</liL

    5. できあがり

参考

2011/08/22(月) たおっぱいおっぱい

生きてることをアピールするために否定演算子の後置をする

私が成仏した、または地獄送りになったのではないかという疑惑を払拭するために大変どうでもいいコードで更新。

http://www.ideone.com/hXj1y

上のコードは

ということで id:kikairoya さんが実際に動かしてみたものです。

つまるところ自分では試してない!他力本願上等!

2011/04/27(水)

モテるミサ力を磨くための4つの心得

こんにちは、一方マネジメントを専攻しているウチドメ嬢です。私は身長も胸も足りませんしゴーグルもないですが、恋愛に関してはプロフェッショナル。今回は、モテるミサ力を磨くための4つの心得を皆さんにお教えしたいと思います。

1. あえて2〜3世代前のケータイを持っていく

あえて2〜3世代前のケータイを使うようにしましょう。そして飲み会の場で好みの男がいたら話しかけ、わざとらしくケータイを出していじってみましょう。そして「この携帯端末にはとてもストレスが溜ります、とミサカは普段から抱いている不満をここぞとばかりに露わにします」と言って、男に「どうかしたか?」と言わせましょう。言わせたらもう大成功。「ミサカは常時ネットワークに接続しているので、このような端末は普段あまり使用しないのですが、それにしてもこの携帯端末は古くて使いにくいのです、とミサカは不満の理由を述べます」と言いましょう。だいたいの男はあなたのお姉様と新しいケータイをペア契約したばかりなので、試験的な新しいケータイを使っているはずです。

そこで男が「新しいケータイにしないの?」と言ってくるはず(言ってこない空気が読めない男は完全武装で蜂の巣OK)。そう言われたらあなたは「そういえば、最近iPhone5という端末の評判が大変良いと聞きましたが、この手の情報はあまり詳しくなく、ミサカに適した端末であるか判断できません、とミサカは自分のネットワークの便利さを示唆します」と返します。すると男は「あー、それは多分iPhone4だ。5はまだ出てないんじゃないかな。まぁ俺もよく知らないんだけど……じゃあ今度一緒にケータイ買いにいくか?」という話になって、次の休みの日にふたりでケータイ選びのデートに行けるというわけです。でも、あなたのミサ力が高くても男は万年金欠なのでケータイは買ってもらえないでしょう。

2. Twitterで「とミサカは大いに困惑した表情を浮かべます」を使うとモテる

「キャー!」とか「悲しい!」などを表現する「とミサカは大いに困惑した表情を浮かべます」をコメントに入れると、Twitterの男性ユーザーは「なんやこの子カワイイなぁ」や「ボクぁ落下型ヒロインのみならず、(中略)幽霊獣耳娘内心曝け出し娘まであらゆる女性を迎え入れる包容力を持ってるから支えてあげたいわぁ」と思ってくれます。インターネット上では現実世界よりもイメージが増幅されて相手に伝わるので 「とミサカは大いに困惑した表情を浮かべます」 を多用することによって、男性はあなたを可憐で女の子らしいと勘違いしてくれるのです。そういうキャラクターにするとほぼ絶対に他のミサ…同性に嫌われますが気にしないようにしましょう。

3. とりあえず男には「それは何でしょう?とミサカはまるであなたの話題に興味があるようなアピールをします」と言っておく

ファミレスなどで男が女性に話すことといえば病院やお姉様の話ばかり。よって、女性にとってどうでもいい話ばかりです。でもそこで適当に「そうなのですか、とミサカは適当に相槌を打ちます」とか「あなたの言うことはあまり理解できないですが、すごいのですね、とミサカは露骨に興味のない反応をします」と返してしまうと、さすがの男「妹にとってはあんまり面白くない話だったな……」と気がついてしまいます。ダメミサ…女だとバレたら終わりです。そこは無意味にテンションをあげて、「それは面白そうな話ですね、もう少し聞かせてもらえませんか?とミサカは仕方なく興味を持った素振りをします」と言っておくのが正解。たとえ興味がない話題でも、テンションと積極性でその場を乗り切りましょう。積極的に話を聞いてくれる女性に男は弱いのです。

いろいろと話を聞いたあと、「なるほど、覚えておきます、とミサカは既に話の大半を忘れつつ答えます」とコメントすればパーフェクト。続けて頭に指をさしてくるくる回しつつ「キュンキュンキュン、キュンキュンキュン、とミサカは擬音を口にしながら反吐の出るようなジェスチャーをします」と言って、「どうしたんだ?」と男に言わせるのもアリ。そこで「ネットワークにアップロードしています、とミサカはさきほどの行動の意味を答えます」と言えばミサ力アップ! そこでまた男は「まぁ、よく分かんないけど、妹だしな……」と思ってくれます。私は身長も胸もありませんしゴーグルもないですが、こういうテクニックを使えば胸がない私のような幼女のほうがモテたりするのです。男はお姉様とその周囲の世界を守りたいですからね。

4. レストランではオムライスを食べられないミサ…女をアピールせよ

男とレストランに入ったら、真っ先にオムライスなどの卵を使った料理を探して「実は私、これを食べられないのです、とミサカは卵料理が食べられないことを告白します」と言いましょう。するとほぼ100パーセント「どうしたんだ? 卵嫌いなのか?」と聞かれるので、「嫌いではなく食べたいのですが、食べられないのです、とミサカは困惑した表情を浮かべます」と返答しましょう。ここでまた100パーセント「嫌いじゃないのにどうして食べられないんだ?」と聞かれるので、うつむいて3〜5秒ほど間をおいてからボソッとこう言います。「……なぜなら、卵を割ると中のヒヨコは産まれることもなく、ピヨピヨと鳴くこともなく死んでしまう、と……ミサカは……!」と漏電しながら身を震わせて言うのです。

その瞬間、あなたのミサ力がアップします。きっと男は「ヒヨコって食用の卵からも産まれたのか! 絶対に(卵を)守ってみせる! ミサカの前で卵を食うなんて幻想、俺がぶち殺してやる!」と心のなかで誓い、卵を守り抜くはずです。意中の男と付き合うことになったら、そんなことは忘れて好きなだけオムライスを食べて大丈夫です。「食べられないんじゃなかったっけ?」と言われたら「気にしなくなりました、とミサカはしれっと言い放ちます」とか「もう慣れました、とミサカはさらりと流します」、「そのようなことを言った記憶はありません、とミサカはその発言をなかったことにします」と言っておけばOKです。

(文=一方マネジメント・ウチドメ嬢)

参考:http://kirei.biglobe.ne.jp/news/detail/20110426162331_pch19894

2011/04/14(木) 元の鞘に収まるエンドウ豆

BOOST_FOREACHに添え字を付けてみた

手段と目的が交差するとき、無駄努力が始まる。

ほしいという声を聞いたので、BOOST_FOEACHという素材を活かす方向でなんとか。

#include <iostream>
#include <vector>
#include <boost/foreach.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/assign/std/vector.hpp>

#define INDEX_FOREACH(elem, cont, n) \
    INDEX_FOREACH_I(elem, cont, n, BOOST_PP_CAT(_index_foreach_label_, __LINE__))
#define INDEX_FOREACH_I(elem, cont, n, label) \
    if (bool _index_foreach_break = false) {} else \
    if (size_t _index_foreach_index = 0) {} else \
    label: \
    if (_index_foreach_break) {} else \
    BOOST_FOREACH(elem, cont) \
    for (bool _index_foreach_flag = true; \
         _index_foreach_flag; \
         _index_foreach_flag && (_index_foreach_break = true)) \
    if (_index_foreach_break) { goto label; } else \
    for (n = _index_foreach_index; \
         _index_foreach_flag; \
         ++_index_foreach_index, _index_foreach_flag = false)

struct hoge {
    int n;
    hoge(int n) : n(n) {}
};

int main() {
    int ar[] = {10,20,30,40,50,60};
    INDEX_FOREACH (int n, ar, size_t i) {
        if (i == 2) continue;
        std::cout << i << ": " << n << '\n';
        if (i == 4) break;
    }

    std::cout << "-------------------\n";

    using boost::assign::operator+=;
    std::vector<hoge> v;
    v += 20,40,60,60,80,100;
    INDEX_FOREACH (hoge const & x, v, size_t i) {
        if (i % 2) continue;
        std::cout << i << ": " << x.n << '\n';
        break;
    }
}

結果:http://ideone.com/BcH8i

書きながらこれでいいんじゃないかと思った

2011/04/13(水) あうあうあー

else節のないifを式でやる

:の片側は適当な式で埋めたconditional-expressionでもいいんだけどこういうのもアリね。

#include <iostream>
 
struct hoge {
    void f() { 
        std::cout << "hoge\n";
    }
};
 
int main() {
    hoge x;
    false && ((void)x.f(), 0); // if (false) { x.f(); }
    std::cout << "-----------------\n";
    true || ((void)x.f(), 0); // Perl とかの unless 相当
}

結果:http://ideone.com/HJIbG

x.f()をvoidにキャストしてるのはx.f()の型がoperator,をオーバーロードしてるかもしれないからです。

2011/03/17(木) ぴぃーーーーーーーーーー

Phoenix v3で遊ぶ

日記を書かないと死ぬらしい(ソース不明)ので書きましょう。

Phoenix v3がtrunkにあるということで、前に作ったコードを修正しながら遊んでみました。そういえば前は確かapply作ろうとしたらbindになったとか言いましたが、よく考えたらやっぱりapplyでした。applyにプレースホルダ組み合わせたらbind(apply<R>(), f, a, b…) と似たようなものですね。

というか、Phoenixの関数オブジェクトfはbind(f, …)と同じようなもんだと思えばややこしくない。


#include <boost/config.hpp>

#if defined(BOOST_HAS_TR1_UTILITY) && !defined(BOOST_NO_VARIADIC_TEMPLATES) && !defined(BOOST_NO_DECLTYPE) && !defined(BOOST_NO_RVALUE_REFERENCES) && !defined(BOOST_NO_DECLTYPE)
#  define AVAILABLE_0X_FEATURE 1
#else
#  define AVAILABLE_0X_FEATURE 0
#endif

#include <boost/phoenix/core.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/phoenix/scope.hpp>
#include <boost/phoenix/function.hpp>
#include <boost/phoenix/statement.hpp>
#include <iostream>
#include <string>

#if AVAILABLE_0X_FEATURE
#  include <utility>
#  include <type_traits>
#else
#  include <boost/utility/result_of.hpp>
#  include <boost/type_traits/remove_reference.hpp>

#  include <boost/preprocessor/arithmetic/inc.hpp>
#  include <boost/preprocessor/facilities/intercept.hpp>
#  include <boost/preprocessor/facilities/expand.hpp>
#  include <boost/preprocessor/repetition/enum_params.hpp>
#  include <boost/preprocessor/repetition/enum_shifted_params.hpp>
#  include <boost/preprocessor/repetition/repeat.hpp>
#  include <boost/preprocessor/repetition/repeat_from_to.hpp>
#  include <boost/preprocessor/seq/seq.hpp>
#  include <boost/preprocessor/seq/for_each_product.hpp>
#  include <boost/preprocessor/seq/for_each_i.hpp>
#  include <boost/preprocessor/tuple/rem.hpp>
#endif

namespace bst = boost;

#if AVAILABLE_0X_FEATURE

struct apply_t {
    template<typename>
    struct result;
    template<typename This, typename F, typename ...AS>
    struct result<This(F, AS...)> {
        typedef decltype(std::declval<F>()(std::declval<AS>()...)) type;
    };
    template<typename F, typename ...AS>
    typename result<apply_t(F &&, AS &&...)>::type
    operator()(F && f, AS && ...as) const {
        return std::forward<F>(f)(std::forward<AS>(as)...);
    }
};

#else

#define DEF_SPECIALIZED_NESTED_RESULT(z, n, data) \
    template<typename This, BOOST_PP_ENUM_PARAMS_Z(z, n, typename T)> \
    struct result<This(BOOST_PP_ENUM_PARAMS_Z(z, n, T))> { \
        typedef typename \
        bst::result_of< \
            typename bst::remove_reference<T0>::type \
            (BOOST_PP_ENUM_SHIFTED_PARAMS_Z(z, n, T))>::type \
        type; \
    };

#define DEF_APPLY_OVERLOADS_OP(z, n, data) \
  BOOST_PP_SEQ_FOR_EACH_PRODUCT(DEF_APPLY_OVERLOADS, \
                                (((z, n))) BOOST_PP_REPEAT_ ## z(n, MAKE_CONST_SEQ, ~))

#define DEF_APPLY_OVERLOADS(r, seq) \
  DEF_APPLY_OVERLOADS_I( \
    r, \
    BOOST_PP_EXPAND(BOOST_PP_TUPLE_REM(2) BOOST_PP_SEQ_HEAD(seq)), \
    BOOST_PP_SEQ_TAIL(seq))
#define DEF_APPLY_OVERLOADS_I(r, zn, seq) DEF_APPLY_OVERLOADS_II(r, zn, seq)
#define DEF_APPLY_OVERLOADS_II(r, z, n, seq) \
    template<BOOST_PP_ENUM_PARAMS_Z(z, n, typename T)> \
    typename result<apply_t( \
        BOOST_PP_SEQ_FOR_EACH_I_R(r, PARAMS_M, \
                                  BOOST_PP_INTERCEPT, seq))>::type \
    operator()(BOOST_PP_SEQ_FOR_EACH_I_R(r, PARAMS_M, p, seq)) const \
    { \
        return p0(BOOST_PP_ENUM_SHIFTED_PARAMS_Z(z, n, p)); \
    }

#define PARAMS_M(r, data, i, elem) \
    BOOST_PP_COMMA_IF(i) T ## i elem & data ## i

#define MAKE_CONST_SEQ(z, n, _) (()(const))

struct apply_t {
    template<typename>
    struct result;
    BOOST_PP_REPEAT_FROM_TO(1, BOOST_PP_INC(BOOST_PHOENIX_LIMIT), DEF_SPECIALIZED_NESTED_RESULT, ~)
    BOOST_PP_REPEAT_FROM_TO(1, BOOST_PP_INC(BOOST_PHOENIX_LIMIT), DEF_APPLY_OVERLOADS_OP, ~)
};

#endif

namespace p3 {
using namespace bst::phoenix;
using namespace bst::phoenix::arg_names;
using namespace bst::phoenix::local_names;
}

p3::function<apply_t> const apply;

template<typename Sig>
struct fptr_adapt_t;

template<typename R, typename A0, typename A1>
struct fptr_adapt_t<R(A0, A1)> {
    typedef R result_type;
    R operator()(A0 a0, A1 a1) const {
        return f(a0, a1);
    }
    fptr_adapt_t(R (*f)(A0, A1)) : f(f) {}
    R (*f)(A0, A1);
};

int f(int x, int y) {return x * y;}

template<typename F>
fptr_adapt_t<F> adapt_function(F * const & f) { return fptr_adapt_t<F>(f); }

p3::function<fptr_adapt_t<int(int, int)> > const ff(adapt_function(&f));

int main () {
    using std::cout;
    using namespace p3;

    int n = 42;

    cout << ::apply(_1, n, _2)(&f, 2) << '\n';

    let (_a = _1) [cout << ::apply(_a, _2, _3) << '\n'](&f, n, 2);

    cout << ff(_1, 2)(3) << '\n';
    cout << ff(1, 2)() << '\n';
    cout << ff(_1, apply(_2, 3, _3))(2, ff(_1, _2), 4) << '\n';
    return 0;
}

2011/01/15(土) おお勇者よ、情けない

新しいswitch statementのご案内

前に http://ideone.com/7NnRj というのを書いて、その後パターンマッチとかできればうれしいなーと思ったので、そんなものを書いた。書いてるうちにぼくのかんがえたさいきょうのぱたーんまっち。という記事が先に上がってたのでこれお蔵入りかなーとか思ったけど、せっかくtraitsで拡張できるようにしたんだし公開しよう、そうしよう、というわけで公開します。ほげー

実際のところはパターンマッチというか、boost::variantっぽいものから中に入っているオブジェクトを取り出そうと試みて、取り出せなかったら次の節を試して、取り出せたらそのオブジェクトをtieで初期化させろみたいにバラす、というのを同時にやるものです。query, unwrap, unpackというtraitsがあって、説明は省略(うまくできない)。まぁcpp -E -Pでもしたあと適当に改行とか入れて読んでください。ちなみに今回はrvalue referenceに対する取り組みはやっていません。考えるのめんどくさくなったので。あとネストした構造に対しては何も考えてません。


namespace arbital { namespace match {

template<typename Tag, typename T, typename = void>
struct query {
    template<typename U>
    static U & apply(U & v) {
        return v;
    }
};

template<typename T, typename = void>
struct unwrap {
    template<typename U>
    static U & apply(U & v) {
        return v;
    }
};

template<typename Tag, typename T, typename = void>
struct unpack {
    template<typename U>
    static U & apply(U & v) {
        return v;
    }
};

}}


#include <boost/variant/variant_fwd.hpp>
#include <boost/variant/get.hpp>
#include <boost/optional/optional.hpp>
#include <boost/none.hpp>

namespace arbital { namespace match {

template<typename Tag, BOOST_VARIANT_ENUM_PARAMS(typename T)>
struct query<Tag, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> > {
    template<typename U>
    static auto apply(U & v)->
        boost::optional<decltype(*boost::get<Tag>(&v)) &>
    {
        if (auto chosen = boost::get<Tag>(&v)) {
            return boost::optional<decltype(*chosen) &>(*chosen);
        } else {
            return boost::none;
        }
    }
};

}}


#include <boost/optional/optional.hpp>

namespace arbital { namespace match {

template<typename T>
struct unwrap<boost::optional<T> > {
    template<typename U>
    static auto apply(U & v)->decltype(boost::get(v)) {
        return boost::get(v);
    }
};

}}


#include <boost/fusion/support/category_of.hpp> // is_random_access
#include <boost/fusion/support/is_sequence.hpp>
#include <boost/fusion/container/vector/vector_fwd.hpp>
#include <boost/fusion/sequence/intrinsic/at.hpp>

#include <boost/utility/enable_if.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/or.hpp>

namespace arbital { namespace match {

template<typename N, typename T>
struct unpack<N, T,
    typename boost::enable_if<
        boost::mpl::and_<boost::fusion::traits::is_sequence<T>,
                         boost::fusion::traits::is_random_access<T> > >::type>
{
    template<typename U>
    static auto apply(U & v)->
        decltype(boost::fusion::at<N>(v))
    {
        return boost::fusion::at<N>(v);
    }
};

}}


#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/logical/not.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
#include <boost/preprocessor/facilities/intercept.hpp>
#include <boost/preprocessor/punctuation/paren.hpp>
#include <boost/preprocessor/punctuation/comma.hpp>

#define ARBITAL_PP_TUPLE_SIZE(tuple) \
    ARBITAL_PP_TUPLE_SIZE_I(ARBITAL_PP_TUPLE_REM tuple ARBITAL_PP_TUPLE_SIZE_TABLE)
#define ARBITAL_PP_TUPLE_SIZE_I(x) ARBITAL_PP_TUPLE_HEAD((ARBITAL_PP_TUPLE_DROP_255(x)))

#define ARBITAL_PP_TUPLE_ELEM(n, tuple) \
    ARBITAL_PP_TUPLE_ELEM_I( \
        BOOST_PP_ENUM_TRAILING_PARAMS(BOOST_PP_SUB(255, n), \
                                      BOOST_PP_INTERCEPT) \
        ARBITAL_PP_TUPLE_REM tuple)
#define ARBITAL_PP_TUPLE_ELEM_I(x) ARBITAL_PP_TUPLE_HEAD((ARBITAL_PP_TUPLE_DROP_255(x)))

#define ARBITAL_PP_TUPLE_REM(...) __VA_ARGS__

#define ARBITAL_PP_TUPLE_EAT(...)

#define ARBITAL_PP_TUPLE_HEAD(tup) \
    ARBITAL_PP_TUPLE_HEAD_I tup
#define ARBITAL_PP_TUPLE_HEAD_I(...) \
    ARBITAL_PP_TUPLE_HEAD_II(__VA_ARGS__,)
#define ARBITAL_PP_TUPLE_HEAD_II(_1,...) _1

#define ARBITAL_PP_TUPLE_TAIL(tup) \
    ARBITAL_PP_TUPLE_TAIL_I tup
#define ARBITAL_PP_TUPLE_TAIL_I(...) \
    ARBITAL_PP_TUPLE_TAIL_II(__VA_ARGS__ ARBITAL_PP_TUPLE_EAT BOOST_PP_LPAREN() , BOOST_PP_RPAREN())
#define ARBITAL_PP_TUPLE_TAIL_II(_1,...) (__VA_ARGS__)

#define ARBITAL_PP_TUPLE_TAKE(n, tuple) \
    ARBITAL_PP_TUPLE_TAKE_III( \
        BOOST_PP_IIF(BOOST_PP_NOT(n), \
                    () ARBITAL_PP_TUPLE_EAT, \
                    ARBITAL_PP_TUPLE_TAKE_I)(n, tuple))
#define ARBITAL_PP_TUPLE_TAKE_I(n, tuple) \
        ARBITAL_PP_TUPLE_TAKE_II( \
            BOOST_PP_ENUM_TRAILING_PARAMS(BOOST_PP_SUB(255, n), \
                                          BOOST_PP_INTERCEPT), \
            ARBITAL_PP_TUPLE_REM tuple)
#define ARBITAL_PP_TUPLE_TAKE_II(commas, tuple) \
    (ARBITAL_PP_TUPLE_TAKE_255(ARBITAL_PP_TUPLE_EAT BOOST_PP_LPAREN() commas BOOST_PP_RPAREN() tuple))
#define ARBITAL_PP_TUPLE_TAKE_III(res) ARBITAL_PP_TUPLE_TAKE_IV(res)
#define ARBITAL_PP_TUPLE_TAKE_IV(res) res

#define ARBITAL_PP_TUPLE_DROP_255( \
    _1,_2,_3,_4,_5,_6,_7,_8,_9,_10, \
    _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
    _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
    _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
    _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
    _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
    _61,_62,_63,_64,_65,_66,_67,_68,_69,_70, \
    _71,_72,_73,_74,_75,_76,_77,_78,_79,_80, \
    _81,_82,_83,_84,_85,_86,_87,_88,_89,_90, \
    _91,_92,_93,_94,_95,_96,_97,_98,_99,_100, \
    _101,_102,_103,_104,_105,_106,_107,_108,_109,_110, \
    _111,_112,_113,_114,_115,_116,_117,_118,_119,_120, \
    _121,_122,_123,_124,_125,_126,_127,_128,_129,_130, \
    _131,_132,_133,_134,_135,_136,_137,_138,_139,_140, \
    _141,_142,_143,_144,_145,_146,_147,_148,_149,_150, \
    _151,_152,_153,_154,_155,_156,_157,_158,_159,_160, \
    _161,_162,_163,_164,_165,_166,_167,_168,_169,_170, \
    _171,_172,_173,_174,_175,_176,_177,_178,_179,_180, \
    _181,_182,_183,_184,_185,_186,_187,_188,_189,_190, \
    _191,_192,_193,_194,_195,_196,_197,_198,_199,_200, \
    _201,_202,_203,_204,_205,_206,_207,_208,_209,_210, \
    _211,_212,_213,_214,_215,_216,_217,_218,_219,_220, \
    _221,_222,_223,_224,_225,_226,_227,_228,_229,_230, \
    _231,_232,_233,_234,_235,_236,_237,_238,_239,_240, \
    _241,_242,_243,_244,_245,_246,_247,_248,_249,_250, \
    _251,_252,_253,_254,_255,...) \
    __VA_ARGS__

#define ARBITAL_PP_TUPLE_TAKE_255( \
    _1,_2,_3,_4,_5,_6,_7,_8,_9,_10, \
    _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
    _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
    _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
    _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
    _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
    _61,_62,_63,_64,_65,_66,_67,_68,_69,_70, \
    _71,_72,_73,_74,_75,_76,_77,_78,_79,_80, \
    _81,_82,_83,_84,_85,_86,_87,_88,_89,_90, \
    _91,_92,_93,_94,_95,_96,_97,_98,_99,_100, \
    _101,_102,_103,_104,_105,_106,_107,_108,_109,_110, \
    _111,_112,_113,_114,_115,_116,_117,_118,_119,_120, \
    _121,_122,_123,_124,_125,_126,_127,_128,_129,_130, \
    _131,_132,_133,_134,_135,_136,_137,_138,_139,_140, \
    _141,_142,_143,_144,_145,_146,_147,_148,_149,_150, \
    _151,_152,_153,_154,_155,_156,_157,_158,_159,_160, \
    _161,_162,_163,_164,_165,_166,_167,_168,_169,_170, \
    _171,_172,_173,_174,_175,_176,_177,_178,_179,_180, \
    _181,_182,_183,_184,_185,_186,_187,_188,_189,_190, \
    _191,_192,_193,_194,_195,_196,_197,_198,_199,_200, \
    _201,_202,_203,_204,_205,_206,_207,_208,_209,_210, \
    _211,_212,_213,_214,_215,_216,_217,_218,_219,_220, \
    _221,_222,_223,_224,_225,_226,_227,_228,_229,_230, \
    _231,_232,_233,_234,_235,_236,_237,_238,_239,_240, \
    _241,_242,_243,_244,_245,_246,_247,_248,_249,_250, \
    _251,_252,_253,_254,_255,...) \
    _1,_2,_3,_4,_5,_6,_7,_8,_9,_10, \
    _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
    _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
    _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
    _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
    _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
    _61,_62,_63,_64,_65,_66,_67,_68,_69,_70, \
    _71,_72,_73,_74,_75,_76,_77,_78,_79,_80, \
    _81,_82,_83,_84,_85,_86,_87,_88,_89,_90, \
    _91,_92,_93,_94,_95,_96,_97,_98,_99,_100, \
    _101,_102,_103,_104,_105,_106,_107,_108,_109,_110, \
    _111,_112,_113,_114,_115,_116,_117,_118,_119,_120, \
    _121,_122,_123,_124,_125,_126,_127,_128,_129,_130, \
    _131,_132,_133,_134,_135,_136,_137,_138,_139,_140, \
    _141,_142,_143,_144,_145,_146,_147,_148,_149,_150, \
    _151,_152,_153,_154,_155,_156,_157,_158,_159,_160, \
    _161,_162,_163,_164,_165,_166,_167,_168,_169,_170, \
    _171,_172,_173,_174,_175,_176,_177,_178,_179,_180, \
    _181,_182,_183,_184,_185,_186,_187,_188,_189,_190, \
    _191,_192,_193,_194,_195,_196,_197,_198,_199,_200, \
    _201,_202,_203,_204,_205,_206,_207,_208,_209,_210, \
    _211,_212,_213,_214,_215,_216,_217,_218,_219,_220, \
    _221,_222,_223,_224,_225,_226,_227,_228,_229,_230, \
    _231,_232,_233,_234,_235,_236,_237,_238,_239,_240, \
    _241,_242,_243,_244,_245,_246,_247,_248,_249,_250, \
    _251,_252,_253,_254,_255

#define ARBITAL_PP_TUPLE_SIZE_TABLE 256,255,254,253,252,251, \
        250,249,248,247,246,245,244,243,242,241, \
        240,239,238,237,236,235,234,233,232,231, \
        230,229,228,227,226,225,224,223,222,221, \
        220,219,218,217,216,215,214,213,212,211, \
        210,209,208,207,206,205,204,203,202,201, \
        200,199,198,197,196,195,194,193,192,191, \
        190,189,188,187,186,185,184,183,182,181, \
        180,179,178,177,176,175,174,173,172,171, \
        170,169,168,167,166,165,164,163,162,161, \
        160,159,158,157,156,155,154,153,152,151, \
        150,149,148,147,146,145,144,143,142,141, \
        140,139,138,137,136,135,134,133,132,131, \
        130,129,128,127,126,125,124,123,122,121, \
        120,119,118,117,116,115,114,113,112,111, \
        110,109,108,107,106,105,104,103,102,101, \
        100,99,98,97,96,95,94,93,92,91, \
        90,89,88,87,86,85,84,83,82,81, \
        80,79,78,77,76,75,74,73,72,71, \
        70,69,68,67,66,65,64,63,62,61, \
        60,59,58,57,56,55,54,53,52,51, \
        50,49,48,47,46,45,44,43,42,41, \
        40,39,38,37,36,35,34,33,32,31, \
        30,29,28,27,26,25,24,23,22,21, \
        20,19,18,17,16,15,14,13,12,11, \
        10,9,8,7,6,5,4,3,2,1,



#include <type_traits>

#include <boost/mpl/size_t.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/facilities/is_empty.hpp>
#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/arithmetic/dec.hpp>

#define ARBITAL_SWITCH_DEFINE_EXPR_FUN(name_sig, body) \
    auto name_sig->decltype body \
    { return body; }

namespace arbital { namespace match {

namespace _call {

template<typename Tag, typename T>
ARBITAL_SWITCH_DEFINE_EXPR_FUN(
    query(T & v),
    (match::query<Tag, typename std::remove_cv<T>::type>::apply(v)))

template<typename T>
ARBITAL_SWITCH_DEFINE_EXPR_FUN(
    unwrap(T & v),
    (match::unwrap<typename std::remove_cv<T>::type>::apply(v)))

template<typename Tag, typename T>
ARBITAL_SWITCH_DEFINE_EXPR_FUN(
    unpack(T & v),
    (match::unpack<Tag, typename std::remove_cv<T>::type>::apply(v)))

template<size_t N, typename T>
ARBITAL_SWITCH_DEFINE_EXPR_FUN(
    unpack(T & v),
    (_call::unpack<boost::mpl::size_t<N> >(v)))

}

}}

#undef ARBITAL_SWITCH_DEFINE_EXPR_FUN

#define ARBITAL_SWITCH(...) \
    if (bool _switch_cond_ = true) \
        for (auto && _switch_var_ = (__VA_ARGS__); \
             _switch_cond_; \
             _switch_cond_ = false) \
            if (0) {}

#define ARBITAL_CASE(...) \
    else if (_switch_var_ == (__VA_ARGS__))

#define ARBITAL_DEFAULT else

#define ARBITAL_MATCH(...) \
    ARBITAL_MATCH_I((__VA_ARGS__))
#define ARBITAL_MATCH_I(tup) \
    ARBITAL_MATCH_II(BOOST_PP_DEC(ARBITAL_PP_TUPLE_SIZE(tup)), tup)
#define ARBITAL_MATCH_II(n, tup) \
    ARBITAL_MATCH_III(ARBITAL_PP_TUPLE_TAKE(n, tup), ARBITAL_PP_TUPLE_ELEM(n, tup))
#define ARBITAL_MATCH_III(tag, vars) \
    else if (auto && _query_var_ = ::arbital::match::_call::query<ARBITAL_PP_TUPLE_REM tag>(_switch_var_)) \
        for (auto && _match_var_ = ::arbital::match::_call::unwrap(_query_var_); \
             _switch_cond_; \
             _switch_cond_ = false) \
            ARBITAL_SWITCH_UNPACK_TUPLE(_match_var_, vars)

#define ARBITAL_SWITCH_UNPACK_TUPLE(packed, vars) \
    BOOST_PP_REPEAT(ARBITAL_PP_TUPLE_SIZE(vars), ARBITAL_SWITCH_UNPACK_TUPLE_M, (packed, vars))

#define ARBITAL_SWITCH_UNPACK_TUPLE_M(z, n, data) \
    ARBITAL_SWITCH_UNPACK_TUPLE_M_I(n, ARBITAL_PP_TUPLE_REM data)
#define ARBITAL_SWITCH_UNPACK_TUPLE_M_I(n, x) \
    ARBITAL_SWITCH_UNPACK_TUPLE_M_II(n, x)
#define ARBITAL_SWITCH_UNPACK_TUPLE_M_II(n, packed, vars) \
    BOOST_PP_IIF(BOOST_PP_IS_EMPTY(ARBITAL_PP_TUPLE_ELEM(n, vars)), \
        ARBITAL_PP_TUPLE_EAT, \
        ARBITAL_SWITCH_UNPACK_TUPLE_M_III) \
    (n, packed, ARBITAL_PP_TUPLE_ELEM(n, vars))
#define ARBITAL_SWITCH_UNPACK_TUPLE_M_III(n, packed, var) \
    for (auto & var = ::arbital::match::_call::unpack<n>(packed); _switch_cond_; _switch_cond_ = false)



#include <iostream>
#include <string>

#include <boost/variant/variant.hpp>
#include <boost/fusion/container/vector/vector.hpp>
#include <boost/mpl/print.hpp>

void print() { std::cout << '\n'; }

template<typename T, typename ...U>
void print(T && x, U && ...xs) {
    std::cout << std::forward<T>(x);
    ::print(std::forward<U>(xs)...);
}

int main() {
    char x = 'x';
    ARBITAL_SWITCH (x)
    ARBITAL_CASE ('a') {
        print("'a'");
    } ARBITAL_CASE ('b') print("'b'");
    ARBITAL_CASE (x) {
        print("x");
    } ARBITAL_DEFAULT {
        print("other");
    }

    namespace bst = ::boost;
    namespace fu = bst::fusion;

    boost::variant<
        int,
        fu::vector<int, std::string> >
    v = fu::vector<int, std::string>(1, "hogehoge");

    ARBITAL_SWITCH (v)
    ARBITAL_MATCH (int, (n)) {
        print("int: n = ", n);
    }
    ARBITAL_MATCH (fu::vector<int, std::string>, (n, str)) {
        print("(int, string) : n = ", n, ", str = ", str);
    }
}

したいかどうかは別として、caseとmatchは混ぜて使うことができます。

2011/01/04(火) レンゲの刃

プリプロセス時足し算・引き算・かけ算・剰余演算を定数時間で

http://ideone.com/eK4vc

#include <boost/preprocessor/cat.hpp>
 
#define PP_ADD_C(x, y) PP_NAT_TO_LIT(PP_NAT(x) PP_NAT(y))
 
#define PP_SUB_C(x, y) PP_COMPL(PP_ADD_C(PP_COMPL(x), y))
 
#define PP_MUL_C(x, y) PP_NAT_TO_LIT(PP_CST_REPEAT(x, PP_NAT(y)))
 
#define PP_MOD_C(x, y) \
    PP_CST_ELEM_16(PP_NAT(x) PP_MOD_MAKE_MOD_TABLE(y))
 
/**
 * m-1,m-2...,0の繰り返しを作って、CST_ELEM_16で0が来るように先頭に16%m+1個要素を追加
 *
 * PP_CST_DROP_16(PP_NAT(PP_SUB_C(m,1)) NAT_TABLE)   必要なオフセット
 * の繰り返しのF番目まで
 * 2,1,0,2,1,0,2,1,0,2,1,0,2,1,0,2,1,                16%3 + 1
 * 3,2,1,0,3,2,1,0,3,2,1,0,3,2,1,0,3,                16%4 + 1
 * 4,3,2,1,0,4,3,2,1,0,4,3,2,1,0,4,3,                16%5 + 1
 * 5,4,3,2,1,0,5,4,3,2,1,0,5,4,3,2,1,                16%6 + 1
 * 6,5,4,3,2,1,0,6,5,4,3,2,1,0,6,5,4,                16%7 + 1
 * 7,6,5,4,3,2,1,0,7,6,5,4,3,2,1,0,7,                16%8 + 1
*/
#define PP_MOD_MAKE_MOD_TABLE(m) \
    PP_CST_DROP_16(PP_NAT(PP_REM_OF_DIV_16(m)) NAT_TABLE) \
    PP_CST_REPEAT(16, PP_CST_DROP_16(PP_NAT(PP_SUB_C(m,1)) NAT_TABLE))
 
#define PP_COMPL(n) PP_NAT_TO_COMPL(PP_NAT(n))
 
#define PP_NAT_TO_LIT(...) PP_CST_ELEM_16(__VA_ARGS__ NAT_TABLE)
#define PP_NAT_TO_COMPL(...) PP_CST_ELEM_16(__VA_ARGS__ COMPL_TABLE)
 
#define PP_REM_OF_DIV_16(x) PP_CST_ELEM_16(PP_NAT(x) REM_TABLE)
 
#define NAT_TABLE 16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,
#define COMPL_TABLE 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
#define REM_TABLE 0,1,2,3,4,5,6,7,0,2,4,1,0,1,0,0,ERROR,
 
#define PP_NAT(n) PP_NAT_I(n)
#define PP_NAT_I(n) PP_NAT_ ## n
 
#define PP_NAT_0 
#define PP_NAT_1 ,
#define PP_NAT_2 ,,
#define PP_NAT_3 ,,,
#define PP_NAT_4 ,,,,
#define PP_NAT_5 ,,,,,
#define PP_NAT_6 ,,,,,,
#define PP_NAT_7 ,,,,,,,
#define PP_NAT_8 ,,,,,,,,
#define PP_NAT_9 ,,,,,,,,,
#define PP_NAT_10 ,,,,,,,,,,
#define PP_NAT_11 ,,,,,,,,,,,
#define PP_NAT_12 ,,,,,,,,,,,,
#define PP_NAT_13 ,,,,,,,,,,,,,
#define PP_NAT_14 ,,,,,,,,,,,,,,
#define PP_NAT_15 ,,,,,,,,,,,,,,,
#define PP_NAT_16 ,,,,,,,,,,,,,,,,
 
// tuple operations
#define PP_TUPLE_REM(...) __VA_ARGS__
 
// comma-separated-tokens operations
#define PP_CST_ELEM_16(...) PP_CST_HEAD(PP_CST_DROP_16(__VA_ARGS__))
 
#define PP_CST_HEAD(...) PP_CST_HEAD_I(__VA_ARGS__)
#define PP_CST_HEAD_I(_0,...) _0
 
#define PP_CST_DROP_16(...) PP_CST_DROP_16_I(__VA_ARGS__)
#define PP_CST_DROP_16_I(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_a,_b,_c,_d,_e,_f,...) __VA_ARGS__
 
#define PP_CST_REPEAT(m, ...) PP_CST_REPEAT_I(m, (__VA_ARGS__))
#define PP_CST_REPEAT_I(m, x) PP_CST_REPEAT_ ## m(PP_TUPLE_REM x)
// PP_CST_REPEAT_n で __VA_ARGS__ を繰り返すのがめんどくさいので
// 1トークン列として認識させる
 
#define PP_CST_REPEAT_0(x)
#define PP_CST_REPEAT_1(x) x 
#define PP_CST_REPEAT_2(x) x x
#define PP_CST_REPEAT_3(x) x x x
#define PP_CST_REPEAT_4(x) x x x x
#define PP_CST_REPEAT_5(x) x x x x x
#define PP_CST_REPEAT_6(x) x x x x x x
#define PP_CST_REPEAT_7(x) x x x x x x x
#define PP_CST_REPEAT_8(x) x x x x x x x x
#define PP_CST_REPEAT_9(x) x x x x x x x x x
#define PP_CST_REPEAT_10(x) x x x x x x x x x x
#define PP_CST_REPEAT_11(x) x x x x x x x x x x x
#define PP_CST_REPEAT_12(x) x x x x x x x x x x x x
#define PP_CST_REPEAT_13(x) x x x x x x x x x x x x x
#define PP_CST_REPEAT_14(x) x x x x x x x x x x x x x x
#define PP_CST_REPEAT_15(x) x x x x x x x x x x x x x x x
#define PP_CST_REPEAT_16(x) x x x x x x x x x x x x x x x x
 
 
#include <iostream>
 
int main() {
    std::cout << PP_ADD_C(PP_MUL_C(1,3),5) << '\n';
    std::cout << PP_SUB_C(PP_ADD_C(7,4),6) << '\n';
    std::cout << PP_MUL_C(3,PP_MOD_C(14,6)) << '\n';
}

ループを回さない、テーブルを大きくしすぎない、あたりを目標に作ってみました。割り算は無理でした。割り算難しい。

まぁここまでやるならテーブルを二段構えに、つまり16のカンマ区切りテーブルを16個カンマで区切って九九表ならぬ十六十六表作ってもいいのかも知れませんが。

2010/12/30(木) パラボナアンテナ in the dark

fontconfigの設定(1)

fontconfigの設定ぐらい手打ちでできないと人間として認められないそうなので、がんばって調べてみました。しかし折角人間と認められても、数ヶ月後には再び人間でない何かになってしまいそうなので、未来の自分も人間と認められ続けるべく、ここに記録を残しておきます。nヶ月後の私も、どうかこれを見て人間を続けられるように頑張ってください。もっとも調べたと言っても9割方はmanの情報です。

最初にfontconfigの基本的な事項を解説し、それを元にフォントを決定するまでの大まかな流れを示します。その後、設定ファイル(XML)の各要素について、擬似的なプログラミング言語を用いた処理を交えながら解説します。

かなり内容が多くなってしまいましたので、今回のこの記事では、基本的な事項について解説します。

フォント

このシリーズの記事で言う「フォント」とは、「字形情報のセット」という意味です。「フォント名」「フォントの名前」は「字形情報のセットに付けられた名前」です。「フォントファイル」は「字形情報のセットが格納されているファイル」です。一応本文中に出てくるこれらの言葉は、ここに挙げた意味通りに使っているつもりですが、もしかしたらそうでないこともあるかも知れません。ごめんなさい。

font name

"name" とは言いますが、これは「IPA ゴシック-8:bold:antialias=true」のように、フォントの名前以外に大きさや太さその他のオプションまで含めることができます(含めなくてもいいです)。Xftに対応しているアプリケーションなら、.Xresourcesなどでフォントや大きさやその他もろもろを指定するとき、この書式で指定できます。font nameに含まれている、フォントの名前、大きさその他の色々な項目は、プロパティといいます。一般的に表わすと「<family>-<size>:<propety1>=<value11>,<value12>…:<property2>=<value21>,<value22>…:…」という感じです。フォント名も、familyというプロパティです。いくつかのプロパティのいくつかの値については省略記法が用意されていて、「bold」や「weight=bold」は、「weight=200」と同じ意味になります。

一つのプロパティに対して複数の値を指定できます。指定した順に優先度が高い要求となります。要するに本命要求以外の代替案を付けられるということです。font nameで表わすと「IPA ゴシック-9:width=thin,normal,bold」と書きます。この場合、thinがいいけどなかったらnormalでもいいよ、それもなかったらboldで、みたいな感じです。familyも複数指定できて、「IPA ゴシック-9:family=M+2P+IPAG,sans-serif」とすればIPA ゴシックがなければM+2P+IPAGが、それもなければsans-serifが選ばれます。sizeも同様にIPAG ゴシック-9:size=12,10,15みたいな指定ができます。別に連続した数値である必要はないですし、昇順、降順になっている必要もありません。自分が優先したい順に並べてください。

先にでてきた例の"sans-serif"というのを見れば分かるように、このfamily名というのは特定のフォント名であるとは限らないです。fontconfigの設定ファイルで、いくつかのフォント名をセットにして一つの名前を付けることができます。sans-serifはもちろんゴシック体フォントの集合に対する名前で、要するに、「とにかく何かゴシック体のやつで」と言っているわけです。

pattern

font nameの抽象表現がpatternです。というか、patternを文字列で表現したものがfont nameです。

システムにインストールされているフォントも全てpatternで表現することができます。ここでちょっとポイントですが、例えばTimesがインストールされているなら、8ptで普通の太さ傾きなしで名前がTimesというpatternと、8ptで太字傾きなしで(略)と、8ptで普通の太さ斜体で(略)と、8ptで太字斜体で(略)と、9ptで(略)と、…というpatternが全てインストールされている、という考えかたをします。もちろんプロパティはここに挙げた以外にもいっぱいあるので、それら全ての組み合わせ分だけpatternがあることになります。fontconfigは、このインストールされたpatternの中から、要求されたpatternに最も近いものを選びます。

fontconfig設定ファイルの概要

fontconfigの設定ファイルは、定められたDTDに従っている妥当なXML文書である必要があります。正直言ってXMLという時点で人間が読み書きするには不便なフォーマットですが、そこに文句を言っても始まらないのでやめておきます。このDTDを記述しているファイルは、システムのどこかに置いてあるでしょう。ArchLinuxの場合は/etc/fonts.d/fonts.dtdがそれでした。fonts.dtdはコメントで注釈を付けてくれてますので、これも書き方のヒントになるかと思います。

設定ファイルはいくつかに分割することができます。その場合も、一つ一つのファイルは妥当なXML文書でないといけません。デフォルトの状態で大本の設定ファイルである/etc/fonts.d/fonts.confに、~/.font.confと.font.conf.dを読みこむように書いてあるので、それらのファイルに設定を書く、またはディレクトリ下に設定ファイルを置くなどすれば、読まれます。

fontconfigがフォントを決定する流れ

fontconfigは、所望するpatternを受け取り、そのpatternを各<match target="pattern">要素の内容に従って修正します。

それから、修正したpatternに一番近いpatternを、システム内で見つかる全patternから選びます。この「近さ」を測定するためのルールについては、そんなに気にしなくても多分大丈夫です。詳しくはmanでも読んでください。

最後に、選ばれたpatternを、今度は各<match target="font">要素の内容に従って修正します。こうしてできたpatternが示すフォントを、最終的なフォントとして返します。

2010/12/17(金) 衝動に駆られても決して自ら買わない精神

Unicode, UCS-2/4, UTF-8/16/32についての整理

※注意:これは私が調べた結果をまとめたものなので、間違いがあれば指摘していただけるとありがたいです。

用語

ここで整理したいことについて話せるようにするための説明なので、正確ではないです(符号化文字集合とか符号点とか)。

文字集合
文字の集合
符号化文字集合
文字と負ではない整数(または整数列)の対応表。その整数が、コンピュータで扱うビットの並びそのものとは限らない。コンピュータでは、この整数を符号化方式によって変換したものを扱う。
符号点、符号位置、コードポイント
符号化文字集合で、文字に対応している整数(または整数列)のこと。「符号位置」はUnicodeでの呼び方。
符号化方式
符号点を、コンピュータで扱うビット列で表現するための変換方式。よく「エンコーディング」と呼ばれるものはこちら。符号化符号化うるさいので、ここでは「エンコーディング」ということにする。

※「符号化文字集合」と「符号化方式」が指す「符号化」の意味は多分違う。前者のそれは文字に番号(符号)を振ったことを言い、後者は符号をコンピュータが扱うためのビット列に変換することを言うと思う。

UnicodeとUCS

Unicodeとは符号化文字集合の名前であり、UCSは符号化文字集合の規格である。UCSでは、UCS-4とそのサブセットであるUCS-2の2種類の符号化文字集合が定められている。

Unicodeはもともと16ビットで表現した符号空間に文字を割り当てていたが、足りなくなったので後に21ビットに拡張した。

UCSは、31ビットで表現される符号化文字集合で、Unicodeとは別に作られていたが、最終的に当時16ビット空間であったUnicodeをまるまる取り込むことになった(このため下記のような互換性が発生している)。この16ビットの部分が、そのままUCS-2である。

コードポイントの表記法

Unicodeのうち、16ビットで表現できるコードポイント、およびUCS-2ではU+XXXXと表記する。Xは16進一桁。Unicodeにおいて21ビットに拡張された部分にあるコードポイントついては、適宜5, 6桁で表記する。また、UCS-4では8桁、U+XXXXXXXXと表記する。

UTF-8, UTF-16, UTF-32について

UCS, Unicodeともに、UTF-8, UTF-16というエンコーディングを定めている。つまり、UCSのいうUTF-8と、UnicodeのいうUTF-8は規格としては別物である(UTF-16についても然り)。

ただしどちらの規格のUTF-8/16も、変換の計算方法は同じで、U+0000〜U+FFFFの範囲では同じ配置になっているため、この範囲に限れば最終的なビット列にしても違いはない。(U+10000より後がどうなのかは知らない)。

Unicodeには更に、UTF-32というコードポイントをそのままコンピュータで扱うバイト列とするエンコーディングが定義されている。UCSには存在しないが、UCS-4は、そのままエンコーディングでもあるので、実質UTF-32とは同等である。

UCS-2もまたそのままエンコーディングでもあるが、こちらはサロゲートペアについての規定がないので、UTF-16とは違ってU+10000以降の文字は扱えない。

個々のエンコーディングの具体的な計算方法は省略。

要約すると、

  • Unicode, UCS双方のUTF-8/16は、エンコーディングでの計算方法は同じ
  • Unicode, UCS双方のUTF-8/16は、U+0000〜U+FFFFにおいてはUTF-8/16でエンコードしたあとのバイト列は同じ。
  • UnicodeにはUTF-32があり、UCSにはエンコーディングとしてのUCS-4があり、両者が実質同じ計算方法であり、やはりU+0000〜U+FFFFまではエンコード後のバイト列も同じになる

ここには書いていないこと

  • UTF-8/16が具体的にどのようにして符号をバイト列に変換しているのか
  • UnicodeのUTF-16/32にはバイトオーダーの話
  • 符号点と群・面・区・点、BMP
  • いくつかのコードポイントからなる合成文字について

分からないこと

  • UCS-4での文字の割り当て方と、Unicodeのそれで、異なる点はあるのか?(追記現時点で最新の「Wikipedia 異体字セレクタ」の項によると、UCSとUnicodeは同期を取ることになっているので、結局両者の同じコードポイントに違う文字が割り当てられることはない)
  • UCSのUTF-8/16とエンコーディングとしてのUCS-2, UCS-4のバイトオーダーについての規定は?

参考

2010/12/14(火) 2010年12月14日の日記

vlcをCUIから使う

みなさんごきげんよう。今日も元気にラブライブ!の1stシングル「僕らのLIVE 君とのLIFE」を聞きましょう。

…あぁ、いつもいつもCD挿すのめんどくさいですね。ここはちょいとvorbisあたりに変換してハードディスクに保存してしまいましょう。誰ですかiPodとかで聞いてるのは?そんなものは知らない。見えない。分からない。

さて、手元にvlcがあります。こいつを使って収録されている曲及び各員からのコメントを変換して保存するには、トラックごとにダイアログから設定して、エンコードしてやらないといけません。おいぃ13トラックもあるぞ!手でやってられへん!!!

これはめんどくさい、一気に変換する方法はないものか…そうだ、vlcってCUIから使えねーの?と思ってvlc --helpとか打ってみたらやたらと長い説明が出てきて、それだけでは飽き足らず「網羅的なヘルプを表示するためには、'-H'オプションを指定してください。」とか言わはる。これはやばい。相当にやばい。しかし私はラブライブ!1stシングルを快適に聞くために頑張ってvlcでバッチエンコードするためのマンドを探しまくりました。ありました。やったー

というわけで、私の備忘録、兼、今日もどこかでvlcのプログラムオプションに困っている人達のために残しておきます。

サンプル

cvlc --sout-vorbis-quality=4 \
  --sout "#transcode{acodec=vorb,channels=2,samplerate=44100}:std{access=file,mux=ogg}" \
  cdda:///dev/cdrom :cdda-track=2 :sout-standard-dst=友情ノーチェンジ.ogg \
  cdda:///dev/cdrom :cdda-track=5 :sout-standard-dst=はじめまして-高坂穂乃果-.ogg

これは以下と同じです(理由は後述):

cvlc --sout-vorbis-quality=4 \
  --sout-transcode-acodec=vorb --sout-transcode-channels=2 \
  --sout-transcode-samplerate=44100 --sout-standard-access=file --sout-standard-mux=ogg \
  cdda:///dev/cdrom :cdda-track=2 :sout-standard-dst=友情ノーチェンジ.ogg \
  cdda:///dev/cdrom :cdda-track=5 :sout-standard-dst=はじめまして-高坂穂乃果-.ogg

Overview

cvlc [オプション] [[MRL] [オプション…]]…

vlcは、多くのオプションに従ってMRLごとにデータを再生(スピーカーやモニターに出力)、ストリーム出力(webに出力)、ローカルに保存(ファイルに出力)、その他何かします。

cvlcとは、GUI剥ぎ取ったvlcです。なので、vlcに同じコマンドを渡すこともできますし、ちゃんと動きます。ただしバッチ中にウィンドウ出てきてもうざいだけですが。

MRL

MRLは、入力データの場所です。ローカルファイル以外にも、web上のファイルやストリームとかでもいいです。

MRLはURLに何か色々付け足したものですが、その「何か」の部分は知りませんので知りません。URLは以下のような書式です(vlc --helpから抜粋)。

URL syntax:
  [file://]filename              Plain media file
  http://ip:port/file            HTTP URL
  ftp://ip:port/file             FTP URL
  mms://ip:port/file             MMS URL
  screen://                      Screen capture
  [dvd://][device][@raw_device]  DVD device
  [vcd://][device]               VCD device
  [cdda://][device]              Audio CD device
  udp://[[<source address>]@[<bind address>][:<bind port>]]
                                 UDP stream sent by a streaming server
  vlc://pause:<seconds>          Special item to pause the playlist for a certain time
  vlc://quit                     Special item to quit VLC

例えば、 /dev/cdrom に挿さっているオーディオCDなら、 cdda:///dev/cdrom です。

screen:// は多分、ディスプレイ出力を入力にするためのURLだと思います。ゲームのプレイ動画とか作れるかもしれませんね。

vlc://quit を見つけるとvlcは終了します。つまり、これを付けないとバッチ処理後もvlcは起動したままになります。

オプション

オプションは、グローバルなものと、MRLごとに指定するための記法の二つがあります。

--optionと書くと、全てのMRLに対するオプションになり、:optionと書くと、直前のMRLに対するオプションとなります。:optionは--optionより優先されます(この説明が完全に正しいわけじゃないっぽいです)。

また、--sout-*系のオプションは、

--sout "#module1{option1=value,option2=value…}:module2{option1=value,option2=value…}…"

という風に書くことができ、これは

--sout-module1-option1=value --sout-module1-option2=value … --sout-module2-option1=value …

と同じです。

#module1{options}:module2{options}… の形式が使えるmoduleは、 transcode, standard, duplicate, rtp と、他あったような気がするけど忘れました。

個々のオプション項目

サンプルをもう一度記載します。

cvlc --sout-vorbis-quality=4 \
  --sout "#transcode{acodec=vorb,channels=2,samplerate=44100}:std{access=file,mux=ogg}" \
  cdda:///dev/cdrom :cdda-track=2 :sout-standard-dst=友情ノーチェンジ.ogg \
  cdda:///dev/cdrom :cdda-track=5 :sout-standard-dst=はじめまして-高坂穂乃果-.ogg
  • --sout-vorbis-*: Vorbisコーデックのオプション。デフォルトではVBRエンコードになっている
    • --sout-vorbis-quality=1..10: エンコードの品質(デフォルトは多分5)
  • --sout-transcode-*: 入力を変換する系のオプション
    • --sout-transcode-acodec=(vorb|mpga|a52|ac3): 音声コーデックの指定
      • vorb: Vorbis
      • mpga: MPEG audio layer 2
      • a52, ac3: AC3 sound
    • --sout-transcode-ab=kbps: 音声ビットレート(kbps)(コーデックにVorbis使う場合は無視される)
    • --sout-transcode-channels=number: チャンネル数(ステレオなら2)
    • --sout-transcode-samplerate=Hz: サンプリングレート(Hz, オーディオCDなら多分44.1kHzだから44100)
  • --sout-standard-*: 出力先に関するオプション
    • --sout-standard-access=(file|udp|rtp|http): アクセス先の指定
      • file: 普通のファイル
      • udp, rtp, http: それらのプロトコルで垂れ流す(調べてないから知らない)
    • --sout-standard-mux=(avi|ogg|ps|ts): コンテナの指定
      • avi: AVI
      • ogg: OGG
      • ps: MPEG2-PS
      • ts: MPEG2-TS
    • --sout-standard-dst=url: 出力先の場所。--sout-standard-accessがfileの場合は普通にファイルのパスを指定するか、もしくはfile://<path>の形式で指定。その他のプロトコルの場合は何かURL(調べてないから知らない)

standardには、stdという別名があるので、#module1{}:module2{}という書き方をする場合はそのstdという別名が使えます。

--sout-transcode-*には、もちろん映像関連のオプションもあります。 --sout-transcoode-vcodec=video_codec とか。

この他のmoduleも、全然使ってないけど憶測で適当に紹介します。

  • --sout-duplicate-*: 出力を分岐する
    • --sout-duplicate-dst=#module1{options…}:module2{options…} : オプションの内容に従って出力する。dstを何個も書いてやることで、出力先が増える
  • --sout-rtp-*: 指定するURLにRTPで出力

これらのオプションについて、詳細はvlc -Hや、

あたりを見てください。ちなみに上のURLのリストは多分内容が偏ってると思うので、もっと色々なことをしたい人は、勝手にwikiの中を漁ってください。

そんなわけでvlcをコマンドラインから使う方法を紹介してきましたが、ぶっちゃけMRLとオプションをずらずら並べなくても、シェルのfor文使ってもいいですね。

あとなんか、--optionと:optionの関係はそんな単純なものじゃないっぽくて、まぁうまいことやってください。

明日から使える boost/preprocessor/facilities/*

やったーC++ Advent Calendarすっかり忘れていました。

今日は数あるBoost.PPのマクロのうち今一つ何がしたいのか分からない連中にスポットを当ててみたいと思います。

BOOST_PP_IS_EMPTY

例えばコンパイルフラグとしてHOGEが定義されていたとします。gcc -DHOGEとか、#define HOGEとかそういう感じで。その時に、ある関数で何か特別なことをするには:

void f() {
…
#if defined HOGE
何か特別なこと
#endif
…
}

普通はこうですが、ワンライナーでやりましょう

#include <boost/preprocessor/control/expr_iif.hpp>
#include <boost/preprocessor/facilities/is_empty.hpp>

void f() {
…
BOOST_PP_EXPR_IIF(BOOST_PP_IS_EMPTY(HOGE), 何か特別なこと)
…
}

HOGEは、-DHOGEとあれば空に展開されるはずなので、BOOST_PP_IS_EMPTY(HOGE)は1になります。HOGEの定義がなければHOGEは展開されず、IS_EMPTYは0を返します。

BOOST_PP_IS_1

先と同じく、今度はHOGEが1と定義されていた場合:

void f() {
…
#if HOGE == 1
何か特別なこと
#endif
…
}

これは、

#include <boost/preprocessor/control/expr_iif.hpp>
#include <boost/preprocessor/facilities/is_1.hpp>

void f() {
…
BOOST_PP_EXPR_IIF(BOOST_PP_IS_1(HOGE), 何か特別なこと)
…
}

HOGEが1になるのであれば、何か特別なことがコードとして展開されます。

BOOST_PP_IS_EMPTY_OR_1

-DHOGEでも-DHOGE=1でもどちらでもOKとする場合:

#include <boost/preprocessor/control/expr_iif.hpp>
#include <boost/preprocessor/facilities/is_empty_or_1.hpp>

void f() {
…
BOOST_PP_EXPR_IIF(BOOST_PP_IS_EMPTY_OR_1(HOGE), 何か特別なこと)
…
}

BOOST_PP_INTERCEPT

struct hoge0 {}; struct hoge1 {}; … というのを量産したいとします。これはhogeNの後ろに{};があるので、BOOST_PP_ENUM_PARAMSではできませんね。そこでBOOST_PP_ENUM_BINARY_PARAMSを使います。

#include <boost/preprocessor/facilities/intercept.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>

BOOST_PP_ENUM_BINARY_PARAMS(10, struct hoge, {}; BOOST_PP_INTERCEPT)

BOOST_PP_ENUM_BINARY_PARAMSはA1 a1, A2 a2, みたいなのを作るときによく使いますが、2番目のパラメータには数字いらんのじゃ、というときにBOOST_PP_INTERCEPTを入れてやると数字を消せます。

BINARYじゃないENUM系のマクロでも使えるので、

#include <boost/preprocessor/facilities/intercept.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
BOOST_PP_ENUM_PARAMS(8, YO BOOST_PP_INTERCEPT)! Check it out!

とすればYO , YO , YO , YO , YO , YO , YO , YO ! Check it out!になりますYO! Check it out YO!

BOOST_PP_EXPAND

BOOST_PP_EMPTY BOOST_PP_EMPTY BOOST_PP_EMPTY() () ()

これは、BOOST_PP_EMPTY BOOST_PP_EMPTY () () と展開されますが、ただこう書いただけではこれ以上展開されません。マクロの引数にしてやれば、もっと展開できます。そこで、

BOOST_PP_EXPAND(BOOST_PP_EMPTY BOOST_PP_EMPTY BOOST_PP_EMPTY() () ())

BOOST_PP_EXPANDを使って展開を促進できます。

BOOST_PP_IDENTITY

工事中

おわり

というわけ痒いところに手が届くfacilitiesディレクトリの中身を紹介しました。今まで微妙に回りくどいことをしていた人も、これで幸せになれるかな!なれないですね!

おまけ・クリスマスを無くす

なんかクリスマスを無くしてやるのが流行りらしいのでとりあえず置いときますね

#undef Christmas
#undef Xmas
#define Christmas
#define Xmas

2010/12/10(金) スーパー怠惰マン

最近流行りのアイドル(動画あり・音が鳴ります)

http://gs.dengeki.com/lovelive/

http://www.sunrise-inc.co.jp/lovelive/

http://www.lantis.jp/special/lovelive/

ラブライブ!です。

今のところキャラクターと1stシングルしかないのでアイドルならもっとアイドルっぽくグラビアとか欲しいところです。

School idol projectなんですから学校の話の漫画あってもええやろ!というか昨今のアイドルは漫画にもアイドルは漫画にもなりますしもっと燃料くださいといった感じなんですがまぁそんなことより花陽さん子供と目線一緒ってのはそれ多分褒めらてないんじゃないかなーと思います。あとことりさんはなんでそんなに人気ないのか分からないですね。

あとなんか色々書きたいところですがネタが足りない!!!

PVは力入ってるのに全然燃料がない大変アンバランスな状態なので、もっとみんなで文句言っていきましょう。

ラブライブ! 1stシングル「僕らのLIVE 君とのLIFE」 PV(ショートサイズ)

ラブライブ! 2ndシングル「Snow halation」/μ's PV(ショートサイズ)

※ちなみに現在のところ「LISP 漫画」でググると、まずLisp漫画がヒットします(というか普通これのことを言います)。

2010/12/05(日) ぺぺぺんぺんぴーん

メタプログラミングの会に行ってきたような気がするような気がしないこともない気がするんじゃないかと思わないこともない気がする

今回はもうめんどいのでtwitterの私の発言をまとめる作業で行きたいですね。

実は直前まで色々他の作業してて、夜行バス乗り場(大阪駅桜橋口)まで行く時間がさし迫って大変やばい感じになってました。

駅に着いてホームに上がると「環状 遅れ約8分」とか案内が出てて、次の電車は4駅手前のアナウンスがあってどう考えてもやばい。というわけでJRの改札を出て地下鉄に行ったわけです。

ここに至ってようやく「中央」ドリーム号の意味を理解する。あのルートでもそこまで所要時間延びないのね。

新宿着きました。ここから新宿をうろつきます。

なぜそれを思い出したのかと言うと、うろついていると目に入ったのです。

なんかモンハンブースがあって、その前にハンターが群がっていました。お前らのほうがよっぽどモンスター。

地下でハトを見ました。どうやって入ってきたのか気になります。

変態タワー3号を見てから、しばらく適当に歩いてました。歌舞伎町行きました。傾いてました。あそこはなんか本当に宗右衛門町と同じ空気です。ところで朝マックは本当にケチくさいサイズですね。まぁバーガーはもともとケチくさいけど。

しばらくマクドで本を読んだり、ボーっと新宿駅に出入りする電車を眺めてました。なんとなく難波周辺にいるような気分でしたが、難波は南海以外の駅が全て地下にあるので電車は全く見えないですね。

また新宿をクネクネしていたわけですが、ここでふとそんな気分になりました。

そしてなぜか電車使わずに40分弱、靖国通りを歩いてました。細かい坂道とか本当に何も考えずに引いた道に道を重ねてばっかりでやばいですね。特に曙橋のあたりとかあの地形何がしたいのか分からない。サイクリングする分には飽きなさそうでいいですが。

しんどいというのと、あと2時間ほどでメタプロ会が始まるし、買うもの買う→昼食→会場まで移動の時間を考えると歩いていたのでは到底間に合いそうにないので中央線に乗ることに。

快速という感じのスピードじゃないですね。130km/hぐらい出してほしい。

結局秋葉原に来るわけです。ちゃんと恋チョコを買いました。昼食はラーメンにしました。

Boost.勉強会#1と同じビルだったので今度は迷わず行けました。

一番面白かったのはMetaOCamlかなー。あとハッシュタグの設定ミスってしまいました。

懇親会ではBoost.勉強会#3で発表したアレをもう一回発表してきました。bindとかほしいねーという話をいただきましたので、気が向いたら実装します。ちなみに https://patch-tag.com/r/digitalghost/pplambda/home に置いてあります。実は今回Chaos-PPで発表ネタを考えようとしていたのですが、気が付いたら前日だったので無理でした。二次会ではなぜかミルホ鑑賞会が始まりました。本当に意味が分からない。どうしてこうなった。23時40分のバスに乗るため、23時10分には会場を出たかったのですが、最後に注文した酒がちょうど23時10分に出てくるというすばらしい演出のため、23時20分に出たわけです。走りましたよ。

バスに乗ったと思ったらすぐにサービスエリアでびっくりしました。というか乗ってすぐに寝てしまったようです。

足柄サービスエリアのファミマですが、コンビニの敷地外までレジの列が延びていました。ここで一本だけ残っていた生茶と一袋だけ残っていた普通のカラムーチョとファミチキを買いました。

しかし酒によっても車には酔わないのがこの私。

あの駅周辺から漂ってくる匂いは本当に腹へってるときにはたまらないです。匂いに殺される。

恋チョコです。

それがこれです。

s/起きてから/帰ってから/ 結局サービスエリアで買ったカラムーチョは家でRedBullといっしょにおいしくいただきました。

2010/10/25(月) Out of Geansay

Boost.勉強会#3で適当なことをしゃべってきました

あぁどうしよう。今回は大阪開催なので旅程がなくて書くことがないのであった。終わり。

などというだけではあんまりなので、まぁ千里中央の地形はやばいですね。前にも第一回GC勉強会のときに行きましたが、会場の建物に入るまでやっぱり迷いました。地上なのにぐぐる地図が役に立たないというすばらしい地形ですね。あと誰も御堂筋線が地下鉄だと信じてくれないのですが、一つ誤解があって、御堂筋線は新大阪以北だと江坂までで、そこから先は北大阪急行線であって地下鉄じゃないんです。よってみなさんはほとんど地下鉄には乗っていないわけです。西中島南方から先まで乗るべきです。

全発表はここから見れます。実況のまとめはいつもの【Twitter】ブー速zakブログで。私は気になった話の感想を書きます。私の発表の話の補足とかは後ほど別の記事として用意します。

エラーハンドリング

の話は、聞いててふと関数呼び出しの際に例外から戻り値に変換できたらなーとか思いましたが、これはもっとこの考えを推し進めて、エラーかもしれない戻り値(ぬるぽ・optional系)/例外/errno + get_last_error/errno + out引数その他様々なエラー処理は、送出する側/捕捉する側で別々の方法で扱えるべきなんですよ!errnoで通知したものをcatchで捕捉できたりしてもいいじゃないですか!

前に「エラーは捕捉する側が都合のいい手段で捕捉できるべきだ」とか書きましたが、常にエラーを捕捉する側が、送出する側のとったエラー通知の方法に合わせないといけないわけです。先に書きましたが、送出する側が例外なら捕捉はcatchでしかできないですし、ぬるぽなら if (!f()) ですし、errnoならget_last_errorなど。「なんでお前はthrowしてないんや」「おいぃoptional使えやボケが!」とかそういう戦争が日々繰り広げられていて、大量のエラーハンドリングラッパが世の中に量産されている。大変不毛です(不毛といってもすっぽすっぽ先生のことではないです)。

だからまぁ、送出側と捕捉側を分離できる仕組みがあればいいですねという話です。

Goodbye Doost!

Dなんとか言語のA先生はRange中二病?大丈夫なんでしょうか。あと懇親会会場の道すがらの話で、コミッタの中では日本人勢が一番マジキチなんじゃないかと思いました。大丈夫なんでしょうか。まぁ、俺のD言語がこんなにまともなわけがない。

Boost.Build

よーしパパビルドしちゃうぞーという気持ちですね。そんなに難しくなさそうでよかったです。

あとRypplと組み合わせて何かできないかな。まぁ私Ryppl全然調べてないですけど。

Boost.PropertyMap

前回のshared_ptrに引き続き、動機から話を積み上げていく大変に分かりやすいお話で非常に明解で分かりやすい(←これで使い方あってるかな)お話でした。まぁ私全然使う機会ないですけど。

二次会

懇親会は大変賑っていました。道化師さんredboltzさんkikairoyaさんは昔話に花を咲かせていました。ちょっと年齢層の違う話なので私はよく分からなかったです。

二次会ではalohakunさんが「みんなもっと残念な感じのキモオタだと思ってた。がっかりした。」「(アキラさんに)ほんとイケメンですよねー」「(めるぽんさんに)ほんと勝ち組ですよねー」「うちの会社は高齢化が激しいのに、社長に同年代だと思われている」などとずっと悲しくなるトークをしていました。そういえば一次会の席でもなつたんさんに説教していたという話を聞いてますね…。まぁそんななかでも私は期待通りの感じだったそうです。やったねたえちゃん!

そういえばHiNaさんって見あたらなk(ry

2010/10/12(火) カラッと揚げないから揚げ粉

有界の会の話

今回は節約して夜行バスで行きました(行く前に用事があったり飲んでたりしましたがその話は省略)。

裕福な私は東京行く手段といえばいつも新幹線で、夜行バスとかそんな安物の乗り物は初めてだったのでとりあえず8000円ぐらいの割とゆったりめのヤツにしてみました。そのチョイスは割と当たってて、あれより狭いと、触ってるとドロドロに融けるナマコみたいな感じで私は死にます(※ただし融けたナマコは放置していると再生します)。ただまぁイスが大変残念な感じで、とても腰が痛い。寝られないのです。椅子はかなり大きく倒せましたがどうもきまりが悪く、終いには俯せになってみたりもしてみた結果、ただ苦しいだけでした。あと枕。枕が必要です。

座席の上の棚に鞄が収まらなくて悲しいとか、そういうこともありました。ご存知の方も多いですが、私の鞄は別段大きいわけでもないですし、重いわけでもないです。どこにでもよくある普通の鞄です。荷物も大して入ってませんので、膨らんでなんかいませんし、なんで入らないのか大変疑問です。棚をもっと大きくしてください。

そんなわけで江戸のバスターミナルに着きました。これが朝の7時半です。会が始まるのが11時。3時間半をどうすればいいのでしょう。こういう時間はどこかに保管しておきたいので誰かそういうwebストレージサービスを作ってください。

ちょっと歩き回った挙句、早々に国鉄江戸駅構内のスタバで過ごしました。同じく時間を潰している人が多い中、カウンター席の二人の女性客の間に一つ空いている席を見つけ、居心地が悪いことを承知で居座りました。大変居心地が悪いですし、そもそもスタバとか私のような、ゴミに近い人間が行くところじゃないのでもっとそういう人間が過ごしやすい空間作りをしてください。

10時前になるとぼちぼち「そろそろ出よう」などのpostが増えてきましたので、私も向かうことにしました。また地下鉄です。本当にこの町は節操なくて、田舎を整備せずにコンクリートジャングルにしただけで悲しいですね。そもそも地下鉄なんて人間が利用することを想定したものじゃなくて、企業などの生体部品を運んだりするのが主目的なんだから仕方ないのでしょうが。human-frendlyな交通機関の登場が期待されます。

会場最寄り駅ギロッポンからはカーチーローツウと、カーチーからのターレーカーエスでシューゴーバーショのルービーまでツーチョクでした。1階ロビーに着いた私は、開始まで30分ほど時間があるのでRedBullを買いにビル内4階にあるローソンに行くべく、適当なエスカレータを探しはじめました。

探していると、「ECO NAVI CAFE」というスペースがあって、ガラス張りの部屋の中では大量の照明をギンギラギンに焚いている様子が見受けられました。大変ECOいですね。

さて、エスカレータを見つけたので乗りました。すると着いたのは3階でした???2階はどこに行ったのですか???人間さんに優しくなさすぎませんか?????あとエスカレータは、一階ごとに次の階に行くためのエスカレータを探さないといけないオサレ系のつくりになっていて、ちょっと君この作りはオサレ系頭悪い作りですねって思いました。

それからとりあえずローソンに着いてRedBullを買いました。ローソンの近くにエレベータがあったので、それで一階まで降りました。すると車用のエントランスに出ました???私がいた一階はどこに行ったのですか???

適当に入口を探して入ると、同会参加者であるアキラさんから声をかけられました。どうやらここが一階のようです。だったらさっき一階だと思っていたのは一体どこなんですか???

実は最初に着いた場所は2階のロビーだったらしく、つまり私が地下から上がるために乗ったエスカレータは、なんと2階に通じていたのでした。帰りに確認すると、エスカレータからちゃんと到達不能の一階部分が見えました。ちょっと本当にもうこのビル頭おかしいの本格派ですね。

話を戻して、アキラさんと一緒にいた方はpepshisoさんでした。どうやらみんな正しい待ち合わせの場所が分からないようです。困りました。

ところでこれ以上集合するまでの話を書くと森タワーの悪口しか出てこないので、次行きましょう。そういえばこんさんはそのへんのビルをワープしまくっていたようですが、ズーヒルにはそういう装置もあるんですね。もうダンジョンですね。

会場は新しいGREEのオフィスということだったのですが、ビルのセキュリティが尋常じゃなくて、しょうむないアホ面をした一般人はビルのエレベータすら動かせない素敵仕様でした。会の内容はあんなに適当なのに!

さて肝心の会の内容ですが、私はだいたい自分の席でプリプロセッサをいじってましたので、面白い話は他の参加者の日記とか見ればいいんじゃないでしょうか?まぁいつものことですね。

会の後は二次会です。ビル内の店は満席とかそんな感じで無理っぽかったので秋葉原に行くことになりました。六本木から秋葉原なので、メトロ日比谷線に乗るわけです。皆様の愛あるwait-free改札によって切符を買っている間に見事六本木駅に放置された私がそこにいました。どうやら江戸の若者は電車の料金を調べるのが面倒臭いのでSuicaなんだそうです。江戸は怖いですね。

さて、一人あとから秋葉原に着き、アキラさんに迎えに来ていただいて(あの時はお手数おかけしました)店の前で待っていた皆さんと合流を果たして二次会です。煮魚とか焼き魚おいしかったです。あと秋刀魚の刺身でしたっけ、あれも脂乗ってておいしかったです。食べ物以外の話としては、まぁC++の話とか、メタプログラミングの話とかしました。

私の帰りの夜行バスの時間が迫ってきたので、そろそろ失礼するついでに全員解散ということになりました。帰りも夜行バスで、行きのバスよりは寝心地が良かったです。まぁ、寝心地とか関係なくなるほど疲れてたという可能性はありますが。

ところでこの日記を見返してみると内容の2/3が始まるまでの話でお前は一体何をしに行ったんだ。

2010/09/27(月) 破滅するのはお前だけで十分

Boostのgit mirrorを見つけました

http://repo.or.cz/w/boost.git

svn upとか遅すぎてやってらんねーよボケがという人へ。 git pullで光の速度!!!

2010/09/21(火) 市場の借り物競争が激化している

新型scope guard

以前にも何回か書いているスコープガードネタですが(これこれこれ)、http://d.hatena.ne.jp/faith_and_brave/20100921/1285049653 とか http://d.hatena.ne.jp/gintenlabo/20100921/1285084859 から、なんかtwitterで話のネタになってたので、ちゃんとC++0xで使えるカッコイイスコープガード作りましたよ!

#include <iostream>
#include <functional>
#include <utility>

#define PP_CAT(a, b) PP_CAT_I(a, b)
#define PP_CAT_I(a, b) a ## b

template<typename F>
struct scope_exit_t {
    scope_exit_t(F & f) : f(f) {}
    ~scope_exit_t() { std::cout << "by lvalue: "; f(); }
private:
    F & f;
};

template<typename F>
struct scope_exit_t<F&&> {
    scope_exit_t(F && f) : f(f) {}
    ~scope_exit_t() { std::cout << "by rvalue: "; f(); }
private:
    F f;
};

struct scope_exit_helper {
    template<typename F>
    scope_exit_t<F&&> operator->*(F && f) const {
        return scope_exit_t<F&&>(std::forward<F>(f));
    }
};

#define scope_exit_base auto PP_CAT(scope_exit_, __LINE__) = scope_exit_helper() ->*
#define scope_exit scope_exit_base [&] ()
#define scope_exit_2 scope_exit_base

struct fun_obj {
    void operator()() { std::cout << "fugafuga\n"; }
};

void f() {
    std::cout << "私はカモメ\n";
}

void g(int x) {
    std::cout << x << "\n";
}

int main() {
    scope_exit { std::cout << "hogehoge\n"; };
    scope_exit { std::cout << "piyopiyo\n"; };
    scope_exit_2 f;
    fun_obj x;
    scope_exit_2 x;
    scope_exit_2 std::bind(g, 10);
}

scope_exitマクロラムダ式用、scope_exit_2はそれ以外の関数オブジェクトなどにも使える汎用定義。たいへんいけめんですね。

2010/09/14(火) 回避!!!!!!

Boost.勉強会#2にいってきた話を書こうと思うけど、めんどくさいからどうしようか悩んでいる

勉強会で配る用の名刺を作らねばとプリンタのインクと名刺用の紙を買ってきたわけですが、前日の夜にハードディスク整理を始めたから名刺作る時間がなくなってきた上に、家のプリンタが大きすぎて名刺印刷できなかった。やることがあるときに限って掃除をしたくなるのは何も部屋だけではないということが分かる、心温まる一場面ですね。仕方ないのでわんくまに参加したときに作った名札を持っていくことにした。この時点で7時。本当は開始の10時に間に合うように出発したかったけどそれなりに急いで新幹線に乗ったわけです。で、セッションのお題目を確認するためatnd開いたらなんと13時からであった。まさかの開始時間詐欺!訴えてやる!!!!!

QJD(急に時間ができたので)の私は前回江戸に来たときの続きで秋葉原を見て周ることに。日中のクソ暑い中、暑さでへばった犬(not めるぽん)のような表情で歩いていると突然「ちょっといいですか」と声をかけられ、まさかアイドルのスカウト?!などと思うわけもなく持ち物検査をされた。とてもいい日ですね不審者ざまぁwwwwwwwwwwwwwwwww死ねやwwwwwwwwwwwwwwwキモヲタがwwwwwwwwwwwwwwwwwwwwww

そんなわけで適当に駅前のカレー屋で昼食にして会場へ向かった。神保町A9出口は大変遠いですし、なんであんなに地下深いんですか?まぁ最終的に江戸にある全ての駅の各部分集合Sの順列全てを路線として作るわけですから、たかだか数十m程度の深さでガタガタ言ってはいけないのかもしれませんね。一生掘ってろボケが。

会場に入る前に、RedBullを探してあたりをうろうろしていた。それにしてもみんなtwitterで「会場到着」とか書いてるのに全然会わなかったんですが何か私だけ結界の中にいたんですかね?江戸こわいわー

RedBull買って飲みながら会場のビルに入ると、ドアの前に大きな犬がいたのでなんでかなーと思ったらめるぽんさん(not 犬)だったのでびっくりも何もしなかった。「あ、お久しぶりです」などと挨拶をするとさらりとセキュリティカードを渡されて、じゃあ続きお願いしますとビルの奥に姿を消した。すごくナチュラルに会場案内を託されて私はドアの前で右手にRedBullを、左手に携帯を持ってtwitterをしながら案内についた。傍から見れば全くやる気ない人ですがやる気ないので問題ない。開始時間を少しすぎて遅れてくる人もいなくなった頃、会場に入ってeldeshさんの隣の席に座り、バッテリーが死んでいるノートPCに電源入れようとしたら、おいここ電源タップ八分やないか!!!今度からはちゃんと延長コードもっていこう。それと便座カバー。結局隣のテーブルの電源タップを引き寄せて接続した。大変厚かましくてすみません。次からは延長コード持参しますから赦してくだしあ><電源をつないでからは、twitterしながら変なコード書きながらセッションを聞いていた。

勉強会の内容はtwitterのC++の話題まとめシステムであるところzakさんがとぅぎゃってくださっている(ブラクラ的重さらしいです)のと、アキラさんの日記、その他適当にググってそちらをご覧あれ。まぁこの日記で「勉強会行った」系の話をしてその内容について触れることなんてほとんどないというのは周知の事実なので問題なし。そういえば「(Boost.Interfacesを)もっとカッコいいイケメンにしろ」とアキラさんが言っていたので、まぁそのうち考えたいところですね。

懇親会。席の人と話していると何やら突然一冊の本をさし出された。え、サインですかそうですか、というか一般人にサイン求められてもそんなのあるわけないでしょう!お前はサインあるんか!!!まぁ適当に書いた。するとまた本をさし出された。サインらしい。気付けば魔導書に著者のサインしてほしい人が懇親会場に著者巡りの列を作っていた。観測できた範囲ではどうやら、…→kinabaさん→私→melponさん→アキラさん→…という順に回っているようであった。「サインお願いします」と差し出される本にずらりと並ぶ著者のサインと、私の残念な字が対比になってより他の方のサインがカッコよく見える効果がってやかましいわ!!!

ほどよくアルコールが回ってきたのでふらふらと他の席へ行ったりした。「PDF内のテキスト検索って遅いじゃないですか?あとCtrl-A Ctrl-C Ctrl-Vで全文コピペしたりすると文章の前後が入れかわったりするけどあれって実はたとえ一行の内容でも数文字とかで切って、そのブロックごとに位置を微調整してるからみたいなんだけど、そのお陰で見た目「あいうえお」となってても実は「あい」「うえ」「お」と切れてるもんだから単純なテキスト検索はひっかからなくて各断片の座標をもとに文字列を復元しないと検索できないクソが†uck」という話をした。あとエクセル方眼紙の話とか。

懇親会のあとはカラオケにいった。もともと一人でオールナイトする予定だったところ、なんだか15人ぐらいでとりあえず2時間カラオケやったあと改めてオール組は入りなおすということに。部屋に入ったあとらんはさんはずっとPCに向かってたけど何してたんだろう…

2時間後一旦退室して、オール組(最終的に7人)はややこしい形の交差点の斜向かいにあるカラオケ屋に入った。大都会エド(笑)の交差点はみんなこんなんで渡るの苦労しますやう゛ぁい。近い将来都民全員が一つの交差点で同時に事故って死ぬ。オールナイト中、kinabaさんとはみがきさんはずっとマリ姉ばかりを歌っていたことぐらいしか記憶にない。夜が開ける頃にはみんな死にかけていた。秋猫さんとれーらさんと私は、翌日の予定(リナカフェに行って「りなかふぇなう」とpostする)を考えて、秋葉原のネカフェで仮眠をとった。ところで寝る前にこのようなpostがあったのだけど、遠出前なのにオールとか付き合っていただいてすみません…

昼。前日のカレー屋の隣でバーガーなど食べて、銭湯に行った。それにしても行く場所行く場所みんな地形がおかしい。建物が木造からコンクリートになっただけで道路を整備していない街が、そこにはあった。ていうか、江戸だった。江戸はもっとグラフ的な意味で道を整備するべきじゃないですかね!ダイクストラもびっくりですよ!エドガー・ダイクストラだけに!

それはそうとあそこの銭湯、備え付けのシャンプー粗悪じゃなかろうか…頭洗ったらやたらと髪の毛同士が絡まるようになった。ジェットバスがあったのだけど、あれの噴射を尻の穴あたりにあてていた(アナニー的な意味ではなく)。浣腸ってされたことないけど、されたらあんな感じじゃないかなーと思った。

銭湯を出て、広間で涼んでいるとtwitterにこんなpostがあったので、早速リナカフェに向かった。着いたら、らんはさんとにはさんがいた。にはさんが分からない話なのに私が分かるわけないので私はとくに必要なくて大変悲しくて、大変悲しみながらよくわからないC++の話をしてた(長いので別記事で)。

夕方6時頃になって2人は仮面ライダーの映画にいくとかでその場は解散しました。私は帰ろうと思っていたら、どういう経緯かは聞いてないけど秋猫さんとれーらさんはリナカフェで会った鳥谷さんと男の娘カフェかメイドカフェか男装カフェに行くという話になっていたので、2次元ならともかく3次元ではそんな属性はないがご一緒させていただいた。行ったのは男装カフェだった。

男装カフェは割と男性でも入れるというか男装であること以外は普通なので普通だったけど、店員同士の会話は男ってよりは女子校の会話という感じだった。まぁ私は女子校とかテレビでしか見たことないのでよく知らんけど。適当に秋猫さんをいじった後、帰る頃合いになったので店を出た。そしてふとリナカフェにはこんさんとかぱいさんとかいたなぁカフェ行く前に挨拶すればよかったなーなどと思った。

帰りの新幹線は指定席の切符買ったけど、窓側じゃなかったし、しかもコンセントがある列車だったので、ダメ元で自由席の車両を探したら空いてる窓側の席を見つけたので、そっちにした。指定席の切符を買っておいて自由席に座るなんて、まさに外道!!

江戸駅で買ったカツサンドはうまかった。箱見たらなんか神戸とか書いてありましたがエドはいつから神戸になったんや?そのあとじゃがりこをボリボリしながらこの日記を書き始めた。

この記事に登場した人物:

(だいたい話に登場した順、@付きはtwitter id)

よく分からない話

一つめのアレは実は「え、RVO?」とか思ってたけど、そうかmakeSの呼び出しがRVOされるのかー。

C++.Tのオーバーロード解決の話はなんか

void f(int);
template<typeid T> where { ConceptA<T> } void f(T);
template<typeid T> where { ConceptB<T> } void f(T);
template<typeid T> typename enable_if<is_same<T, double>, void>::type f(T);

とかの場合はどうなるんだろうと思った。にはさんが思いついた方法で書き直すと、

template<typeid T> where { T ~ int } void f(T);
template<typeid T> where { ConceptA<T> } void f(T);
template<typeid T> where { ConceptB<T> } void f(T);
template<typeid T> typename enable_if<is_same<T, double>, void>::type f(T);

で、一番下のヤツは場合制約ベースで解決できんのかこれ…Overload Resolutionのルールをちゃんと理解してないから知らん。いや、制約ベースで考えたとしたら、特に制約ないから一番適合度合いが低い扱いになるのかな?やっぱり知らん。

あと、

template<typename T>
struct id { typedef T id; };
template<typename T>
void f(T) { printf("T"); }
template<typename T>
void f(typename id<T>::type) { printf("id<T>::type"); }
int main() {
    f(1);
    f<int>(1);
}

がgcc4.4, 4.6(trunk), VC(の多分そこそこ新しいやつ)でコンパイル及び実行結果が違うけどどれが正しいのか的な。

f(1)ではそもそもテンプレート版が呼ばれたらおかしいのはいいとして、f<int>(1);は多分曖昧でコンパイルエラーになるはずで、その点では4.4が期待する動作だけど規格読んでも根拠見つけられなかったので知ってる人はどこか人目につきやすい場所にFCDか何かのパラグラフとセクションを示せばいいんじゃないでしょうかみんな喜びます。

2010/07/06(火) 味噌カツおでんうどん風たこ焼まんじゅうパイ

user-defined string literalはliteral operator templateでほげほげできないと何度言ったら…

私自身何度も疑問に思ってはその度に調べていたし、しょっちゅうtwitter上でも話題になるのでここに書いておきましょう。

すごく手短に言うと、

template<char ...str>
struct hoge_t {};

template<char ...str>
constexpr hoge_t<str...> operator ""_hoge();

このような定義(ここではliteral operator template形式とでも呼ぶことにします。)は、決してuser-defined string literalに対するoverload resolutionで選ばれることはないのです。literal operator template形式が使えるのは、「100_mega」や、「3.14_oyoso」など、オペランドが整数、浮動小数点数の時に限ります。

2010/05/29(土) 私は帰ります

C++WG Adhoc Conferenceに行ってきました(の書きかけ)

出発〜懇親会まで

5/29、このような会議があるということで、参加させていただきました。

赤坂で10時から、ということで、だいたい3時間半あればいけるかなと思ってそんな感じで時間を見て出発した。お近くのみどりの窓口にて新幹線の指定席の予約を取ろうとすると、思ったより遅い時間の予約しか取れない。仕方ないので帰りの新幹線の指定席の予約だけとって、行きは自由席にした。しかしここでまっこと残念なことに買った切符で改札が通れない。駅員さんに「あのーこの切符通らないんですが…」と尋ねてみるも、しばらく分からず。そして駅員は言ったのです、「この切符、6月27日(6月第四土曜)から有効ですよ」と。あぁ、私はとうとう目もおかしくなっていたのですね、分かります。

そんなわけで窓口で当日の切符に変更してもらって、ようやく出発。このときにもうなんか時間やばいんじゃないかなーなどと思いつつ、とりあえず新幹線に乗ってから落ち着いたところでgoogle mapで所要時間を調べたところ、あちゃーこれはもう遅刻決定ですよ。まいりましたね。とりあえず光成さんにメールを入れておきつつ、twitterにもpostしておくとredboltzさんから「伝えておこうか?」とありがたいreplyが。いやーうれしいですね人とのつながりは。twitterで広がる仲間の輪(笑)

遅刻連絡を入れたところで、幸運なことに電源が使える車両だったので、先日とんできたメールに添付されていたコメントをAndroid端末で確認しながら、持ってきたラップトップでFCDを読むことに。あー二つヂスプレイがあるって快適ですねという気分で、一通りのコメントをチェックしおわった頃には新横浜でした。一方twitter上に割と「今起きた」とか「間に合わない」などという不届きなpostがあったのは一体どういうことなんでしょうか。

さて、新幹線を降りて、新橋まで山手線で移動して、そこから地下鉄を使うわけですが、あの路線図、何度見ても慣れないですね。あの、大量のミミズがのたくったような路線図。あれはもう路線案内をビジネスとして成立させるためにあんな複雑にしているとしか思えない。陰謀や!これは陰謀なんやで!

半蔵門線は溜池山王で降車して、それからまた地上まで息を切らしながら上がるわけです。深すぎますね。酸欠で死ぬかと思った。

ようやく会場のあるビルに到着するわけですが、あれーエレベーターが12階までしかないよー?→12階からもう一つのブロックのエレベーターに乗る→あれーついたけど見つからないよー?→東館でした→東館のエレベーターどこー?→あれー東館のエレベーターあったけど12階までしかないよー?→12階からもう一つのブロックのエレベーターに乗る→あった!という具合に迷いまくって、ようやく着いた頃には表決とってました。さすがに来たばっかりで何も把握していないので、賛成も反対もできませんでしたが。落ち着いてあたりを見回していると、場にいる人全員がC++に精通し、C++について何か思っているんだなーと実感して、それだけで背筋がゾクゾクしましたね。なお、会議の内容などはググってください。

DIYの精神に則って、差分だけ書きます。くらいおらいとさんはオーラが出ていたのですぐに分かりました。kikairoyaさんは、おじさんだと思っていました。ごめんなさい。あと私のラップトップの壁紙は全然あれげじゃないです普通です!きっと見間違いか何かでしょう。あとお昼ごはんのとき、「0xだったらプリプロセッサもういらないんじゃね?」という話になったので、「あれは構文でっちあげ器なんです!」という主張を展開してきました。なぜ納得していただけないんでしょう?C99から可変長マクロ引数も輸入されたというのに!ちなみに別の日の話ですが、諸般の事情により今回は不参加だったuskzさんからは、「最初みたときなんて読み易いコードなんだ」というコメントをいただいています。ですよね!!!まぁプリプロセッサがどうとかより、モジュール機能は切実に欲しいです。namespaceとかtemplate spacialization, ADLのあたりが絡んでくると難しいだろうなーと思いますが。

会議が終わると、問い詰める会です。こっちはC++の人に加えて、各方面の人が集まりました。私は終始飲みながら席をふらふらしてました。ところが、新幹線の終電が迫ってきたわけです。一応日帰りのつもりだったのですが、面白くなってきているので、予定を変更して一泊することに。しかし席は朝に予約しちゃったもんだから、あわてて新橋まで行ってみどりの窓口で次の日に変更しました。ご予約は計画的にね!

さて、割と酔いが回った状態で飛んでいったので、会場の最寄り駅を降りたあと、道どうだっけ…などという自体に陥ったわけですが、ウルトラスーパーヒューリスティックな探索で会場を探索して帰ってきました。やりました。

カラオケ

  • ゆゆさんマジライブハウス
  • めるぽんさんマジサンホラー
  • くらいおらいとさんマジ津軽
  • アキラさんマジ歌いたい放題
  • egtraさんマジCC
  • はみがきさんマリ姉
  • 私はいつも通りでした

明けて

  • くらいおらいとさんと二人、渋谷に投げだされてネカフェを探してうろうろ
  • 「こんな都会のど真ん中に坂とか大阪じゃあ絶対ないですね」
  • 感想:東京は都会の皮を被った田舎だねこりゃ。
  • ネカフェ発見
  • 各自適当に休憩したのち店を出ることに
    • イスで寝る
    • celeron Dの分際でリネージュIIとかどうすんねん…おいアップデートとかすんなよそもそもこの環境でプレイできんやろ
    • とりあえずtwitterを起動する
    • BGMに包まれて寝ようとyoutubeで動画を開いていると突然のシャットダウン。何事かと思ったけど、これ多分過熱からの保護機能じゃなかろうか
    • またアップデート。多分同じ内容の。おいシステムのバージョンぐらい確認しろよ!
    • あれ?「再起動後は元の状態にシステムを復元します」ということは、リネIIのアップデートもなかったことになってるだけか!アホか!
    • もうマシン使うの止めて寝よう…
    • 右を向いて寝る→首を痛める
    • 左を向いて寝る→首を痛める
    • 結局申し訳ないと思いつつ、机の上の奥のほうに足をほうり出して寝た。
    • 断続的に寝てたので全然疲れがとれた感じがしないまま、ネカフェを出る。結局あのネカフェはネットすらまともにできないのであった。
  • 歩いている人は、早朝とは違う雰囲気で、普通の人が多かった。

神保町

  • とりあえず着いたものの、どこに何があるかとか全然しらないので適当にそのへんを歩いていたら、何軒か古本屋があったので1時間ほど回っていた。
  • 字源辞書とかあって、ちょっと欲しかった
  • はらがへってきたのでラーメン屋を探したが、なかなか入りたい店が見つからなかった。そこで急に「秋葉原に行きなさい」という電波を受けとったので、行くことにした。

秋葉原

  • JR秋葉原 電気街口の前
    f:id:DigitalGhost:20100530153129j:image:medium
  • 特に何も買っていない
  • まず康龍でラーメンを食う
  • ゲマに入る。上に伸びてるけど上下の移動手段の配置がつぎはぎすぎるやろ
  • 魔窟の全容
    f:id:DigitalGhost:20100530161115j:image:medium
  • 前回来たときは真夜中だったし全然見てないので、街中をじっくり回ることに
  • 木彫りサイバスター
    f:id:DigitalGhost:20100530143747j:image:medium
  • ガンダムカフェ
    f:id:DigitalGhost:20100530145120j:image:medium
  • ヨドバシの前に行くとQOIでトイレに。トイレットペーパーは、ありました。
  • 裏通り
    f:id:DigitalGhost:20100530155119j:image:medium
  • 昭和通りのほうからもういっかい電気街のほうへ
  • 日本橋は堺筋沿いに縦長に作られてるけど、ここは一箇所に集中している。裏手はカオスすぎる。
  • まんだらけの近くは行ってません
  • 帰る

2010/05/13(木) もれなくはずれが当たります

Xmonadの設定など

Xmonadを導入しようとして設定したこと

でもなんか漢字とかの描画が残念な感じになったので、Xfceセッションログインしたときにフォント描画の設定してるんどこやーと探しまわった。

  1. /usr/share/xsessions/xfce4.desktop の設定から、startxfce4が走る
  2. /usr/bin/startxfce4 が設定があるディレクトリを探す
  3. /etc/xdg/xfce4/xinitrc がウィンドウマネージャとかセッションを起動する。で、ここで起動するxfsettingsdっていうdaemonがやってるっぽい。

結局それしか分からなかったし、XsessionとかXresourcesとかも探したけど原因は分からなかった。ところが、ふと、残念になってるのはメニューとかテキストボックスだけっぽいことに気付いたので、これはなんかシステムがデフォルトで使うフォントの設定じゃないかなーと思って.fonts.configを適当に設定したらうまくいった。

あとxmobarのほうは多分自分でutf8サポートのオプション付けてビルドしないと無理っぽいのでcabal installしよう。

2010/04/24(土) 泥猿猿人

劇場版 魔法少女リリカルなのはThe MOVIE 1stを見にいきました

なのはさんがフェレットユーノ君をかばって前に出たとき、当然のように彼の位置は明らかに絶妙なポジションでした。初対面の女の子のぱんつを覗こうとするなんて、劇場版でも淫獣っぷりは健在ですね。

2010/03/22(月) あばらんち

C++/CLIについてのよくある誤解

以前にも書いたような気がしますが、もう一度書きます。C++/CLI仕様の邦訳)は、.netからC++やその他ネイティブコードのライブラリを楽に使えるようにするためと、そのための作業を楽に行うための言語です。「.netで使えるC++」ではありません。そういうことを言う人は悉くこの言語の厳つい顎で頭を砕かれ、手足をもがれて、腸を食らい千切られ死にます。それが摂理なのです。あらがえません。C++/CLIは大きく分けてC++と、C++風構文を持つ何か.netっぽい言語(ここでは仮に「Cヰ」と呼びましょう)からなる、キメラです。二つの言語のコードは一つのファイルに混ぜて書くことができます。C++の部分はそのままC++です。何も違いはありません。GCもありません。ありませんが.netのGCにネイティブオブジェクト(要するにC++で作ったオブジェクト)を通知して管理させる方法はあります。interfaceだとか多重継承できないとかそういうのもありません。interfaceはあったような気もしますがVCのC++に対する独自拡張なので知りません忘れてください。今拡張という言葉が出てきましたが、C++/CLIはC++の拡張ではなく、別の言語です。これは真面目にです、別の言語としての規格がちゃんと存在します。それから以前はC++のマネージ拡張というのがありましたがそれも忘れてください。こっちはC++への拡張ですが、構文が違うだけです。誤解しないでほしいですが、私はC++/CLIがクソである、と言いたいわけではありません。しかしマネージ拡張はクソです。

話を戻しましょう。どんな感じの言語かは、ここを見ればだいたい掴めると思います。掴んでください。C++は普通にC++ですが、Cヰはどうでしょうか。まずtemplateが使えません。代わりにgenericsが使えますが、これは構文がC++風なだけで.netのそれそのものです。今、「だったら何も問題なくね?」と思いましたか?

template<typename T>
typename F::result_of_foo func(T & obj) {
    return obj.foo();
}

とても人為的な例ですが、まずこれ相当のものは書けません。今から理由を説明しますので慌てないでください。とりあえず、これと同じことを意図したCヰの擬似コードを示します(あくまでも擬似です)。

generic<typename T>
??? func(T ^ obj) {  // ^ 記号はC++の*(ポインタ修飾)みたいなものです。
    return obj->foo();
}

断わっておきますが、Cヰではメンバに属さないメソッドをアセンブリの外に公開できません。このへんはCLIの仕様によるところです。なので実際にはCヰのクラス(これはあとで説明します)の静的メソッドにするべきですが、ここでは対比として分かりやすくするためにこう書いておきます。ちなみに名前のガイドラインも守ってませんが、そのへんはもういいでしょう。あなたはもう大人なのですから、今言いたいことの本質がそこではないことぐらい分かるはずです。

まずobj.foo()というメソッド呼び出しがコンパイルエラーとなります。それと、???を書く術がありません。C++の真似してるクセに使えなさすぎです。ですがこれには訳があります。

genericなメソッドやクラスは、コンパイル時だけで世界が閉じるわけにはいきません。そのようなメソッドを含むプログラムから生成されたアセンブリを利用する側からでも、彼らが指定した型でメソッドを利用できるような仕組みになっています。当然アセンブリを利用する側が一体どのような型でこのメソッドを利用するのか分かりません。分からないということはコードを生成できません。困りました。でも.netだとできています。実はgenericなメソッドやクラスは、Tが何であろうと同じコードで動いています。つまりTによってコードを生成することはありません。これは半分嘘です。Tが値型の場合はコードを生成しなければいけないこともありますが、そのへんの細かいことは今はいいです。話を進めます。型パラメータTに対して、Tが持つべき特徴を指定します。

interface class IFoo {
    Foo ^ foo();
};
generic<typename T>
    where T : IFoo
Foo ^ func(T ^ obj) {
    return obj->foo();
}

where節が登場しました。これはTがIFooを実装したクラスでなければならない、という制約です。ということは、この関数におけるTは必ずfooというメソッドを持っているはずです、これで、たとえ別のアセンブリに存在する型であっても、IFooを実装しさえすればこの関数を呼び出せます。何せ型に合わせた関数のコードを吐くのではなく、関数のコードを決め打ちにして、引数となる型の構造のほうを関数に合わせたのですから当然です。これでこのメソッド呼び出しは、一つのコードで実現できます。おめでとうございます。しかしこの例だけでは Foo ^ func(IFoo ^ obj); と比べてどう便利なのかという話です。何をおっしゃいますやら、私にも分かりません。ですが generic<typename T> where T : INantoka List<T> bar(Bar<T>); という関数とかそういうのを考えればありがたみが分かると思います。ところが残念なことに、私は今とてもおなかがすいているので次にいきます。今煙に巻きました。

.netにはデリゲートという機能があります。Boost.Signals2相当です。ところがこれはCヰからだと使いにくいです。ちょっとここから例を拝借します。3番目のサンプルコードをC++/CLIで書いてみました。

/// <summary>
/// メッセージを表示するだけのデリゲート
/// </summary>
delegate void ShowMessage();

ref class Person
{
  System::String name;
public:
  Person(System::String name){this.name = name;}
  void ShowName(){Console.Write("名前: {0}\n", this.name);}
};

int main()
{
  Person ^ p = gcnew Person("鬼丸美輝");

  // インスタンスメソッドを代入。
  ShowMessage show = gcnew ShowMessage(p, &Person::ShowName);

  show();
}

できました。最後から2番目の文です、これが使い勝手の悪さの全てです。デリゲートを作るのに毎回C++で言うメンバ関数へのポインタを書くようなものです。激しく使い勝手が悪いです。これがC#(特に2.0以降)だとShowMessage show = p.ShowName; で余裕です。この差は大きいです。.netでGUIプログラミングをやろうとするとイベント登録/削除のためにデリゲートが頻出します。この差が10年分ほど積み重なれば間違いなくエベレスト登頂できます。それと、あまり関係ないですが、同じ理由でBoost.Bindの使い勝手も今一つだと思っているので、C++では主にPStade.Eggで作った関数オブジェクトを主体に扱いたいです。

忘れていました。そもそも組み込み型の一部を除いてC++の型とCヰの型には互換性がないので、以下のようなことは(単純には)できません。

struct Foo {
  int n;
  …
};
value struct Bar {};
ref struct Baz {
  int n;  // これはおーけー
  char * a;  // ポインタもおーけー
//Foo & foo;  // C++の参照をCヰの参照型(値型でも)のメンバとして使うことはできない!
  Foo * pfoo;  // ポインタでやりましょう!
//Foo foo;  // でも値では持てない!
  …
};
struct Qux {
//Bar bar;  // Cヰの値型オブジェクトも同じ!
//Baz ^ baz;  // CヰのハンドルをC++の型のメンバに持つことはできない!
  …
};

// 関数のローカルオブジェクトとしてなら両言語のオブジェクトを自由に作れますし、返せます
Foo f() {
  Foo foo;
  Baz baz1;
  Baz ^ baz2 = gcnew Baz();
  baz1.n = 42;
  foo.n = baz1.n;
  baz2->n = foo.n;
  return foo;
}
Baz ^ g() {
  return gcnew Baz();
}

組み込み算術型、ポインタ型はCヰでもそのまま書けますが、現実はそんなに甘くありません。それ以外のC++の型はCヰの型のフィールドにはできませんし、C++の型のメンバとしてCヰの値型/参照型を持つことはできないです。辛い現実ですが、受け入れなくてはなりません。ちなみに(ref/value)と(struct/class)で4パターンの組み合わせがありますが、そのへんはさっきの入門サイトを見てください。

あとまだ色々あるのですが、もう列挙するのも面倒なので勝手に岩永さんのところさっきのところとか見比べてC#でのある機能に対応するC++/CLIのそれがいかに面倒か調べてください。あなたがEnumeratorです。

そう、面倒、面倒です。ですが私は最初に書きました。「C++/CLIは、.netからC++やその他ネイティブコードのライブラリを楽に使えるようにするためと、そのための作業を楽に行うための言語です」と。あなた自分でいったこと忘れたのですか?って感じで頭おかしいですね。いいえちっともおかしくありません。確かに全てをCヰでやろうとすると面倒ですしC++の使い勝手を期待するとがっかりですしC#使いましょうになるのですが、それはその方針が間違っているからです。VC付属のライブラリや.netのクラスライブラリを使えばネイティブオブジェクトをCLIのGCに管理させたり、ネイティブ側でもマネージオブジェクトへの参照を保持することができますし、それによってネイティブコード、マネージコードそれぞれでできることに制限が生じることもありません(コンパイルオプションがほげほげというのはありますが今は忘れましょう)。それになにより、最初のほうにさらりと書きましたが、C++のコードとCヰのコードは、混在させることができます。直前の関数の例がそうです。ここが重要です。一般に、ある言語から別の言語で作った機能を呼び出す場合、ここまでに述べてきた程度の面倒さでは済まない上に、型安全性の「か」の字どころか「安」の字の第一画すら含まない(.net系の言語間は別です、この記事の文脈からは、それらは全て同じ言語にしか見えません)ので、慎重に自分でそれを保証しなければなりません。ところがC++からはCヰをラッパとしてその背後にある.netクラスライブラリを、CヰからはC++あるいはその他のネイティブなライブラリを簡単に呼び出せます。まあ最近の.net系の言語の事情は知らないので、もしかしたら後者のメリットは無いかもしれないです。が、C++側から.netを使うにはこれ以上にない最高の選択です。だからC++/CLI(というかCヰ)を使う場合は、マネージとネイティブの仲介のためだけに使って、その他のほとんどのコードはC++や.net系言語で書いてください。これはお願いです。そしてこの言語のことを名前で呼んであげてください。君とか貴方じゃなくて、その言語の名前で呼んであげてください。そうすればこの言語あなたを背に乗せてマッハ3(後述のそれぞれの場所において)ぐらいで地を駆け、空を舞い、海を潜り、星光を殲滅するでしょう。背にのったあなたは最後に星の光と共に消滅します。でも許してあげてください、それは彼女なりの友情の証です。あなたはC++/CLIと友達になれたのです。

2010/03/15(月) モノタンク

WG21 Paper n3030 Rvalue Reference as "Funny" Lvalues を Background だけ訳した(主に私が理解するため)

そのうち江添さんがきっちりとしたエントリーにするだろうけど、折角途中まで訳したし、背景だけ終わらせて置いときます(一部訳せなかったけど)。間違いとか飛躍しすぎとかがあればコメントなりtwitterなりで教えていただけると、私のえーぶんどっかいりょくこーじょーにもなるのでとてもありがたいです。

I. Background
Rvalue references were introduced into C++0x to provide a mechanism for capturing an rvalue temporary (which could previously be done in C++ using traditional lvalue references to const) and allowing modification of its value (which could not). When used in the contexts of reference binding, overload resolution, and template argument deduction, it was desired that a function returning an rvalue reference should behave like a traditional function returning an rvalue. The most straightforward way of achieving that goal was to classify such rvalue reference return values as rvalues, and that approach is embodied in the current draft.

I.背景
rvalue referenceは、rvalue temporaryを捕捉し(これは従来でもconstな値へのlvalue referenceを用いてできた)、それに対して変更できるようにする仕組みとして導入された(これは従来できなかった)。関数のrvalue reference返しは、referenceの束縛・オーバーロード解決・テンプレート引数の推論(template argument deduction)の文脈で用いた際、従来的な関数のrvalue返しのように振る舞うべき、ということが望まれる。この目標を達成するための最も直接的な方法は、このrvalue referenceである戻り値を、rvalueとして分類することであった。そしてこのアプローチは現在のドラフトに取り込まれている。

Unfortunately, however, rvalues have certain characteristics that are incompatible with the intended uses for rvalue references. In particular:

  • Rvalues are anonymous and can be copied at will, with the copy assumed to be equivalent to the original. Rvalue references, however, designate a specific object in memory (even if it is a temporary), and that identity must be maintained.
  • The type of an rvalue is fully known – that is, its type must be complete, and its static type is the same as its dynamic type. By contrast, an rvalue reference must support polymorphic behavior and should be able to have an incomplete type.
  • The type of a non-class rvalue is never cv-qualified. An rvalue reference, however, can be bound to a const or volatile object, and that qualification must be preserved.

だが、rvalueは、rvalue referenceの用途と非互換な特徴を持つ。具体的には、

  • rvalueは名前を持たず、コピーしたオブジェクトはオリジナルと同値であるという仮定で自由にコピーできる。しかしrvalue referenceは、それが一時オブジェクトである場合でも、特定のオブジェクトを指して、ずっと保持し続けなければならない。
  • rvalueの型は完全に知られている。言いかえると、rvalueの型はcompleteでなければならず、rvalueの静的な型は、動的なそれと同じである。対照的に、rvalue referenceはpolymorphicな振る舞いをサポートする必要があり、incomplete typeを持つことを認めるべきである。
  • classでないrvalueの型は決してcv修飾されない。しかしrvalue referenceの場合、constやvolatileなobjectへと束縛することが可能で、その修飾は保存される。

In addition, rvalue references (like traditional lvalue references) can be bound to functions. Treating an rvalue reference return value as an rvalue, however, introduces the novel concept of a function rvalue into the language. There was previously no such idea – a function lvalue used in an rvalue context becomes a pointer-to-function rvalue, not a function rvalue – so the current draft Standard does not describe how such rvalues are to be treated. In particular, function calls and conversions to function pointers are specified in terms of function lvalues, so most plausible uses of rvalue references to functions are undefined in the current wording.

さらにrvalue referenceは、(従来のlvalue referenceのように)関数へと束縛できる。しかし、rvalue referenceである戻り値をrvalueとして扱うのは、関数のrvalueについての、妙な概念を言語に持ちこむ。このようなideaは以前存在しなかった―関数のlvalueはrvalueの文脈で関数へのポインタのrvalueとなる(関数のrvalueではない)―そして、現在のドラフトは、このようなrvalueの扱われ方について述べていない。具体的に、関数呼び出しと関数ポインタへの変換は、関数のlvalueでの話で、つまり関数へのrvalue referenceついてのそれらしき使い方は、現在の文言では定義されていない。

One possible approach to resolving these problems would be to maintain the current approach of treating an rvalue reference return value as an rvalue but to add various caveats to the specification of rvalues so that those coming from rvalue references would have special characteristics. This could be called the “funny rvalue” approach. However, further examination of the current wording of the draft Standard indicates that the problems listed above are probably only the tip of the iceberg: many of the specifications that should apply to the objects to which rvalue references refer, such as object lifetime, aliasing rules, etc., are phrased in terms of lvalues, so the list of rvalue caveats could get quite long.

ここまでで述べた方法では、rvalue referenceである戻り値のrvalue扱いについて現在のアプローチを保ちたいのだが、rvalueの仕様に対して、rvalue referenceに由来するいくつかの特例を付け加えるために、特殊な特徴を持つだろう。これは "funny rvalue" アプローチと呼ぼう。しかし、現在のドラフトをより深く調べると、前述の問題は氷山の一角に過ぎないことが分かった:rvalue referenceが参照するオブジェクトへと適用すべき多くの仕様、例えばオブジェクトのlifetimeやaliasing ruleその他は、lvalueの話として記述されている。つまりrvalueに対する特例の一覧はとても長くなった。

This suggests an alternative approach: that rvalue reference return values should actually be seen as lvalues, with a few exceptions to allow them to be treated as rvalues in the cases where that is intended, i.e., in reference binding, overload resolution, and template argument deduction. This idea, dubbed the “funny lvalue” approach, is the subject of this paper.

(The problems described above are discussed in more detail in the following issues in the Core Language Issues List: 664, 690, 846, and 863.)

この提案は、対照的なアプローチを取る:rvalue referenceである戻り値は、意図したケース(referenceの束縛・オーバーロード解決・テンプレート引数の推論)においてrvalueとして扱われることを認めるためにいくつかの例外を除いて、本当にlvalueと見なすべきだ。この考え方、 "funny lvalue" と呼ばれているアプローチは、このペーパーの主題である。

(上記で述べた問題については、Core Language Issues 664, 690, 846, 863で話題になっている)

2010/03/05(金) 壁から白い人がぞろぞろ湧き出てくる

前に作ったPP_IS_EMPTYの問題点

以前、PP_TUPLE_SIZEを作るために作ったPP_IS_EMPTYがどうなっていたのか思いだしたら、少しばかり問題がありました。

#define FOO(a, b, c)
PP_IS_EMPTY(e FOO)

eをカンマで区切られていないトークン列として、上記のようなトークン列を引数とすると、置換に失敗してエラーとなってしまいます。詳しく書くと面倒なのでソースを見て分かってください。

これを修正するには、全く別のアプローチが必要になりそうですが、これよりいい方法は思いつきませんでした。

それともう一つ、Wave driverで遊んでいたmelponさんから、可変長引数マクロの使い方が間違っているのではないか、という報告を受けました。

#define FOO(a, ...) という定義の引数付きマクロに対して、 FOO(e) と書くのはどうやら規格的にはwell-formedではないようです。VCgccは、これを私の意図した通りに展開してしまいますが。

こちらではC++0xのdraftから引用してみます。

n3035 Working Draft, Standard for Programming Language C++ §16.3.1.12

If there is a ... in the identifier-list in the macro definition, then the trailing arguments, including any separating comma preprocessing tokens, are merged to form a single item: the variable arguments. The number of arguments so combined is such that, following merger, the number of arguments is one more than the number of parameters in the macro definition (excluding the ...).

(読み間違えていなければ)前述の形式の引数付きマクロは、マクロ定義に登場するパラメータの数( ... は除く)より1つ以上多くの引数を要求します。で、これが先のコードのどこに影響するのかというと、

#define PP_IS_EMPTY_VIII(a, ...) PP_IS_EMPTY_IX((HELPER_IV_ ## a))

の展開です。PP_IS_EMPTY_VIIIには、PP_IS_EMPTYの引数を展開すると空になる場合、EMPTYというトークンが渡されるのですが、これは1つの引数なので、上記の通り、正しいマクロ置換のコードとは言えません。

これに対する修正は簡単で、PP_IS_EMPTY_Vの定義を、

#define PP_IS_EMPTY_V(a, ...) PP_IS_EMPTY_VI((a __VA_ARGS__ (), NIL))

のようにすれば解決します。 PP_IS_EMPTY_VIII に2引数以上渡るようにしただけです。

そもそもなんでタプルをどうのこうのってことになったのかと言うと、ふとプリプロセス時にラムダ式を書けないかと色々考えた挙句、できそうと言う結論に達した(普通に式を書けるわけではない)ので、せっかくだから少しでも見た目がかっこいいタプルで作ってやろうとして、PP_IS_EMPTYを思いだしたわけです。というわけでそのうち作るかもしれないし、作らないです。

2010/03/02(火) 人造人間虚弱体質

Spirit.Phoenix に apply がなかったので作ろうと思ったら bind ができたでござる

名前は apply だけど bind だよ。ちなみに boost::phoenix::bind よりすごい点は、 bind の第一引数を placeholder にできるところ。

あとC++03とC++0xの両方の場合で作ったけど、C++0xおいしいです (^q^)


#include <boost/config.hpp>

#if defined(BOOST_HAS_TR1_UTILITY) && !defined(BOOST_NO_VARIADIC_TEMPLATES) && !defined(BOOST_NO_DECLTYPE) && !defined(BOOST_NO_RVALUE_REFERENCES)
#  define APPLY_IMPL_BY_0X_FEATURE 1
#else
#  define APPLY_IMPL_BY_0X_FEATURE 0
#endif

#include <boost/spirit/home/phoenix/core.hpp>
#include <boost/spirit/home/phoenix/operator.hpp>
#include <boost/spirit/home/phoenix/scope.hpp>
#include <boost/spirit/home/phoenix/function.hpp>
#include <boost/spirit/home/phoenix/statement.hpp>
#include <boost/spirit/home/phoenix/bind.hpp>
#include <boost/ref.hpp>
#include <iostream>
#include <string>

#if APPLY_IMPL_BY_0X_FEATURE
#  include <utility>
#else
#  include <boost/mpl/assert.hpp>
#  include <boost/mpl/begin_end.hpp>
#  include <boost/mpl/find.hpp>
#  include <boost/mpl/fold.hpp>
#  include <boost/mpl/placeholders.hpp>
#  include <boost/mpl/transform_view.hpp>
#  include <boost/mpl/vector.hpp>
#  include <boost/mpl/void.hpp>
#  include <boost/function_types/function_type.hpp>
#  include <boost/utility/result_of.hpp>

#  include <boost/preprocessor/arithmetic/inc.hpp>
#  include <boost/preprocessor/comparison/greater.hpp>
#  include <boost/preprocessor/facilities/intercept.hpp>
#  include <boost/preprocessor/repetition/enum_params.hpp>
#  include <boost/preprocessor/repetition/enum_shifted_params.hpp>
#  include <boost/preprocessor/repetition/enum_shifted_binary_params.hpp>
#  include <boost/preprocessor/repetition/for.hpp>
#  include <boost/preprocessor/repetition/repeat.hpp>
#  include <boost/preprocessor/repetition/repeat_from_to.hpp>
#  include <boost/preprocessor/seq/seq.hpp>
#  include <boost/preprocessor/seq/for_each_product.hpp>
#endif

namespace bst = boost;

#if APPLY_IMPL_BY_0X_FEATURE

template<typename T>
T value();

struct apply_t {
    template<typename F, typename ...AS>
    struct result {
        typedef decltype(value<F>()(value<AS>()...)) type;
    };
    template<typename F, typename ...AS>
    typename result<F &&, AS &&...>::type
    operator()(F && f, AS && ...as) const {
        return std::forward<F>(f)(std::forward<AS>(as)...);
    }
};

#else

namespace mpl = bst::mpl;

template<typename Seq, typename T>
struct take : public
mpl::iterator_range<typename mpl::begin<Seq>::type,
                    typename mpl::find<Seq, T>::type>
{};

#define DEF_APPLY_OVERLOADS_OP(z, n, data) \
  BOOST_PP_SEQ_FOR_EACH_PRODUCT(DEF_APPLY_OVERLOADS, \
                                ((n)) BOOST_PP_REPEAT(n, MAKE_CONST_SEQ, _))

#define DEF_APPLY_OVERLOADS(r, seq) \
  DEF_APPLY_OVERLOADS_I(BOOST_PP_SEQ_HEAD(seq), BOOST_PP_SEQ_TAIL(seq))
#define DEF_APPLY_OVERLOADS_I(n, seq) \
    template<BOOST_PP_ENUM_PARAMS(n, typename T)> \
    typename result<BOOST_PP_ENUM_PARAMS(n, T)>::type \
    operator()(BOOST_PP_FOR((n, 0, seq), \
                            PARAMS_P, \
                            PARAMS_OP, \
                            PARAMS_M)) const \
    { \
        return param0(BOOST_PP_ENUM_SHIFTED_PARAMS(n, param)); \
    }

#define PARAMS_P(r, state) PARAMS_P_I state
#define PARAMS_P_I(n, i, seq) BOOST_PP_GREATER(n, i)

#define PARAMS_OP(r, state) PARAMS_OP_I state
#define PARAMS_OP_I(n, i, seq) (n, BOOST_PP_INC(i), BOOST_PP_SEQ_TAIL(seq))

#define PARAMS_M(r, state) PARAMS_M_I state
#define PARAMS_M_I(n, i, seq) \
    BOOST_PP_COMMA_IF(i) T ## i BOOST_PP_SEQ_HEAD(seq) & param ## i

#define MAKE_CONST_SEQ(z, n, _) (()(const))

struct apply_t {
    template<typename T0,
             BOOST_PP_ENUM_SHIFTED_BINARY_PARAMS(
                 PHOENIX_LIMIT,
                 typename T,
                 = mpl::void_ BOOST_PP_INTERCEPT)>
    struct result {
    private:
        typedef mpl::vector<BOOST_PP_ENUM_PARAMS(PHOENIX_LIMIT, T)> vec;
    public:
        typedef typename
        bst::result_of<typename
            bst::function_types::function_type<typename
                take<mpl::transform_view<vec,
                                         bst::unwrap_reference<mpl::_> >,
                     mpl::void_>::type>::type>::type type;
    };
    BOOST_PP_REPEAT_FROM_TO(1, PHOENIX_LIMIT, DEF_APPLY_OVERLOADS_OP, _)
};

#endif

namespace p2 {
using namespace bst::phoenix;
using namespace bst::phoenix::arg_names;
using namespace bst::phoenix::local_names;
}

p2::function<apply_t> const apply;

int f(int x, int y) {return x * y;}
int main () {
    using std::cout;
    using namespace p2;

    int n = 42;

    cout << apply(_1, n, _2)(bst::cref(&f), bst::cref(2)) << '\n';
    // cout << bind(_1, n, _2)(bst::cref(&f)) << '\n';  // これはできない

    let (_a = _1) [cout << apply(_a, _2, _3) << '\n'](bst::cref(&f), n, bst::cref(2));

    return 0;
}

これはさすがにタイムアウトhttp://ideone.com/YnxoTX5

2010/03/01(月) ありゃーの神

時代はBoost.Lambdaではなく、Spirit.Phoenixなのだ

#include <boost/spirit/home/phoenix/core.hpp>
#include <boost/spirit/home/phoenix/operator.hpp>
#include <boost/spirit/home/phoenix/scope.hpp>
#include <boost/spirit/home/phoenix/bind.hpp>
#include <boost/spirit/home/phoenix/function.hpp>
#include <boost/spirit/home/phoenix/statement.hpp>
 
#include <string>
#include <iostream>
#include <boost/ref.hpp>
#include <boost/lexical_cast.hpp>
 
namespace bst = boost;
 
namespace p2 {
using namespace bst::phoenix;
using namespace bst::phoenix::arg_names;
using namespace bst::phoenix::local_names;
}
 
struct inc_t {
    template<typename T>
    struct result {
        typedef T type;
    };
    template<typename T>
    T operator()(T x) const {
        return x + 1;
    }
};
 
p2::function<inc_t> const inc;
 
int main () {
    using std::cout;
    using namespace p2;
 
    int n = 42;
 
    cout << _1(n) << '\n';
    (cout << val(42) << '\n')();
    (cout << (ref(n) = 24) << '\n')();
    (cout << inc(_1) << '\n')(n);
    cout << let (_a = _1 + _2,
                 _b = 5)
            [ _a + _b ](n, bst::cref(13))
         << '\n';
 
    let (_a = 0) [
      for_ (nothing, _a < _1, ++_a) [
        cout << if_else(_a % 15 == 0, "fizz",
                if_else(_a % 3 == 0, "buzz",
                bind(bst::lexical_cast<std::string, int>, _a)))
             << ' '
      ],
      ref(cout) << '\n'
    ](bst::cref(15));
 
    return 0;
}

実行結果: http://ideone.com/33mSjQpx

すばらしい実力です。

2010/02/21(日) ライジング・ムーン

packageのインストールに関する備忘録

cabal install のリポジトリ

http://hackage.haskell.org/trac/hackage/wiki/CabalInstall

を見ればいいんだけど、おもいっきり間違えた。 cabal install の stable branche は http://darcs.haskell.org/cabal-branches/cabal-install-x.x である。

Setup configure のオプション

make configure と同じく --prefix=$HOME/xxx を付けてローカルに。ついでに --user を指定しておけば、 パッケージの登録がユーザのデータベースに対してなされるようになる。

道のり

  1. 何かcgi書こう
  2. できた!
  3. 日本語出力おかしい
  4. そうだ ghc 6.10 は日本語そのまま入出力できないのだった
  5. utf8-stringがひつようだ
  6. wget して展開してSetupとかめんどい
  7. cabal install だ
  8. Ubuntuのpackageにcabal installないぞ…
  9. よしdarcs getだ
  10. ライブラリが足りないので apt-get
  11. なんかライブラリの要求バージョンおかしい
  12. もしかして:HEADだから
  13. brancheにした
  14. あれ、cabalコマンドがないぞ
  15. cabalとcabal install間違えてたでござる
  16. できた、やっとcabal install使える
  17. sudo apt-get install libghc6-utf8-string-dev あるとか悲しい…というかなんでさっき apt-get したときに気付かなかったの…