8月に横浜で行われたC++ Dayでさらっと流されたCRTP。
普通ならコンパイルタイムポリモーフィズムとかで使われるんだろうけど、別の使い道ないかな?と思ってやってみたのがCSVの任意項目ピックアップ。
コメントもない汚いソースだけど、折角やってみたので。
今後使うかもしれないクラスだからこっちは標準ライブラリ以外使用しない
csvreader.h
#include
疲れたからこっちはboost使う
csvreader.cpp
#include
#include
#include
#include
#include
#include
#include
#include "csvreader.h"
const std::string null_holder::null_str = std::string();
// ベースクラス
class base : public property_manager {
MAKE_PROPERTY(Name)
MAKE_PROPERTY(Kind)
MAKE_PROPERTY(Price)
public:
DECLARE_STRING_METHOD_BASE(base)
};
// 食べ物
class food : public base, public property_manager {
MAKE_PROPERTY(Weight)
DECLARE_STRING_METHOD(food, base)
};
// お酒
class liquor : public food, public property_manager {
MAKE_PROPERTY(Number)
MAKE_PROPERTY(Proof)
DECLARE_STRING_METHOD(liquor, food)
};
// たばこ
class cigarette : public base, public property_manager {
MAKE_PROPERTY(Number)
MAKE_PROPERTY(Nicotine)
DECLARE_STRING_METHOD(cigarette, base)
};
// ベースクラス用呼び出し定義
BEGIN_DECLARE_CALLER(base)
MAKE_CALLER_ELEMENT(base, Name)
MAKE_CALLER_ELEMENT(base, Kind)
MAKE_CALLER_ELEMENT(base, Price)
END_DECLARE_CALLER(base)
// 食べ物用呼び出し定義
BEGIN_DECLARE_CALLER(food)
MAKE_CALLER_ELEMENT(food, Weight)
END_DECLARE_CALLER(food)
// お酒用呼び出し定義
BEGIN_DECLARE_CALLER(liquor)
MAKE_CALLER_ELEMENT(liquor, Number)
MAKE_CALLER_ELEMENT(liquor, Proof)
END_DECLARE_CALLER(liquor)
// たばこ用呼び出し定義
BEGIN_DECLARE_CALLER(cigarette)
MAKE_CALLER_ELEMENT(cigarette, Number)
MAKE_CALLER_ELEMENT(cigarette, Nicotine)
END_DECLARE_CALLER(cigarette)
typedef boost::shared_ptr object_holder;
typedef std::vector object_array;
typedef std::vector string_array;
typedef boost::char_separator cvs_separator;
typedef boost::tokenizer > cvs_tokenizer;
typedef std::pair string_pair;
inline string_array get_cvs_tokens(const std::string& str)
{
cvs_separator separator(",", 0, boost::keep_empty_tokens);
cvs_tokenizer tokens(str, separator);
return string_array(tokens.begin(), tokens.end());
}
int main(int argc, const char* argv[])
{
if(argc < 2) return 0;
std::ifstream ifs(argv[1]);
if(!ifs.is_open()) return 0;
std::string line;
getline(ifs, line);
string_array tags = get_cvs_tokens(line);
object_array objs;
boost::shared_ptr tmp;
while(!getline(ifs, line).eof()) {
tmp.reset(new base());
string_array params = get_cvs_tokens(line);
string_array::iterator itt = tags.begin();
for(string_array::iterator it = params.begin(); it != params.end(); ++it, ++itt) {
tmp->set_by_string(*itt, *it);
}
std::string kind = tmp->get_Kind();
if(kind == "Food") tmp.reset(new food());
else if(kind == "Liquor") tmp.reset(new liquor());
else if(kind == "Cigarette") tmp.reset(new cigarette());
itt = tags.begin();
for(string_array::iterator it = params.begin(); it != params.end(); ++it, ++itt) {
tmp->set_by_string(*itt, *it);
}
objs.push_back(tmp);
}
for(object_array::iterator it = objs.begin(); it != objs.end(); ++it) {
for(string_array::iterator itt = tags.begin(); itt != tags.end(); ++itt) {
std::string value = (*it)->get_by_string(*itt);
if(value.empty()) continue;
std::cout << *itt << std::ends << ":" << value << std::endl;
}
std::cout << std::endl;
}
return 0;
}
このプログラムに以下のCSVを食わせる
Name |
Kind |
Price |
Weight |
Number |
Proof |
Nicotine |
Comment |
鯛 |
Food |
1000 |
100 |
1 |
|
|
お魚 |
牛肉 |
Food |
2000 |
300 |
2 |
|
|
お肉 |
峰 |
Cigarette |
1500 |
0 |
5 |
|
0.9 |
高級煙草 |
氷結果汁 |
Liquor |
380 |
350 |
1 |
11 |
|
世界一美味い |
パチンコ |
Gamble |
priceless |
1 |
4 |
1 |
1 |
|
実行結果
Name :鯛
Kind :Food
Price :1000
Weight :100
Name :牛肉
Kind :Food
Price :2000
Weight :300
Name :峰
Kind :Cigarette
Price :1500
Number :5
Nicotine :0.9
Name :氷結果汁
Kind :Liquor
Price :380
Weight :350
Number :1
Proof :11
Name :パチンコ
Kind :Gamble
Price :priceless
各クラスがサポートしているパラメータのみが出力されている。
よしよし。
ヘルパーマクロの書式はMFCへのオマージュw