2016-02-29 120 views
3

我有一個老式的帶有純C接口的.dll文件,在完成某些工作時需要調用回調函數。回調需要的是void (f*)(char* arg)將成員函數傳遞給需要回調的C接口

我在尋找一招,通過C++函數對象有這麼回調與「this」指針存儲的地方,像綁定,但簡單綁定不起作用

爲了說明這個調用:C接口:

typedef void (f*)(char* param) Callback; 
void registerCallback(Callback c); 

用法在C++:

class A 
{ 
    void func1() 
    { 
     registerCallback(std::bind(&A::func2, _1, this)); // obviously doens't work 
    } 

    void func2(char* param) 
    { ... } 
}; 
+4

你的C API應該在某處使用'userdata' /'cookie' /'context'參數。如果這不是作者應該受到死刑的話。 – milleniumbug

+2

'std :: bind'返回不可轉換爲函數指針的未指定類型的值。對於一般策略,請參閱https://blogs.msdn.microsoft.com/oldnewthing/20140127-00/?p=1963/ Lambdas不會工作,因爲只有無捕獲的lambda可以衰減到函數指針。 – milleniumbug

+1

registerCallback使用了怎樣的'char * param'? – SergeyA

回答

3

,爲了使這項工作有一個類的實例和類的成員函數的唯一方法,我我知道的是:

  1. 存儲一個指向全局變量中的對象的指針。
  2. 註冊非會員功能。
  3. 使用全局變量從非成員函數調用成員函數。
class A 
{ 
    void func1(); 

    void func2(char* param) 
    { ... } 
}; 

// Global pointer 
A* aPtr = NULL; 

// Non-member function. 
// extern "C" is probably needed if the older DLL is expecting 
// an unmangled C function pointer. 
extern "C" void globalFunc2(char* param) 
{ 
    if (aPtr == NULL) 
    { 
     // Deal with error 
    } 
    else 
    { 
     aPtr->func2(param); 
    } 
} 

void A::func1() 
{ 
    aPtr = this; 
    registerCallback(globalFunc2); 
} 
+0

根據聲明的回調方式,可能需要爲'globalFunc2()'指定'extern「C」' 。您可能還想在運行多線程時添加有關不使用此類實現的警告,特別是如果庫有可能從另一個線程調用回調函數r線程。 –

+0

@AndrewHenle,可能需要使用'extern「C」'。由於我沒有完全理解這些後果,所以我拋棄了多線程問題。 –

+0

爲什麼在這裏需要extern「C」?根據我的理解,它只需要通過名稱通過某個「GetProcAddress」 – Dmitry

1

我看到它的方式,問題的核心是讓回調函數知道A對象的func2調用哪個(registerCallback與註冊的實體)的調用者。

據我所知,您的問題基本上有一堆A對象,並且您只希望這些對象中的一些A對象在發生回調事件時執行它們各自的func2。但是,當回調事件發生時,回調函數的調用者不知道應該調用誰。既然你提到它是一箇舊的.dll,我認爲我們不能進入並改變registerCallback的工作方式,但我們需要存儲註冊爲回調的A對象。

因此,

class Informer { 
public: 
    static void InformAllMembers(char* param) { 
    for(auto& a : m_Members) { //Inform all As registered with me 
     a->func2(param); 
    } 
    } 
    static void Register(A* a) { 
    m_Members.push_back(a); 
    } 
private: 
    static std::vector<A*> m_Members; 
}; 
std::vector<A*> Informer::m_Members; 
... 

registerCallback(Informer::InformAllMembers); 

A a; 
Informer::Register(&a); 

注意:您將不得不處理有些地方的註冊A對象被破壞的情況下,並從Informer註銷它們。

0

這僅僅是@ RSahu解決方案的一個替代方案,它使用A類中的靜態成員。恕我直言,是爲functionnally一樣的全局變量,但至少你在你的類命名空間包容:

class A 
{ 
    static A* current_obj; 

public:  
    void func1() 
    { 
     current_obj = this; 
     registerCallback(func3); 
    } 

private: 
    void func2(char* param) 
    { ... } 
    static void func3(char *param) { 
     if (NULL == current_obj) { 
      // error ... 
     } 
     current_obj->func2(param); 
    } 
... 
}; 

而且如上文所示,註冊功能可以是私有的類,因爲只有功能需要被稱爲外部在這裏func1

但它也有同樣的問題:你只能同時註冊一個對象。

0

也許我不理解問題,但爲什麼不建立一個簡單的包裝來保存「this」指針。因此,應用程序將使用此回調(或lambda)

void RegisterCallback_withthis(char* arg, void* thisptr) 

一次只能向DLL註冊一個回調函數。所以,一個單一的全球thisptr是好的。

static void* thisptr; 
void RegisterCallback(char* arg) 
{ 
    RegisterCallback_withthis(argc, thisptr); 
} 

應用程序必須在註冊回調時設置thisptr。

thisptr = this; 
相關問題