C++で手動DIしようとしたらハマった件


 最近、DI覚えました。真に作りたい機能に集中して作れるのはとってもいいもんです。

 で、ちょっとしたCSVパーサーを作ってた時のこと。CSVを読み込むにも、ファイルからなのかWEB上からなのか、そもそも文字コードの問題もあるわけです。

 そんなことをいちいちパーサーに書きたくない。そこで、読み込み部分はIRepositoryというインターフェースにして疎結合を目指そうとしました。疎結合あるあるですね。

そんな感じでザッと書いたコードがこんな感じ。

#include <iostream>
using namespace std;


class IRepository
{
public:
    virtual string GetText() const = 0;
};

class CSVParser
{
public:
    CSVParser(const IRepository &repository){/*なんやかんや*/}
    const char *Value(int row,int col){return "なんやかんや";}
};

class TempRepo : public IRepository
{
public:
    string GetText() const {return string("ねこ,にゃーん\ncat,meow");}
};

int main(void){
    CSVParser parser(TempRepo());
    cout << parser.Value(0,1); //(1)
    return 0;
}

 具体的なパーシングは省略しましたが、適当なCSV文字列を一旦TempRepoに持たせて、その文字列のカンマや改行が適切に解釈され、(1)の部分で「にゃーん」と表示されるはず。
 しかし、この行で「式にはクラス型が必要です。」のようなエラーが出てビルドさせてくれません。つまりparserがCSVParser型と認識してないんです。

ところで、クラスを引数なしコンストラクタで初期化して使うときは()を書いてはいけません。

CSVParser parser(); //エラー
CSVParser parser; //OK

()を書くと関数宣言とみなされ、意味合いが違ってきます。

症状としては近いのですが、今回は引数ありです。うーん。

CSVParser parser = CSVParser(TempRepo());

これは通りました。

じゃ、試しにキャストしてみるか。

CSVParser parser((const IRepository &)TempRepo());

通った

ここに強く深く刻む わすれられぬように