2014-06-12-01-ファンクタの0x11対応
>> Site top >> weblog >> 月別アーカイブ >> 2014年06月のlog >> 2014-06-12-01-ファンクタの0x11対応
最終更新日付:2014/06/12 01:00:00
ファンクタの0x11対応
2014 年 06 月 12 日
C++11 では、functional の unary_function や binary_function は deprecated となっている。binder1st とかもだ。自分はかなり長いことこのことを見落していた。新しいコンテナとか、アルゴリズムとかに気をとられすぎていたのだ。しかし、ようやくとはいえ気がついた。気がついたからにはなんとかしなければならない。どうやってなんとかするか、はて。
そもそも Lisp は動的な言語なので、静的な型システムを利用したテンプレートのような仕組みはない。だから Common Lisp の CLOS のようなオブジェクトシステムは全て実行時解決だ。結果、unary_function などにあたるファンクタも仮想関数のような仕組みで実現していた。まぁざっくり、総称関数の宣言は以下のような感じだった。
(defgeneric unary-funcall (functor arg)) (defgeneric binary-funcall (functor arg1 arg2))
で、呼び出しの書法はまとめておきたかったので、以下のようにしていた。
(defmacro functor-invoke (func &rest args) (ecase (length args) (1 `(funcall (unary-funcall ,func) ,@args)) (2 `(funcall (binary-funcall ,func) ,@args)))
そもそも C++98 の頃、ファンクタといえば unary_function と binary_function しかなかったものだから、まぁこれで良かったのだ。そして、C++98 ではこれらはクラスとして存在していた。だから、自分はこうしていた。
(defclass unary-function (cloneable) ()) (defclass binary-function (cloneable) ())
そうそう、Lisp は当たり前のように何処にでも lambda 式が登場する。だから C++ の文脈で言うところのファンクタ同様、関数や lambda 式がストレスなく使えるようでなければならない。だから、先の2つの総称関数には、以下のような関数向けのバインディングがあった。
(defmethod unary-funcall ((functor function) arg) (funcall functor arg)) (defmethod binary-funcall ((functor function) arg1 arg2) (funcall functor arg1 arg2))
これで、関数でも lambda 式でも「ファンクタ」クラスのオブジェクトでも、同じように functor-invoke でコールできる。ただし、パラメータの数が1つまたは2つの場合だけに限るけれど。
さて、C++ あるいは STL を詳しく御存知ない Lisper の方々はこう言うかもしれない。「レキシカルクロージャが作れるんだから、わざわざファンクタをクラスとして用意する意味がわからん」と。ごもっとも。レキシカルクロージャは恐ろしく強力だ。自分も Lisp を学び始めて以来、大好きになっている。しかし、自分にはクロージャを「深くコピーする」方法がわからない。間違っていたら恥ずかしいのだけれど、クロージャが閉じ込めているレキシカルコンテキストそのものをコピーして、その時点の内容がコピーされた別々のクロージャを作成する方法がわからないのだ。今のところ、言語そのものがそういうことをサポートしているわけではないのだろうと思っている。そして、STL のファンクタの挙動を模倣するには、このファンクタのコピーがかかせないのだ。そんなわけで、CL-STL には cloneable というクラスがあり、総称関数 clone というものがある。これは何であれ、そのオブジェクトを「コピー」するためのものだ。まぁこのあたりのことは脱線になるので深追いはやめておこう。
話を戻そう。C++11 では、何やらテンプレートが大きく拡張されたらしく、任意の数のテンプレート引数を取るテンプレートが書けるようになった。これを使って、ファンクタも unary_function とか binary_function とかケチなことを言わず、任意の数のパラメータを取る operator() を提供していればファンクタとして扱えるようになった‥‥‥ぽい。
自分はこれで困ってしまった。それまでの考え方のまま、これを素直に実現すると以下のようになるだろう。
(defgeneric unary-funcall (functor arg)) ; deprecated. (defgeneric binary-funcall (functor arg1 arg2)) ; deprecated. #+cl-stl-0x11 (defgeneric functor-funcall (functor &rest args))
しかし、総称関数のパラメータに &rest args とかしたくなかったのだ。パフォーマンスへの影響が大きそうだったからだ。杞憂かもしれないのだけれど、ライブラリというものはそれがどういう使われ方をするかわからない。だからできる限り、その‥‥‥なんだ、「いい感じ」にしておきたいわけだ。
そんなわけで、自分はできるだけレストパラメータを使わずにファンクタの拡張をしようと努力している。ここから先が本題なのだけれど、続きは明日にしよう。
コメント
このページのタグ
Page tag : Common Lisp
Page tag : STLとその移植
Copyright(C) 2005-2021 project-enigma.
Generated by CL-PREFAB.