2016-04-15-01-ストリーム入力のその後
>> Site top >> weblog >> 月別アーカイブ >> 2016年04月のlog >> 2016-04-15-01-ストリーム入力のその後
最終更新日付:2016/04/15 01:00:00
ストリーム入力のその後
2016 年 04 月 15 日
さて、しばらく御無沙汰になってしまった CL-IOSTREAM の話。とりあえず現状はこんなんなってますよ、というか検討段階がふらふらしていますよ、というお話。
前回の最後には、以下のようなコードを提示して、場所を指定するリストに対しても動作しなければならん、みたいなことを書いた。
(_>> cin (car lst) (cadr lst))
型を指定する、という意味においては、前回とほぼ同様に、(場所 型) というリストを指定するイメージになる。美しくはないがこれが限界だろう。ただし、型指定子はやっぱりキーワードにすることにした。これなら、2要素のリストで cadr がキーワードならば型指定子とみなす、でひとまずイケそうだからだ。
(_>> cin ((car lst) :string) ((cadr lst) :integer))
で、ここからが今日の本題。前回書いた時点では、_>> マクロは複数の operator_>> 総称関数呼び出しに展開され、個々の operator_>> はストリームから読み込んだ値を返す、という方式を考えていた。これなら、「場所」自体は operator_>> に渡す必要がないし、シンプルで良いと考えたからだ。イメージとしては、以下のようになる。
(_>> cin ((car lst) :string) ((cadr lst) :integer)) ;=> (progn ; (setf (car lst) (operator_>> cin :string)) ; (setf (cadr lst) (operator_>> cin :integer)) ; cin)
しかし、これだといくつかマズい点がある。まず、途中でファイル終端に到達した場合が問題になる。次に、これは微妙な点だが、C++ の iostream では、それぞれの >> 演算子はストリームオブジェクトを返すのだ。異なる挙動を仕様とした場合に、これがどう影響するか正直まだ把握しきれていない。特に、ユーザ定義のマニピュレータでできることが制限されてしまうようではいけない。そこで、operator_>> はストリームオブジェクトを返す、という点は動かせない仕様と考えることにした。
そうなると、operator_>> にはなんとかして「場所」を与えなければならないが、一方でこれは総称関数でなければ具合が悪い。そうなると、クロージャを与えるしかない。これが現在の結論だ。結果、operator_>> は以下のような宣言となる。
(defgeneric operator_>> (stream setter val type-spec))
stream パラメータは istream クラスのオブジェクトで、まぁとりあえず入力ストリームのことだ。setter はクロージャで、値の設定をするために使用する。だからこれはパラメータを1つとる関数だ。そして val は指定された「場所」の値を渡す。これは、マニピュレータの対応や、なんらかのユーザ定義のオブジェクトに対するストリーム入力で使うことになるだろう。最後に、type-spec はキーワードシンボルで、operator_>> を特定化する最大のポイントとなる。基本型については _>> マクロがコンパイル時点で解決するため、これが CL-IOSTREAM ライブラリのレベルで特定化されることは(マニピュレータを除けば現時点では予定は)ない。
具体的にどうなるか、というと、以下のようになるイメージだ。もちろん、エラー処理なんかは省いているのでこんな簡単な話ではないが。
(defmethod operator_>> (stream setter val (type-spec (eql :foo))) (let ((v (read-foo-from stream))) (funcall setter v)) stream)
で、_>> マクロの展開イメージは、といえば以下のようになるものと(現時点では)考えている。
(_>> cin a (b :integer) (c :string) (d :foo)) ;=> (let ((#:is cin)) ; (setf #:is (__read-anything #:is (lambda (#:val) (setf a #:val)) a)) ; (setf #:is (__read-integer #:is (lambda (#:val) (setf b #:val)))) ; (setf #:is (__read-string #:is (lambda (#:val) (setf c #:val)))) ; (setf #:is (operator_>> #:is (lambda (#:val) (setf d #:val)) d :foo)) ; #is)
これでうまくいくだろうか? ちゃんとコードにしていないのであやふやだけれど、なんとかなるんではないかと思っている。引っ掛かっているのは、マニピュレータだ。上記でいくと、a がマニピュレータであれば、__read-anything がなんとかするということになる。しかし、現状ではマニピュレータは『defconstantされた関数』とするはずなので、コンパイル時点でやれることはもっとあるはずだ。そのあたりも含め、まだ検討は続く。
コメント
このページのタグ
Page tag : CL-IOSTREAM
Copyright(C) 2005-2019 project-enigma.
Generated by CL-PREFAB.