2016-04-15-01-ストリーム入力のその後 - project-enigma

2016-04-15-01-ストリーム入力のその後

>> Site top >> weblog >> 月別アーカイブ >> 2016年04月のlog >> 2016-04-15-01-ストリーム入力のその後

最終更新日付:2016/04/16 10:27:05


ストリーム入力のその後

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-2017 project-enigma.
Generated by CL-PREFAB.