2014-04-07-01-リードマクロでinitializer_listを実現する
>> Site top >> weblog >> 月別アーカイブ >> 2014年04月のlog >> 2014-04-07-01-リードマクロでinitializer_listを実現する
最終更新日付:2014/04/07 01:00:00
リードマクロでinitializer_listを実現する
2014 年 04 月 07 日
C++ 0x11 では、initializer_list という仕組みが追加された。これにより、std::vector のようなものでさえ以下のように初期化できるようになった。
std::vector<int> v = { 0, 1, 2, 3, 4 };
で、これをどうにかして CL-STL に取り込みたいと思うわけだ。今回はそれについて試したことについて。
STL のヘッダファイルを探ってみて、ひとまず initializer-list というクラスを作成することにした。その定義はひとまず以下の通り。
(defclass initializer-list () ((data :type :simple-vector :initarg :data :reader __init-list-data)))
ご覧の通りシンプルだ。STL の initializer_list は size / begin / end などのメンバ関数を提供するので実質コンテナなのだが、上記の initializer-list は sinple-vector を内包しているだけ。これがどうやって begin / end をサポートするのかはまた改めて書くことにしよう。今回のポイントはリードマクロだ。
STL での表記に近付けようとすれば、リードマクロにご登場願うしかない。そこで、やっぱり On Lisp のユーティリティ関数の助けを借りることにする。ddfn と defdelim だ。これらを利用して、#{ ... } というリードマクロを以下のように定義した。
(onlisp/defdelim #\{ #\} (&rest items) `(make-instance 'initializer-list :data (coerce (list ,@items) 'simple-vector)))
結局のところ、#{ から } までの間にあるものを使って initializer-list クラスのインスタンスを作成している。list してから coerce で simple-vector に変換しているのは、initializer_list が変数を含むことができるからだ。つまり、STL でも以下のような記述が可能ということである。これは今回試してみるまで知らなかった。
int a = 10; std::vector<int> v = { 0, 1, a, 3, 4 };
で、昨日書いた方法と合わせると、initializer-list を取る vector の構築は以下のように書けることになるわけだ。
(let* ((a 10) (v (stl:new vector (#{0 1 a 3 4})))) (use v))
#{ ... } の周りにさらに括弧が1つ必要になるのが気持ち悪いが、これは別パッケージのクラス作成をサポートする必要性(詳細は昨日の記事を参照)から仕方がないだろう。
‥‥‥とまぁこんな感じで initializer_list を模倣したわけなのだけれど、引っ掛かっていることが1つある。リードマクロとパッケージの関係だ。上記の #{ ... } リードマクロはパッケージ CL-STL の内部で定義してはいるものの、このパッケージをインポートしなくても上記のように使える。それは、リードマクロがパッケージによる名前衝突の回避の仕組みの庇護下にないということだ(どうもそういうことらしい)。となると、ライブラリでリードマクロを使うというのはリスクが高い行為ということになるだろう。それをやってもいいものかどうか、そこで悩んでいる。
追記
この件について、Twitter で named-readtable なるものが役立ちそうだという情報を頂いた。改めて調べてみよう。
コメント
このページのタグ
Page tag : Common Lisp
Page tag : STLとその移植
Copyright(C) 2005-2021 project-enigma.
Generated by CL-PREFAB.