看板 C_and_CPP 關於我們 聯絡資訊
一個多月沒寫 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") ) ),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 的小小感想 給大家參考 謝謝^^ -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 115.80.107.231 ※ 編輯: spider391 來自: 115.80.107.231 (09/29 23:18)
loveme00835:推認真!! 雖然我個人覺得用function+lambda function 09/29 23:52
loveme00835:會比較直覺 09/29 23:52