Caution!
   本発表には次のものは含まれません
     C++の有用なプログラミング技法
     広くつかわれるべきテクニック
     その他なにか視聴者に役立つもの
   そういうのを期待された方はあしからず
自己紹介
 田中英行 (@tanakh, id:tanakh)
 Haskell愛好家
 C++歴
     1998年 ~
     2005年ぐらいまでは漫然と使っていました
   社内用C++ライブラリ(pficommon)を作成
     C++のきもさを再認識
     その辺で得られた知見を話します
      ○ ※実際に使われているわけではないです
pficommon
 Boostの広く利用できる部分(tr1)
 データ構造いろいろ
 サーバー書く向け機能
     マルチスレッド
     各種プロトコル
     RPC
   ウェブアプリ向け機能
     DSL
本日の内容
 ユーザー定義構文
 Serializable万能説
 Stream Fusion


   の三本立て
概要
   C++の構文のように扱える構文を
    定義してみようとすると、
    それがどういう意味を持つのだろうか、
    というお話
ユーザー定義構文
 ユーザー定義する組み込み構文のように
  扱えるもの
 例えば
     synchronized (Java)
     using (C#)
     actor (Scala)
   のようなものがC++でも書けると嬉しい
    ですね
ほかの言語の例
   Lisp
     マクロ・高階関数
   Haskell
     高階関数・モナドを引数にとる関数
   Ruby
     ブロックをとる関数
   Scala
     最後の引数にラムダ式をとる関数を書くと
     それっぽく見える
例:foreach
(for-each (lambda (x)      forM_ [1..5] $ ¥x ->
             (display x)     print x
  ‘(1 2 3 4 5))




(1..5).each{|x|            List(1,2,3,4,5).foreach{ x =>
  p x                        println(x)
}                          }
C++でのユーザー定義構文(1)
   statementを引数に取るマクロを書き、
    組み込みの構文要素に置換する

    #define foreach(v, c, s) ¥
      for(typeof(c.begin()) it=c.begin(); it!=c.end(); it++){ ¥
        v = *it; ¥
        s ¥
      }

    …

    vector<int> v; v.push_back(1); …
    foreach(int x, v, { cout<<x<<endl; })
問題点
   簡単に書けて、扱いやすいが、
    かっこ悪い
   foreach(, , {})
    ↑ 括弧の中に{}があるのが耐えられない
    }) って…
    foreach(int x, v) {
      cout<<x<<endl;
    }
   のように書きたい
C++でのユーザー定義構文(2)
   statementが後ろに来て整合性が取れる
    ようなマクロを書く

    #define foreach(v, c) ¥
      for (bool b=true; b; ) ¥
      for (v; b; b=false) ¥
      for(typeof(c.begin()) it=c.begin();
          it!=c.end() && (v=*it, true);
          it++)

    …

    vector<int> v; v.push_back(1); …
    foreach(int x, v){ cout<<x<<endl; }
C++でのユーザー定義構文(2)
 この書き方は美しいが、
  記述力を制限される
 既存の制御構文の後ろに文を置けるだけ
後処理
   synchronized(v){ … } のようなことを実
    現するには、{ … } の後に処理をさせな
    ければならない
forを使う方法
 後続の文より後に処理をさせる方法
 forを使う
     #define synchronized(v)       ¥
       for (bool b=true;           ¥
            b && (v.lock(), true); ¥
            b=false, v.unlock())

     #define synchronized(v)               ¥
       for (bool b=true; b; )              ¥
       for (scoped_lock lk(v); b; b=false)

   break, continueが食われてしまう
ifを使う方法
   ifの中で変数を宣言すれば、
    後続の文の後にデストラクタを動かせる
    #define synchronized(v)              ¥
      if (scoped_lock lk=scoped_lock(v))
ifを使う方法(2)
 コピー可能なクラスを定義する
 trueを返すoperator bool() を定義する
 デストラクタを定義する

    class hoge { … };

    if (hoge h=hoge()) stat;

    処理の流れ
     hoge::hoge()
     hoge::operator bool()
     stat
     hoge::~hoge()
改良
   operator bool() はfalseを返したほうがいい

    #define synchronized(v)               ¥
      if (scoped_lock lk=scoped_lock(v)); ¥
      else
活用例:CGI DSL
 class my_cgi : public cgi{
 public:
   void run(){
     html__{
       head__{
         title__{ text__("タイトル"); }
       }
       body__{
         a__{ href__ = "http://kzk9.net/blog/";
           text__("super blog!");
         }
         br__;
       }
     }
   }
 };
議論
 hoge(…){ … } の形で、
  前処理→処理→後処理 の形のユーザー
  定義構文が書けることが分かった
 だがそれだけだろうか?
 こういうことができるということは、
  つまりどういうことなのだろうか
見えざる継続
   継続とは
     ある計算過程のある瞬間における、その過
      程の未来全体を表すもの、あるいは計算過
      程の実行スナップショットと説明される。
      (Wikipedia)
     Schemeなどでは言語レベルでサポート
   継続は、実行中のどんな計算機プログラ
    ムにも存在する
関数呼び出しと継続
 関数呼び出しとはすなわち、継続をとも
  なう手続きへのジャンプ
 関数が呼ばれた=
  関数から返った後の継続が手に入る
    int foo(){ return 1; }   foo()が呼ばれる際に渡される継続
                             ・返された値に2を書けて
    void bar()               ・それを表示する
    {
      cout<<foo()*2<<endl;   実際のところC++では、
    }                        ・スタック
                             ・リターンアドレス
                             の対として表現される
ユーザー定義構文では
     hoge::operator bool() が呼ばれた瞬間の
      継続に注目する
class hoge { … };

if (hoge h=hoge()) stat;

処理の流れ
 hoge::hoge()
 hoge::operator bool() ← trueを返せば stat → hoge::~hoge() → …
 stat                    falseを返せば hoge::~hoge() → …
 hoge::~hoge()           なる継続
継続を取り出す
   callee save レジスタ、スタック、リター
    ンアドレスを取り出す

    cont get_ret_cont(){
      regs=get_callee_save_regs();
      stack=get_stack();
      ret=get_ret_addr();
      return (regs, stack, ret);
    }
継続が取り出せるなら
   operator bool()にて、継続を取り出し、
    さらに文実行後の継続を設定し、stat後
    の処理の流れを決められる
    hoge::operator bool()
    {
      cont c=get_ret_cont();
      next=…;
      c(true);
    }
    hoge::~hoge()
    {
      next();
    }
文を後ろに置く=継続を渡す
 つまるところ、if (hoge h=hoge()) { … } は
  後ろの文を引数に関数を呼び出しているの
  と同じである
 Schemeの高階関数や、Rubyのブロック構
  文と同じことができる
例:スレッド
   thread{ … } でスレッドを立てる
    後続の文が終了するとスレッド終了
    mutex m; int n;

    int main()
    {
      thread{
        for (int i=0; i<10; i++)
          synchronized(m)
            n++;
      }
      for (int i=0; i<10; i++)
        synchronized(m)
          n--;
    }
実装
class thread_forker{ … };

thread_forker::operator bool(){
  cont c=get_ret_cont();
  pthread_create(&tid, NULL,
                 bind(apply_cont, c, true));
  c(false);
}

thread_forker::~thread_forker(){
  pthread_exit(NULL);
}
別スレッドでの継続の起動
 スタックを別のスタックにコピーする
 ポインタの張り替えなどめんどい


      ret     ret     ret
    frame   frame   frame



      ret     ret     ret
    frame   frame   frame
まとめ
 C++でユーザー定義構文は意外ときれい
  に作れるんじゃなかろうか
 ポータブルな実装ができるかどうか
概要
 シリアライザというものがあります
 シリアライザが意外と色々なところに使
  えるという話
シリアライザとは
 データをバイト列に変換したり(シリアラ
  イズ)、バイト列からデータに変換したり
  (デシリアライズ)するもの
 クラスやコンテナの内容をファイルに保存
  したり、ネットワーク越しに転送したりす
  るのにとても便利
boost::serialization
   Boostに入っているシリアライズライブ
    ラリ
    class hoge{
    private:
      string a;
      vector<int> b;

      friend class boost::serialization::access;
      template <class Archive>
      void serialize(Archive &ar){
        ar & a & b;
      }
    };
boost::serializationの特徴
 シリアライズとデシリアライズを
  共通のコードで記述
 テンプレートでディスパッチ


    hoge h;
    text_oarchive oa(cout);
    oa << h; // text_oarchiveを引数にserializeが呼ばれる

    hoge g;
    text_iarchive ia(cin);
    ia >> h; // text_iarchiveを引数にserializeが呼ばれる
仕組み
   serialize()関数をオーバーロード
    template <class Archive>
    Archive &operator &(Archive &ar, T &v){
      serialize(ar, v);
      return ar;
    }

    void serialize(text_iarchive &ar, int n){
      ar.read_int(n);
    }

    void serialize(text_oarchive &ar, int n){
      ar.write_int(n);
    }
シリアライズするというのはどうい
うことか?
   クラスのシリアライズ
     メンバの列挙
   コンテナのシリアライズ
     データの列挙
テンプレートでのシリアライズ
 serializeがテンプレートメンバ関数
 シリアライズ・デシリアライズを
  共通化
 色々なシリアライザ(text, binary)に適用
  可能
 拡張可能
 → シリアライザじゃなくてもいいので
  は?
型を書きだす
   データの代わりに、型を書きだしてみる

    class type_oarchive{ … };

    template <class T>
    void serialize(type_oarchive &oa, T &v){ // デフォルト
      oa.enter_struct();
      serialize(oa, v);
      oa.leave_struct();
    }

    void serialize(type_oarchive &oa, int &n){ // 特殊化
      oa.add(new int_type(true, sizeof(int)));
    }
    …
型を書きだす(2)
   コンテナ型は特殊化する

template <class T>
void serialize(type_oarchive &oa, vector<T> &v){
  oa.add(new array_type(get_type<T>());
}

template <class K, class V>
void serialize(type_oarchive &oa, map<K, V> &v){
  oa.add(new map_type(get_type<K>(), get_type<V>());
}
型を書きだす(3)
   型情報取得関数

    template <class T>
    type_info *get_type()
    {
      T v;
      type_oarchive oa;
      oa << v;
      return oa.get();
    }
Serializable = Reflectable
 値の代わりに型を書きだすことにより、
  Serializableなクラスは(部分的には)
  Reflectableなクラスとなる
 Reflectableなクラスは明らかに
  Serializableに出来るのでこれはそう
  おかしな話ではない
 つまり、(部分的には)Serializableと
  Reflectableは等価である
応用例:RPC
// シグニチャ
RPC_PROC(add, int(int, int)) // メソッド定義

RPC_GEN(calc, add)                     // クラス定義


// サーバ                                   // クライアント
int add(int x, int y){ return x+y; }     hoge_client cli(“localhost”, 12345);
int main(){                              cout<<cli.call_add(1,2)<<endl;
  hoge_server serv;                      // ↑ 3が返ってくるはず
  serv.set_add(&add);
  serv.serv(12345, 10);
}
RPC説明
 適当に関数のシグニチャを定義して、
  それに合う関数をセットする
 関数の引数、返り値はソケットでやり取
  りされる → シリアライズ可能でなけれ
  ばならない
RPC:クライアントコード生成
   言語bindingを自動で生成
     C++ソースを読み込む
     パーズするのは大変すぎる
     g++にやらせる
     リフレクションする
     RPCの型情報が取れる
     好きなコード生成できる
     RPCでやり取りするデータはすべてSerializable
     でなければならないはずなので、すべて何もし
     なくてもリフレクション出来るはずである
                                      for
RPC:テスト生成
   おなじ原理でRPCテスト用のWebサー
    バーをC++コードから自動で生成
まとめ
   Serializableなデータ構造は思ったより
    有用だ
概要
   ストリームを扱う計算が
    テンプレートで速くなるという話
ストリーム
   (ここでは)何かデータの列
     配列とか
     外部メモリ上のデータとか
   ストリームに対する演算
       map
       filter
       fold
       sort
       merge
       など…
例
   ストリームに対する演算の繰り返し
vector<int> v, w, x;
for (int i=0; i<100; i++) v.push_back(i);

remove_copy_if(v.begin(), v.end(), back_inserter(w), is_even());
transform(w.begin(), w.end(), back_inserter(x), div2());


 中間配列ができる
 配列を二回なめる
 外部メモリを扱う時に特に顕著
                                                            for
1パスでやるには
   一か所に書けばいい
vector<int> v, x;
for (int i=0; i<100; i++) v.push_back(i);

for (int i=0; i<v.size(); i++)
  if (v[i]%2==0)
    x.push_back(v[i]/2);



 モジュラリティが低い
 さっきのように書いて、こうなってほしい
遅延ストリーム
   それぞれの処理で一気に配列を舐めるのが
    いけない → 遅延させてやればいい
    template <class T>
    class stream{ … };

    template <class S, class F>
    class filter_stream{
    public:
      typedef S::elem_type elem_type;
      filter_stream(S &s, F f=F()): s(s), f(f) {}
      elem_type get(){
        for (;;){
          elem_type r=s.get();
          if (f(r)) return r;
        }
      }
    };
遅延ストリーム(2)
 template <class S, class F>
 class map_stream{
 public:
    typedef S::elem_type elem_type;
    map_stream(S &s, F f=F()): s(s), f(f) {}
    elem_type get(){
      elem_type r=s.get();
      return f(r);
    }
 };

 stream<int> s;

 filter_stream<typeof(s), is_even> t(s);
 map_stream<typeof(t), div2> u(t);

 while(!u.empty())
   cout<<u.get()<<endl;
インライン化
 テンプレートで書かれているので
  インライン化できる
 1パスでできる

    stream<int> s;

    filter_stream<typeof(s), is_even> t(s);
    map_stream<typeof(t), div2> u(t);

    u.get();
    // => r=t.get(); return div2(r);
    // => int r; for (;;){ r=s.get(); if (!is_even(r)) break; }
    //    return div2(r);
yieldとの対比
   C#でのyield
    class FilterStream{
      public FilterStream( … ) { … }
      public IEnumerator<int> GetEnumerator(){
        foreach (int t in s)
          if (!f(t))
            yield return i;
      }
      …


   ループを回すだけのようなことなら
    同じようにできる
コルーチン=遅延評価
 yield=コルーチンで遅延評価はできる
 遅延評価でyieldのようなこともできる
 列挙のようなときにはうまくいく
まとめ
   C++の謎テクをいくつか紹介
     ブロック=継続
     Serializable=Reflectable
     コルーチン=遅延評価
   pficommon近日リリース予定

Pfi Seminar 2010 1 7

  • 3.
    Caution!  本発表には次のものは含まれません  C++の有用なプログラミング技法  広くつかわれるべきテクニック  その他なにか視聴者に役立つもの  そういうのを期待された方はあしからず
  • 4.
    自己紹介  田中英行 (@tanakh,id:tanakh)  Haskell愛好家  C++歴  1998年 ~  2005年ぐらいまでは漫然と使っていました  社内用C++ライブラリ(pficommon)を作成  C++のきもさを再認識  その辺で得られた知見を話します ○ ※実際に使われているわけではないです
  • 5.
    pficommon  Boostの広く利用できる部分(tr1)  データ構造いろいろ サーバー書く向け機能  マルチスレッド  各種プロトコル  RPC  ウェブアプリ向け機能  DSL
  • 6.
  • 8.
    概要  C++の構文のように扱える構文を 定義してみようとすると、 それがどういう意味を持つのだろうか、 というお話
  • 9.
    ユーザー定義構文  ユーザー定義する組み込み構文のように 扱えるもの  例えば  synchronized (Java)  using (C#)  actor (Scala)  のようなものがC++でも書けると嬉しい ですね
  • 10.
    ほかの言語の例  Lisp  マクロ・高階関数  Haskell  高階関数・モナドを引数にとる関数  Ruby  ブロックをとる関数  Scala  最後の引数にラムダ式をとる関数を書くと それっぽく見える
  • 11.
    例:foreach (for-each (lambda (x) forM_ [1..5] $ ¥x -> (display x) print x ‘(1 2 3 4 5)) (1..5).each{|x| List(1,2,3,4,5).foreach{ x => p x println(x) } }
  • 12.
    C++でのユーザー定義構文(1)  statementを引数に取るマクロを書き、 組み込みの構文要素に置換する #define foreach(v, c, s) ¥ for(typeof(c.begin()) it=c.begin(); it!=c.end(); it++){ ¥ v = *it; ¥ s ¥ } … vector<int> v; v.push_back(1); … foreach(int x, v, { cout<<x<<endl; })
  • 13.
    問題点  簡単に書けて、扱いやすいが、 かっこ悪い  foreach(, , {}) ↑ 括弧の中に{}があるのが耐えられない }) って… foreach(int x, v) { cout<<x<<endl; }  のように書きたい
  • 14.
    C++でのユーザー定義構文(2)  statementが後ろに来て整合性が取れる ようなマクロを書く #define foreach(v, c) ¥ for (bool b=true; b; ) ¥ for (v; b; b=false) ¥ for(typeof(c.begin()) it=c.begin(); it!=c.end() && (v=*it, true); it++) … vector<int> v; v.push_back(1); … foreach(int x, v){ cout<<x<<endl; }
  • 15.
    C++でのユーザー定義構文(2)  この書き方は美しいが、 記述力を制限される  既存の制御構文の後ろに文を置けるだけ
  • 16.
    後処理  synchronized(v){ … } のようなことを実 現するには、{ … } の後に処理をさせな ければならない
  • 17.
    forを使う方法  後続の文より後に処理をさせる方法  forを使う #define synchronized(v) ¥ for (bool b=true; ¥ b && (v.lock(), true); ¥ b=false, v.unlock()) #define synchronized(v) ¥ for (bool b=true; b; ) ¥ for (scoped_lock lk(v); b; b=false)  break, continueが食われてしまう
  • 18.
    ifを使う方法  ifの中で変数を宣言すれば、 後続の文の後にデストラクタを動かせる #define synchronized(v) ¥ if (scoped_lock lk=scoped_lock(v))
  • 19.
    ifを使う方法(2)  コピー可能なクラスを定義する  trueを返すoperatorbool() を定義する  デストラクタを定義する class hoge { … }; if (hoge h=hoge()) stat; 処理の流れ  hoge::hoge()  hoge::operator bool()  stat  hoge::~hoge()
  • 20.
    改良  operator bool() はfalseを返したほうがいい #define synchronized(v) ¥ if (scoped_lock lk=scoped_lock(v)); ¥ else
  • 21.
    活用例:CGI DSL classmy_cgi : public cgi{ public: void run(){ html__{ head__{ title__{ text__("タイトル"); } } body__{ a__{ href__ = "http://kzk9.net/blog/"; text__("super blog!"); } br__; } } } };
  • 22.
    議論  hoge(…){ …} の形で、 前処理→処理→後処理 の形のユーザー 定義構文が書けることが分かった  だがそれだけだろうか?  こういうことができるということは、 つまりどういうことなのだろうか
  • 23.
    見えざる継続  継続とは  ある計算過程のある瞬間における、その過 程の未来全体を表すもの、あるいは計算過 程の実行スナップショットと説明される。 (Wikipedia)  Schemeなどでは言語レベルでサポート  継続は、実行中のどんな計算機プログラ ムにも存在する
  • 24.
    関数呼び出しと継続  関数呼び出しとはすなわち、継続をとも なう手続きへのジャンプ  関数が呼ばれた= 関数から返った後の継続が手に入る int foo(){ return 1; } foo()が呼ばれる際に渡される継続 ・返された値に2を書けて void bar() ・それを表示する { cout<<foo()*2<<endl; 実際のところC++では、 } ・スタック ・リターンアドレス の対として表現される
  • 25.
    ユーザー定義構文では  hoge::operator bool() が呼ばれた瞬間の 継続に注目する class hoge { … }; if (hoge h=hoge()) stat; 処理の流れ  hoge::hoge()  hoge::operator bool() ← trueを返せば stat → hoge::~hoge() → …  stat falseを返せば hoge::~hoge() → …  hoge::~hoge() なる継続
  • 26.
    継続を取り出す  callee save レジスタ、スタック、リター ンアドレスを取り出す cont get_ret_cont(){ regs=get_callee_save_regs(); stack=get_stack(); ret=get_ret_addr(); return (regs, stack, ret); }
  • 27.
    継続が取り出せるなら  operator bool()にて、継続を取り出し、 さらに文実行後の継続を設定し、stat後 の処理の流れを決められる hoge::operator bool() { cont c=get_ret_cont(); next=…; c(true); } hoge::~hoge() { next(); }
  • 28.
    文を後ろに置く=継続を渡す  つまるところ、if (hogeh=hoge()) { … } は 後ろの文を引数に関数を呼び出しているの と同じである  Schemeの高階関数や、Rubyのブロック構 文と同じことができる
  • 29.
    例:スレッド  thread{ … } でスレッドを立てる 後続の文が終了するとスレッド終了 mutex m; int n; int main() { thread{ for (int i=0; i<10; i++) synchronized(m) n++; } for (int i=0; i<10; i++) synchronized(m) n--; }
  • 30.
    実装 class thread_forker{ …}; thread_forker::operator bool(){ cont c=get_ret_cont(); pthread_create(&tid, NULL, bind(apply_cont, c, true)); c(false); } thread_forker::~thread_forker(){ pthread_exit(NULL); }
  • 31.
  • 32.
    まとめ  C++でユーザー定義構文は意外ときれい に作れるんじゃなかろうか  ポータブルな実装ができるかどうか
  • 34.
  • 35.
    シリアライザとは  データをバイト列に変換したり(シリアラ イズ)、バイト列からデータに変換したり (デシリアライズ)するもの  クラスやコンテナの内容をファイルに保存 したり、ネットワーク越しに転送したりす るのにとても便利
  • 36.
    boost::serialization  Boostに入っているシリアライズライブ ラリ class hoge{ private: string a; vector<int> b; friend class boost::serialization::access; template <class Archive> void serialize(Archive &ar){ ar & a & b; } };
  • 37.
    boost::serializationの特徴  シリアライズとデシリアライズを 共通のコードで記述  テンプレートでディスパッチ hoge h; text_oarchive oa(cout); oa << h; // text_oarchiveを引数にserializeが呼ばれる hoge g; text_iarchive ia(cin); ia >> h; // text_iarchiveを引数にserializeが呼ばれる
  • 38.
    仕組み  serialize()関数をオーバーロード template <class Archive> Archive &operator &(Archive &ar, T &v){ serialize(ar, v); return ar; } void serialize(text_iarchive &ar, int n){ ar.read_int(n); } void serialize(text_oarchive &ar, int n){ ar.write_int(n); }
  • 39.
    シリアライズするというのはどうい うことか?  クラスのシリアライズ  メンバの列挙  コンテナのシリアライズ  データの列挙
  • 40.
    テンプレートでのシリアライズ  serializeがテンプレートメンバ関数  シリアライズ・デシリアライズを 共通化  色々なシリアライザ(text, binary)に適用 可能  拡張可能  → シリアライザじゃなくてもいいので は?
  • 41.
    型を書きだす  データの代わりに、型を書きだしてみる class type_oarchive{ … }; template <class T> void serialize(type_oarchive &oa, T &v){ // デフォルト oa.enter_struct(); serialize(oa, v); oa.leave_struct(); } void serialize(type_oarchive &oa, int &n){ // 特殊化 oa.add(new int_type(true, sizeof(int))); } …
  • 42.
    型を書きだす(2)  コンテナ型は特殊化する template <class T> void serialize(type_oarchive &oa, vector<T> &v){ oa.add(new array_type(get_type<T>()); } template <class K, class V> void serialize(type_oarchive &oa, map<K, V> &v){ oa.add(new map_type(get_type<K>(), get_type<V>()); }
  • 43.
    型を書きだす(3)  型情報取得関数 template <class T> type_info *get_type() { T v; type_oarchive oa; oa << v; return oa.get(); }
  • 44.
    Serializable = Reflectable 値の代わりに型を書きだすことにより、 Serializableなクラスは(部分的には) Reflectableなクラスとなる  Reflectableなクラスは明らかに Serializableに出来るのでこれはそう おかしな話ではない  つまり、(部分的には)Serializableと Reflectableは等価である
  • 45.
    応用例:RPC // シグニチャ RPC_PROC(add, int(int,int)) // メソッド定義 RPC_GEN(calc, add) // クラス定義 // サーバ // クライアント int add(int x, int y){ return x+y; } hoge_client cli(“localhost”, 12345); int main(){ cout<<cli.call_add(1,2)<<endl; hoge_server serv; // ↑ 3が返ってくるはず serv.set_add(&add); serv.serv(12345, 10); }
  • 46.
    RPC説明  適当に関数のシグニチャを定義して、 それに合う関数をセットする  関数の引数、返り値はソケットでやり取 りされる → シリアライズ可能でなけれ ばならない
  • 47.
    RPC:クライアントコード生成  言語bindingを自動で生成  C++ソースを読み込む  パーズするのは大変すぎる  g++にやらせる  リフレクションする  RPCの型情報が取れる  好きなコード生成できる  RPCでやり取りするデータはすべてSerializable でなければならないはずなので、すべて何もし なくてもリフレクション出来るはずである for
  • 48.
    RPC:テスト生成  おなじ原理でRPCテスト用のWebサー バーをC++コードから自動で生成
  • 49.
    まとめ  Serializableなデータ構造は思ったより 有用だ
  • 51.
    概要  ストリームを扱う計算が テンプレートで速くなるという話
  • 52.
    ストリーム  (ここでは)何かデータの列  配列とか  外部メモリ上のデータとか  ストリームに対する演算  map  filter  fold  sort  merge  など…
  • 53.
    例  ストリームに対する演算の繰り返し vector<int> v, w, x; for (int i=0; i<100; i++) v.push_back(i); remove_copy_if(v.begin(), v.end(), back_inserter(w), is_even()); transform(w.begin(), w.end(), back_inserter(x), div2());  中間配列ができる  配列を二回なめる  外部メモリを扱う時に特に顕著 for
  • 54.
    1パスでやるには  一か所に書けばいい vector<int> v, x; for (int i=0; i<100; i++) v.push_back(i); for (int i=0; i<v.size(); i++) if (v[i]%2==0) x.push_back(v[i]/2);  モジュラリティが低い  さっきのように書いて、こうなってほしい
  • 55.
    遅延ストリーム  それぞれの処理で一気に配列を舐めるのが いけない → 遅延させてやればいい template <class T> class stream{ … }; template <class S, class F> class filter_stream{ public: typedef S::elem_type elem_type; filter_stream(S &s, F f=F()): s(s), f(f) {} elem_type get(){ for (;;){ elem_type r=s.get(); if (f(r)) return r; } } };
  • 56.
    遅延ストリーム(2) template <classS, class F> class map_stream{ public: typedef S::elem_type elem_type; map_stream(S &s, F f=F()): s(s), f(f) {} elem_type get(){ elem_type r=s.get(); return f(r); } }; stream<int> s; filter_stream<typeof(s), is_even> t(s); map_stream<typeof(t), div2> u(t); while(!u.empty()) cout<<u.get()<<endl;
  • 57.
    インライン化  テンプレートで書かれているので インライン化できる  1パスでできる stream<int> s; filter_stream<typeof(s), is_even> t(s); map_stream<typeof(t), div2> u(t); u.get(); // => r=t.get(); return div2(r); // => int r; for (;;){ r=s.get(); if (!is_even(r)) break; } // return div2(r);
  • 58.
    yieldとの対比  C#でのyield class FilterStream{ public FilterStream( … ) { … } public IEnumerator<int> GetEnumerator(){ foreach (int t in s) if (!f(t)) yield return i; } …  ループを回すだけのようなことなら 同じようにできる
  • 59.
  • 60.
    まとめ  C++の謎テクをいくつか紹介  ブロック=継続  Serializable=Reflectable  コルーチン=遅延評価  pficommon近日リリース予定