2015-12-21 57 views
0

我試圖在下面的代碼,但該程序崩潰的工作:將回調函數傳遞給std :: map的最佳方式是什麼?

#include <iostream> 
#include <string> 
#include <map> 

using namespace std; 

typedef void (*callBackMethod)(string); 
class CTest 
{ 
private: 
    map<string, callBackMethod> mapMethod; 

    void testMethod(string msg) 
    { 
     cout << msg << endl; 
    } 
public: 
    CTest() 
    { 
     addFunction("AA", (callBackMethod) &CTest::testMethod); 
    } 
    void addFunction(string funName, callBackMethod methodName) 
    { 
     mapMethod[funName] = methodName; 
    } 
    callBackMethod getMethod(string funName) 
    { 
     auto fun = mapMethod.find(funName); 
     if(fun == mapMethod.end()) { return nullptr; } 
     return fun->second; 
    } 
    void runFunction(string funName) 
    { 
     getMethod(funName)("test"); 
    } 

}; 

int main() 
{ 
    CTest test; 
    test.runFunction("AA"); 

    return 0; 
} 

我有,我需要私有方法傳遞到地圖的要求。該程序編譯時出現警告:

converting from 'void (CTest::*)(std::__cxx11::string) {aka void (CTest::*)(std::__cxx11::basic_string<char>)}' to 'callBackMethod {aka void (*)(std::__cxx11::basic_string<char>)}' 

當我執行此操作時,它崩潰。

當我將回調方法移出它的工作類之外。我的要求是讓程序流動(隱藏需要添加到地圖的外部呼叫的方法)。

期待您的意見。

+1

爲什麼地球上你會認爲將一個指向成員函數的指針指向函數是個好主意? –

+0

@ T.C。那你有什麼建議?如果你願意,請修改代碼。 – programmer

+0

[如何將非靜態成員函數作爲回調函數傳遞?](http://stackoverflow.com/questions/2097738/how-to-pass-a-non-static-member-function-as- a-callback) –

回答

0

如果您需要指向CTest成員函數和自由函數,然後您可以使用std::function<void(std::string)>

#include <iostream> 
#include <string> 
#include <map> 
#include <functional> 

using namespace std; 

using callBackFunction = std::function<void(string)>; 

void testFunction(string msg) 
{ 
    cout << "[" << __PRETTY_FUNCTION__ << "] " << msg << endl; 
} 

class CTest 
{ 
private: 
    map<string, callBackFunction> mapMethod; 

    void testMethod(string msg) 
    { 
     cout << "[" << __PRETTY_FUNCTION__ << "] " << msg << endl; 
    } 
public: 
    CTest() 
    { 
     addFreeFunction("AA", testFunction); 
     addMemberFunction("BB", &CTest::testMethod); 
    } 
    void addMemberFunction(string funName, void(CTest::*methodName)(string)) 
    { 
     using std::placeholders::_1; 
     mapMethod[funName] = std::bind(methodName, this, _1); 
    } 
    void addFreeFunction(string funName, void(*methodName)(string)) 
    { 
     mapMethod[funName] = methodName; 
    } 
    callBackFunction getMethod(string funName) 
    { 
     auto fun = mapMethod.find(funName); 
     if(fun == mapMethod.end()) { return nullptr; } 
     return fun->second; 
    } 
    void runFunction(string funName) 
    { 
     getMethod(funName)("test"); 
    } 

}; 

int main() 
{ 
    CTest test; 
    test.runFunction("AA"); 
    test.runFunction("BB"); 

    return 0; 
} 

注意CTest必須根據要傳遞什麼類型的功能,因爲對於成員函數必須提供,這就是它在此被調用,this對象元素插入所述地圖以不同的方式例。這通過使用std::bind來實現。

+0

謝謝。你的代碼工作。但是現在隱藏了回調方法簽名。我還需要從main傳遞迴調方法。例如:void tst2(string s){cout <<「New」<< s;} ....以及main(){... test.addFunction(「BB」,&tst2); ...}編譯錯誤。有沒有更好的方法來處理這種情況。非常感謝。 – programmer

+0

所以你需要使用成員和非成員函數? –

+0

是的。我需要使用成員函數和非成員函數。 – programmer

0

既然你想使用成員變量,你需要在你的typedef指定不同的簽名:

在C++ Builder中的下面可以做:

typedef void(__closure *callBackMethod)(string); 

如果你這樣做,我不建議您保留一個智能指針指向該成員所屬的對象,以便您可以在調用該函數之前檢查該對象是否仍然有效,否則將導致該應用程序崩潰。

__closure關鍵字是C++ Builder的擴展來解決要求使用完全合格的成員名稱source

爲了應對全球和成員函數,我們有以下幾點:

typedef void(__closure *callBackMethodMember)(string); 
typedef void (*callBackMethodGlobal)(string); 

/* And then on 2 overloaded functions */ 
void addFunction(string funName, callBackMethodMember methodName) {} 
void addFunction(string funName, callBackMethodGlobal methodName) {} 
+0

'__closure'是什麼意思?它是標準的C++嗎? –

+0

我看到'__closure'是C++的C++ Builder擴展。所以如果你不使用C++ Builder,它將不可用。 [文檔](http://docwiki.embarcadero.com/RADStudio/XE8/en/C%2B%2B_Keyword_Extensions#closure) –

+0

但它有什麼作用?可能有一個標準的等值。 –

相關問題