C++でCSVを解釈しようと思ったのですが、いろいろ考えた結果、SAXのように処理することにした。
SAXはXMLパーサーのAPIの1つ。Simple API for XMLの略なのですが、確かにシンプルですね。
オブジェクトを設定しておいて、文書を読み込んだら、まずオブジェクトのstartDocumentメソッドを呼び出して、
開始タグを発見したらstartElementメソッド、テキストを発見したらcharactersメソッド、終了タグを発見したらendElementメソッド、最後にendDocumentメソッド。
という風にメソッドを呼び出して読み込んでいくわけ。
これは何の役に立つんだという話ですが、XML文書から必要な部分だけオブジェクトに落とすときに使います。
複雑な処理はできないけど、例えばfoo要素の子のname要素・addr要素の値を得たいとか、
そういうことを処理するのにはちょうどいいのかもしれない。
複雑な処理をしたければXML文書を一回読み込んで、それをツリー状にして処理できるDOM使え。
さて、そういうわけで読み取るオブジェクトのためのインターフェースを作ろうと思った。
やはり使うメソッドをインターフェースで定めて、これを実装していただこうと。
しかしC++にはインターフェースってないんですね。
ただしC++は多重継承できるので、クラスでインターフェース風のものを作ります。
class ICSVCatchable{
public:
virtual void BeginCSV(void) =0;
virtual void BeginRow(void) =0;
virtual void NewCell(std::string data) =0;
virtual void EndRow(void) =0;
virtual void EndCSV(void) =0;
};
virtualをつければ仮想メソッドになります。これでオーバーライドできるわけです。
まずこれは必要なのですが、これでは宣言したメソッドを定義しなくてはならなくなる。
そこで最後に=0をつけることで定義をしなくてよくなる。
C#で言うと、抽象メソッドと呼ばれるやつですね。あれは、継承された先で専らオーバーライドされるメソッドですから。
まぁそんなところです。
しかしJavaやC#のインターフェースとはほど遠いものであるのも確かです。
というのもなぜかICSVCatchableのインスタンスが作れてしまうんですね。
まぁあまり気にしてはいけませんね。
JavaやC#では性質ごとにインターフェースを用意して、必要に応じて実装するということが普通です。
例えばC#で繰り返し可能なオブジェクトはIEnumerableを実装するとかね。
ただC++ではそんな習慣がないんですよ。
繰り返し可能なオブジェクトはbeginとendのメソッドでイテレータがもらえるものだという程度。
そのイテレータも、++・*・!=の演算子をオーバーロードしているかというぐらいのものです。
こういうのダックタイピングというんですけどね。
なんでC++でこんなことができるのかといえばテンプレートを多用しているから。
けどテンプレート絡みのエラーはとても読みにくい。それを考えるとあんまり便利とは言えない気がする。
やはりこういうインターフェースを使う方がわかりやすいし、うまくやりやすいと思う。
そんなものいらんだろうという話もありますが、
しかし多重継承ではこういうことも予定されてますからね。
工夫してインターフェースを実現すれば使い道は多いと思います。
引数の型にICSVCatchableとか書くだけでいいんだからね。便利便利。