2014-06-17-02-ファンクタの0x11対応 - 4 - project-enigma

2014-06-17-02-ファンクタの0x11対応 - 4

>> Site top >> weblog >> 月別アーカイブ >> 2014年06月のlog >> 2014-06-17-02-ファンクタの0x11対応 - 4

最終更新日付:2014/06/17 23:50:00


ファンクタの0x11対応 - 4

2014 年 06 月 17 日

ファンクタの C++11 対応の話、まだまだ続く。今回は、bind が一体どんなもので、なぜそれに悩まされているかについて。上手く説明できる自信はないのだけれど、書いてみよう。

 

C++11 よりも前の C++ には、binder1st とか binder2nd というファンクタのアダプタがあった(というか、deprecated ながら存続している)。これは、binary_function と第一引数、または第二引数に「バインド」する値を取ることで unary_function を作りだす。以下の例では面倒なので auto とか bind2nd とか使っているのは大目に見て欲しい。bind2nd というのは binder2nd オブジェクトを簡単に作成するための関数テンプレートだ。

#include <functional>
#include <iostream>

int main( void ) {
    auto fnc = std::bind2nd( std::minus<int>(), 10 );
    std::cout << "fnc( 13 ) = " << fnc( 13 ) << "." << std::endl;
    return 0;
}

 

やっていることと言えば、引算を行う minus というファンクタの第二パラメータを 10 にバインドし、与えたパラメータから 10 を引くようなファンクタを作成しているだけだ。lambda 式が使える Lisp の世界から見るとなんだか罰ゲームでもさせられているような気分になる。ちなみに、そんな罰ゲームっぽさをそのまま CL-STL の世界に移してくると以下のようになる。

(let ((func (stl:bind2nd (stl:new :minus ()) 10)))
   (format t "(func 13) => ~A." (stl:functor-invoke func 13)))

 

それはさておき、これらのバインダーの問題点はと言えば、それが binary_functionunary_function しか想定していないことだ。まぁ STL 自体が単項関数と二項関数しか使わないとはいえ、もっとたくさんのパラメータを取るような関数に任意数のパラメータをバインドすることで unary_function を作り出したりとかができない。しかも、C++11 ではファンクタ自体が単項/二項という縛りから開放されたのだから、バインダーももっと柔軟にならなければ、というわけだ。

そんなわけで、C++11 では binder1st とかは deprecated となり、bind テンプレートが追加された。もう面倒臭いので CL-STL のイメージで説明させてもらおう。まず、パラメータを3つ取ってそれらの掛け算をする mult3 というワザとらしい関数を用意しよう。

(defun mult3 (a b c)
  (* a b c))

 

で、「パラメータを1つ取り、それに 2 と 3 をかけた値を返す関数」が欲しいとしよう。「lambda 式書けば?」というのは言いっこナシでね。実際問題自分だってそうするよ。でも STL 移植となったらやらないわけにはいかないでしょ。

話を戻そう。CL-STL では現在以下のように書ける。cl-stl:bind はマクロで、最初のパラメータに与えた関数(もしくはファンクタ)に後続の『プレースホルダ』およびパラメータをバインドした新しいファンクタを作成する。ここで作成されるファンクタオブジェクトは非公開クラスのインスタンスだ。

(stl:bind #'mult3 :1 2 3)

; => #<CL-STL::__BINDED-EXPR {10065EA7A3}>

 

『プレースホルダ』は、コロンに数文字だけが後続するキーワードで、上記の例では :1 がそれにあたる。プレースホルダは、作成されるバインダーオブジェクトに渡された「何番目のパラメータか」を示している。ちなみにこれは1から始まる。そして、プレースホルダ以外のものは全てバインドされるパラメータだ。だから上記で作成されるバインダーオブジェクトは、パラメータを1つだけ取り、それに 2 および 3 という3つの値を使って mult3 をコールする、ということになる。

さて、ではお待ちかねの macroexpand-1 をしよう。(stl:bind #'mult3 :1 2 3) は、こんなふうに展開される。

(make-instance 'cl-stl::__binded-expr
               :bindee #'mult3
               :adapter
               (let ((#:prm2-36 2)
                     (#:prm3-37 3))
                 (lambda (#:fnc-34 #:arg1-35)
                   nil
                   (cl-stl:functor-invoke #:fnc-34
                                          #:arg1-35
                                          #:prm2-36
                                          #:prm3-37))))

 

‥‥‥今回は bind マクロの実装までやりたかったのだけれど、続きは次回以降ということで。

 

コメント

このページにコメントする

 

このページのタグ

Page tag : Common Lisp

Page tag : STLとその移植

 

 


Copyright(C) 2005-2017 project-enigma.
Generated by CL-PREFAB.