一個多月沒寫 C++ 程式了,最近有個小案子,需要將 "para.dat" 之類的二進位檔案讀取出來,
在對其中的參數做些設定。
"para.dat" 中內涵
#pragma pack(push,1)
typedef struct _PARA
{
short open_interval;
short unlock_interval;
..... // bla bla..
}PARA;
#pragma pack(pop)
之類的資料結構。
基本的想法是做個選單
====================
1) load para // 宣告一個 struct Para 並將 para.dat Load 到 Para
2) change open_interval 3) change unlock_interval
4) bla bal...
w) write back to para.dat // 將變更後的 Para 寫回 para.dat=====================
然後用 switch(n){
case 1:
loaod_para();
break;
....
}
這樣的方式做出來。
我大概花了半小時的時間寫好~
就想用 Command 設計模式 (將函式產生和函式呼叫解耦) 來重寫,
用 Command 設計模式的話,就可以用 macro 的方式,一次執行多道指令,並且實作 undo 的功能
我就用了 boost lambda 的方式實作 (參考 beyond the C++ standard library)
以下是我在寫的時候推導的一些範例,分享一下
// copy from << beyond c++ >>
class command
{
boost::function<void()> f_;
public:
command() {}
command(boost::function<void()> f) : f_(f) {}
void execute()
{
if(f_)
{
f_();
}
}
template<typename Func>
void set_function(Func f)
{
f_ = f;
}
bool enable() const
{
return f_;
}
};
// use map to invoke command
// such as command_map["1"].execute()
map<string,command> command_map;
void menu()
{
cout << "==============================================\n";
cout << "command demo";
cout << "==============================================\n";
cout << "1. direct say hello\n";
cout << "2. say hello using _1 \n";
cout << "3. say what you type\n";
cout << "h. greet at someone\n";
cout << "w. write to file 'new_para.dat'\n";
}
// this add concrete command to map: command_map
void init_command()
{
command_map["1"] = command( (cout << constant("hello")) ); // 基本的 lambda 用法 command_map["2"] = command( bind( &(cout << boost::lambda::_1), "hello" ) );
// 因為 command 只接收 void() ,所以要用 bind 將參數綁定,也就是有這個函式讓 command 模式很好用
command_map["3"] = command( bind(& (cin >> boost::lambda::_1 , cout << boost::lambda::_1) , string() ) );
// 這一步我試了很久,原本用 (*istream_iterator<string>(cin)) 得到輸入
// 但這樣會在 init_command 時就叫你輸入而不是在選單選 3 時。
// 後來發現用 , 這個運算子將 expression 分開就解決這個問題
// 我還試過 bind(&(cout << (cin >> _1)))
int n = 1;
command_map["3"] = command(
bind((switch_statement(
boost::lambda::_1,
case_statement<0> ( var(cout)<< "I" ),
case_statement<1> ( var(cout)<< "am" ),
case_statement<2> ( var(cout)<< "a" ),
case_statement<3> ( var(cout)<< "boy" ),
default_statement (var(cout)<< "error people")
)
),n)
);
// 這個我也試了很久 = ="
}
int main()
{
init_command();
while(1)
{
system("cls");
menu();
cout << "choose:";
string i; cin >> i;
command_map[i].execute(); // invoker
system("pause");
}
return 0;
}
有了這些基礎設施,就可以實作像是 macro , undo 的動作了
若是比較複雜的選項,就用函數實作在 assign 給 command_map
eq.
void write_log()
{
.... bla bla
}
void init_command()
{
command_map["w"] = command(write_log);
}
若是函式需要參數勒,
eq. void write_log(string filename){...}
用 bind 就對了 XD
我 try 的方法先從小的開始寫起,
例如:
(cout << _1) ("hello");
這個 work 後,因為他有一個參數,所以我用 function 來試
boost::function<void (int) > f = (cout << _1);
這也 OK 後,利用 bind 去參數
boost::function<void () > f = bind(&(cout << _1),"hello");
這個 compile 能過後,就可以加入 command_map 啦
這是我使用 Lambda 配合 Command DP 的小小感想
給大家參考
謝謝^^