看板 C_and_CPP 關於我們 聯絡資訊
其實很早就想貼出這段程式,其中程式寫了一段落了,卻一直擺著沒再去想他, 看到最近相關位址轉換議題討論熱烈,也貼出來給大家一個參考。 為什麼要取得結構或類別中定義的成員函式位址? 主要就是為了回呼 (callback),藉想能回呼到某物件或結構成員函式上去。 callback 實在是大型模組化程式中,相當方便模組間階層組合的設計方案之一。 將主程序中的某個函式位址,傳遞給某一個處理模組,當該模組處理出現某狀態, 可以透過所傳遞的函式位址,直接呼叫主程序,告知該模組發生了什麼事。 好,誠如前面所說的,若確定一定得要傳遞某結構或類別的成員函式, 也就是說,將模組的運作通知直接由該成員函式接手,怎辦? 我想第一步驟就是得先正確取得成員函式位址,於是,我做了以下實驗。 (程式焦點將集中在取得成員函式指標,所以請恕刪減回呼實作的部份) 平台:slackware 14.0, 編譯器:gcc version 4.7.1 編譯指令:g++ -o test test.cpp << test.cpp >> /* 檔案開始 */ #include <stdio.h> /* 模組中所定義的回呼函式原型 */ typedef void (* MYCALLBACKFUNCTION)(int); /* 某結構 */ struct MyStructure { void MyFunction(int iResult) { printf("Result = 0x%08X\n", iResult); } }; /* 實驗處理程序 */ int main(void) { /* 回呼函數指標 */ MYCALLBACKFUNCTION pMyCallbackFunction = NULL; /* 取得成員函數位址 */ void (MyStructure::*pMyFunction)(int) = &MyStructure::MyFunction; /* 轉成空指標 */ /* 嘗試方法一:void * 轉型,有警告 */ void *pPointer = (void *)pMyFunction; /* 嘗試方法二:硬轉,一樣有警告 */ void *pPointer = reinterpret_cast <void *> (pMyFunction); // 以上警告訊息都是: // 從「void (MyStructure::*)(int, int)」 // 轉換到 「void*」[-Wpmf-conversions] /* 嘗試方法三:網路上查到的一個方法 */ void *pPointer = (void *&)pMyFunction; // 啥?編譯器安靜了?!有興趣的大大們可否勞煩提供原因嗎? /* 轉成函數指標,準備稍候傳遞給模組 */ pMyCallbackFunction = (MYCALLBACKFUNCTION)pPointer; /* 那麼,所有位址列印一下看看 */ printf("pMyCallbackFunction => 0x%08X\n", pMyCallbackFunction); printf("pPointer => 0x%08X\n", pPointer); printf("pMyFunction => 0x%08X\n", pMyFunction); printf("&MyStructure::MyFunction => 0x%08X\n", &MyStructure::MyFunction); /* 呼叫?結果正確嗎? */ pMyCallbackFunction((int)pPointer); return 0; } /* 檔案結束 */ 上述程式碼中三種轉 void *pPointer 方法都各自編譯後執行。 其中 void *pPointer = (void *)pMyFunction; 方法的執行結果: pMyCallbackFunction => 0x08048678 pPointer => 0x08048678 pMyFunction => 0x08048678 &MyStructure::MyFunction => 0x08048678 Result = 0x08048678 然後是... void *pPointer = reinterpret_cast <void *> (pMyFunction); 方法的執行結果: pMyCallbackFunction => 0x08048678 pPointer => 0x08048678 pMyFunction => 0x08048678 &MyStructure::MyFunction => 0x08048678 Result = 0x08048678 最後是... void *pPointer = (void *&)pMyFunction; 方法的執行結果: pMyCallbackFunction => 0x08048656 pPointer => 0x08048656 pMyFunction => 0x08048656 &MyStructure::MyFunction => 0x08048656 Result = 0x08048656 也給大家參考一下,這個關於成員函式位址轉換實錄。 但是,當關於使用回呼,我還是照老方法,回呼到一般的函式上。 因為我發現這個程式中 MYCALLBACKFUNCTION 當修改想傳遞兩個以上的參數時, 此時就出現問題了,成員函式所接收參數值全部亂掉,我想大概是參數順序的問題, 但就算嘗試想設 __stdcall 或 __cdecl 在結構中成員函式敘述上, 也不確定怎麼寫才對,因為再來牽涉的語法實在也不是非常熟。 自此之後,乾脆取居中的作法,結構中放置的是函式指標,拿去指向一般函式, 然後再來把該函式指標送交給模組去呼叫算了。 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 123.240.167.99
SeamusBerloz:這篇似乎貼得太晚,大家都舉出相當多的實例了... 08/27 23:10
LPH66:嘛, 也就是因為這樣搞麻煩一堆所以才有 std::function 08/27 23:14
littleshan:參數值當然會亂掉,因為成員函式有一個隱藏的參數this 08/27 23:14
LPH66:就只是把這些東西包成一個 functor 拿來用而已 08/27 23:14
littleshan:「成員函式非函式,有個參數叫this」麻煩默念十次 08/27 23:15
SeamusBerloz:押韻耶!...函式,...this ...函式,...this ...函 08/27 23:26
SeamusBerloz:式,...this 呼呼...函式,...this ...函式,...this 08/27 23:26
SeamusBerloz: ...函式,...this! 08/27 23:26
Feis:大舌頭喔~ 08/27 23:27
SeamusBerloz:呵呵,看到好東西,趕快好好記住先呀~ 08/27 23:39
descent:(void *&)pMyFunction void*& 是在轉什麼? 08/28 16:03
Feis:應該是一種騙編譯器的作法類似 *(void **)&pMyFunction 08/28 22:49