しすぎじゃない?
変更点
まずは、C++17になっていたら文字列をstd::string_view
で受け取ったり、toml::value
をstd::string_view
で初期化できるようにした。どうやらC++17で使っている人がいたっぽいので、特に要求はなかったがあったほうがいいかなと思って入れた。C++17の新機能の中でもstring_view
はかなり待ち望まれていた機能でもあるし、使っている人も比較的多いのではないか。
あとは、値を取り出すときに、簡単に書くためにas_integer()
とかas_string()
みたいなのを入れた。これは複雑な変換はできないが、今ちょっとだけ中身を覗きたい、というときに仰々しくならないので便利だ。
const auto v = toml::parse("sample.toml"); if(v.is_integer() && v.as_integer() == 42) // あっさり { // ... }
あまり順当でない変更としては、コメントを取得できるようにしたことだろう。以下のようなファイルがあったとき、
# comment for a. a = 42 b = 3.14 # comment for b.
const auto data = toml::parse("sample.toml"); std::cout << data.at("a").comment(); // "# comment for a." std::cout << data.at("b").comment(); // "# comment for b."
のようにして関連するコメントを取り出せる。
こんな機能足したら重くなるんじゃない? というのは当然の疑問だと思うが、重くはならない。ここまでの実装からコスト無しに導入できる機能だったので、それと要望もあったので、入れた。
前に記事を書いたとおり、toml11では、各toml::value
がファイル内のどの領域に対応するかを保持している。これは前にも記事を書いたとおり、エラーメッセージのためだ。値が文字列であることを期待して変換しようとしたが失敗した、というとき、何行目で何が書かれているのか、型は何で何への変換に失敗したのかなどをパースが終わってからも出力するためだ。以下のように。
terminate called after throwing an instance of 'toml::type_error' what(): [error] toml::value bad_cast to integer --> example.toml 3 | title = "TOML Example" | ~~~~~~~~~~~~~~ the actual type is string
ここでは、整数だと思って取り出そうとしたら文字列だった、というエラーが表示されている。これを表示するにはファイル名と部分文字列と行数を覚えておく必要がある。だがそれを全てのtoml::value
が覚えるのはしんどいので(配列とかだと部分文字列が超長くなることもある)、実際にはファイルの全内容とファイル名を入れた構造体へのshared_ptr
と、その中で何文字目から何文字目かだけをtoml::value
は覚えている。ファイル中での位置はパースの時も使っているから、廃物利用だ。地球に優しい(無駄なコピーを発生させないことによってエネルギー消費を抑えているため)。
で、ここでファイル内の全データとその値が対応する場所を覚えているということは、その周囲にあるコメントを検索できるということでもある。value.comment()
を呼ぶと周囲のコメントを探し、見つけた分だけ返してくれる。よってtoml::value
にはデータメンバは一切追加されていない。また、パース時もコメントは今までどおりガン無視している。もしコメントが必要なら後で探せばいいので。パース時はガン無視しているのに後でコメントを取得することもできるというのはちょっとおもしろい。
今後の方向性
問題点としては、これは単にコメントを取得できるようにしただけであって、コメントを編集することはできないということだ。編集できないならあまり意味もないかと思い、シリアライズの時も気にしていない。ついでに、値のデータが変更された時は、基本的にファイル内のどの領域に対応していたかという情報は失われる。なので、まあまだあまり役には立たないだろう。
将来的には、シリアライズの際にコメントを色々できるようにしてみたい気持ちは少しある。だが、コメントは必要無い場合が多かろうとも思うので、必要のない場合はオフにできる機能であってほしい。だがtoml::value
の構造を変えるとなるとかなり大きな変更ということになるので、メジャーアップデートとして出したい。
そうなってくると他にもやりたいことが色々ある。例えば、初期はキャメルケースを使っていたが途中でスネークケースに切り替えたなどの経緯で滅茶苦茶になっている命名を統一したい(今の所、キャメルケースのAPIは一切触らずに使えるようになってはいるので、最近知って使ってみようとしてくれている人は「そうだったの?」くらいの話かも知れない)。命名を直すのはとてもやりたくてウズウズしているが確実にユーザーコードをぶっ壊すのでやってこなかった。メジャーアップデートにしかならないが、名前を直すだけのメジャーアップデートって……と二の足を踏み続けている。だが便利機能がいっぱい入るならついでにやるのはありかもしれない。移行準備は色々やらないとダメだろうが……(片方をdeprecate
しておいて両方使える期間を取るとか)。
あと、toml::parse
がtoml::table
を返す(toml::value
ではなく)のも修正したい。これは初期からのことで、TOMLファイルがテーブルに一対一対応するからなのだが、今やvalue
はただのtagged-union
ではなく、ファイル内の位置などの情報を持ってしまっているし、その情報を利用するために「toml::value
をテーブルだと見当を付けて値を検索する」というような機能を足してきているので、逆に不自然になってしまった。今になって見るとtoml::table
は、ただのunordered_map<key, value>
のエイリアスなので、多機能なtoml::value
に比べて前面に押し出すにはあまりにも簡素すぎる。当時は全てが簡素だったのでバランスも取れていたのだが、toml::value
の機能が増えて重点がそっちに移ってしまった。
他には、コンテナの種類を選べないのも気になってはいた。std::unordered_map
は定数時間でルックアップができるが、メモリをそれなりに使う。std::map
の方が良い場面もそれなりにある、というような記事は見るし、かなり使う要素でもあるので、そうなると選べるようにしておきたい。そうしておけば、flat_map
が入った時も対応が簡単だ。その場合、まさしく型を変えるのだから、これはテンプレートを使う以外の方法はない。あと、配列を格納するときにstd::vector
を使うことに文句を付ける人は少ないかも知れないが、一応std::deque
とかに切り替えられるようにするのはありかも知れない。
だが、普通に考えるとtoml::value
がテンプレート引数を取るとは思わないので、そこは隠さないといけない。デフォルトテンプレート引数はこういうときは不便で、C++17以前だとtoml::value<> v = ...
のような奇妙な宣言になってしまう。なのでデフォルト引数でうまく隠すことはできない。隠すとしたら、std::string
のようにエイリアスを使うしかない。つまり、toml::basic_value<...>
を作っておいて、using toml::value = toml::basic_value<...>
のようにするのだ。
そうしておけば、コンテナの種類も変えられるし、コメントをstd::string
のようにして別に保持するかどうかも決められる。アロケータも変えられるようにしてもいいのかもしれない。toml::value
にはデフォルトの型を設定する必要があり、それを決めるのも難しくはあるが、まあ何とかなるだろう。型パラメータの異なるtoml::value
間の変換は難しい話だ。それに関してはサポートしなくてもいいかもしれない。他の問題としては例えば、toml::array
やtoml::table
のような、コンテナの型に依存してしまう型名はややこしいことになるが、デフォルトの型を入れておくということでよいのではないだろうか。
指定はどうするのが良いかだが、これは決まっている。関数テンプレートの場合はデフォルトパラメータを使うときに<>
が必要無いことを利用して、toml::parse
をテンプレートまみれにすればよい。auto
で受け取っていれば簡単だ。
const auto v = toml::parse("sample.toml"); // デフォルト const auto v = toml::parse<toml::comment::preserve, std::map, std::deque>("sample.toml"); // カスタム
のようにすればいいのだ。
こうすれば、デフォルトでいい場合は今まで通りの書きかたで動き、カスタマイズしたい場合は必要に応じて設定を色々できるということになり、悪くない辺りに落ち着くのではないだろうか。むしろ何で最初からこうしなかったんだろ。多分パーサを書くところで手一杯だったんだろうな。
とまあ、やりたいことは沢山あるのだが、やることが結構種類があって手が回らないというのと、疲れているのかわからないが「よっしゃ書くぞ!」となる時間が短くなってきている気がするのが懸念材料だ。上記のを全て盛り込むととても大きな変更になるが、バージョン3、いつ出せるだろう。