2010-01-03 117 views
1

我試圖將成員函數作爲參數傳遞。基本上我有一個叫做AboutWindow類,和標題的外觀,因爲這(修剪爲了簡潔):C++(C3867)將成員函數傳遞給函數調用

class AboutWindow 
{ 
private: 
    AboutWindow(void); 
    ~AboutWindow(void); 

public: 
    int AboutWindowCallback(XPWidgetMessage inMessage, XPWidgetID inWidget, long inParam1, long inParam2); 
}; 

和在源我試圖通過AboutWindowCallback構件用作(指針/參考)的功能。

它看起來是這樣的:

XPAddWidgetCallback(widgetId, this->AboutWindowCallback); 

,但我得到了以下智能感知警告:

一個指向綁定功能只能 被用來調用函數

是否可以將成員函數傳遞給XPAddWidgetCallback。請注意,它必須是該特定實例的特定函數,因爲在AboutWindowCallback函數中使用了this關鍵字。

在此先感謝!

+0

您需要一種將指向AboutWindow的指針傳入方法的方法。你不能直接這樣做,但你可以通過XPSetWidgetProperty()來完成,見下文。 – 2010-01-03 20:22:26

回答

3

我假設你在這裏使用x平面。

不幸的是XPAddWidgetCallback預計的

int callback(XPWidgetMessage inMessage, XPWidgetID inWidget, 
      long inParam1, long inParam2) 

形式的回調你提供一個類的成員函數。如果這是一個全球性功能,它將會起作用。所以這個:

class AboutWindow 
{ 
private: 
    AboutWindow(void); 
    ~AboutWindow(void); 
}; 

int AboutWindowCallback(XPWidgetMessage inMessage, XPWidgetID inWidget, 
         long inParam1, long inParam2); 

是對的。

然而,這不是很好必須有全球性功能,如noted in this thread在X平面。

爲了避免這種情況,請查看隨x平面提供的Wrapper類示例「XPCWidget.cpp」,或者使用tr1/boost bind來包裝回調。

XPAddWidgetCallback(std::tr1::bind(&AboutWindow::AboutWindowCallback, this)); 

您也可以使功能有問題static

class AboutWindow 
{ 
private: 
    AboutWindow(void); 
    ~AboutWindow(void); 
    static int AboutWindowCallback(XPWidgetMessage inMessage, XPWidgetID inWidget, 
       long inParam1, long inParam2); 
}; 
+0

嗨Kornel。絕對使用X-Plane SDK!好的觀察。我認爲最好的選擇是讓它變成靜態的。 – 2010-01-03 18:31:54

+3

實際上靜態成員函數是不允許的。沒有爲靜態成員函數定義調用約定。您需要爲回調方法使用「C」函數(因爲調用約定已定義)。你碰巧碰到像gcc這樣的編譯器對靜態方法使用相同的調用約定和「C」函數一樣。 – 2010-01-03 18:32:51

+0

嗯?你的意思是不允許的?我一直在使用它們。 – 2010-01-03 19:32:10

1

由於各種原因,您無法傳遞非靜態(即綁定)成員函數的地址。你可以只通過該this指針代替,或使一個函數對象存儲此this指針,知道調用哪個方法,即:

struct CallAboutWindowCallBack{ 
    CallAboutWindowCallBack(AboutWindow* that) 
     : m_that(that){ 
    } 

    int operator()(XPWidgetMessage msg, XPWidgetID wdg, long p1, long p2){ 
     return m_that->AboutWindowCallBack(msg, wdg, p1, p2); 
    } 

    AboutWindow* m_that; 
}; 

則:

XPAddWidgetCallback(widgetId, CallAboutWindowCallBack(this)); 
+0

問題在於我無法更改XPAddWidgetCallback函數簽名,它是我正在使用的SDK的一部分。 – 2010-01-03 18:18:09

+0

然後,函數對象是唯一的解決方案,如果你真的想傳遞一個綁定的成員函數。 – James 2010-01-03 19:02:37

0

當你傳遞一個函數(或者一個成員函數),你本質上是傳遞了代碼開始的地址。這不是綁定到一個特定的實例(即,同一類的兩個實例不會有不同的代碼),所以你不能真正傳遞特定實例的版本。另外,由於C++的實現方式,在多態情況下無法獲得任何版本的調用。

話雖這麼說,有可能是更多的面向對象的方法來達到你想要而不訴諸指向成員函數做什麼。詳情請查閱C++ FAQ的第33.2節。

+0

OP使用的SDK不允許他做更多的OO;> – 2010-01-03 18:26:51

+0

我在更新之前回復了... :) – Uri 2010-01-03 18:36:22

1

你可以得到一個成員函數的地址,但你需要給它分配給成員函數指針:

int (AboutWindow::*func)(XPWidgetMessage, XPWidgetID, long, long); 

func = &AboutWindow::AboutWindowCallback; 

成員函數指針只是一個指向成員函數的指針,但對該實例一無所知。要調用它,則需要提供對象的實例:

AboutWindow aw; 
(aw.*func)(msg, id, l1, l2); 

AboutWindow *paw = &aw; 
(paw->*func)(msg, id, l1, l2); 

因此,成員函數指針與常規函數指針不兼容。除非您正在調用的代碼知道您正在傳遞成員函數,否則不能使用它們。

您需要做的是使用非成員函數作爲成員函數的thunk。例如:

int thunk(XPWidgetMessage msg, XPWidgetID id, long l1, long l2) 
{ 
    paw->AboutWindowCallback(msg, id, l1, l2); 
} 

這裏的問題是如何訪問您正在使用的對象。你可以使用全球性的,雖然這不是很乾淨。理想情況下,這個API可以使用兩個long中的任何一個作爲上下文指針。在這種情況下,您將指針傳遞給AboutWindow實例,後來找回來:

int thunk(XPWidgetMessage msg, XPWidgetID id, long l1, long l2) 
{ 
    AboutWindow *paw = reinterpret_cast<AboutWindow *>(l2); 
    paw->AboutWindowCallback(msg, id, l1, l2); 
} 
+0

OP的問題是XPAddWidgetCallback以'int callback(XPWidgetMessage inMessage,XPWidgetID inWidget,long inParam1,long inParam2)'作爲參數。 – 2010-01-03 18:25:57

1

通常採取的回調被傳遞給回調函數的至少一個用戶指定的參數。你只需要把它作爲指向對象的指針。然後你需要一個使用這個指針的C回調函數並調用正確的方法。

不幸的是XPlane似乎沒有這個功能。
但是,您可以使用可以在回調中使用的小部件註冊一個值。

不幸的是它沒有使用void *參數,但是從技術上講,它不是可移植的(但實際上它可以工作,儘管你應該在你的編譯系統中添加一個檢查)。

extern "C" int Call_AboutWindowCallback_AboutWindow(XPWidgetMessage inMessage, XPWidgetID inWidget, long inParam1, long inParam2); 

int Call_AboutWindowCallback_AboutWindow(XPWidgetMessage inMessage, XPWidgetID inWidget, long inParam1, long inParam2) 
{ 
    long awptr = XPGetWidgetProperty(inWidget,1101,NULL); 
    if (awptr != NULL) 
    { 
     AboutWindow* aw = reinterpret_cast<AboutWindow*>(awptr); 
     aw->AboutWindowCallback(inMessage,inWidget,inParam1,inParam2); 
    } 
} 


int main() 
{ 
    AboutWindow aboutWin; 

    // Create your widget. 

    XPSetWidgetProperty(inWidget,1101,reinterpret_cast<long>(&aboutWin)); 
    XPAddWidgetCallback(inWidget,Call_AboutWindowCallback_AboutWindow); 

    // Use Your Widget. 
} 

請參閱有關XPWidgetPropertyID的文檔以查看要使用的編號。我隨機使用了1101種你應該做的適當的研究:看到這裏http://www.xsquawkbox.net/xpsdk/mediawiki/XPWidgetPropertyID

1

因爲你有XPWidgetID,你可以使用全局映射來分派回調。

static std::map<XPWidgetID, AboutWindow*> mapping; 

static int AboutWindowCallback(XPWidgetMessage inMessage, XPWidgetID inWidget, long inParam1, long inParam2) { 
     return mapping[inWidget]->AboutWindowCallbackMemberFn(inMessage, inWidget, inParam1, inParam2); 
} 

要添加,你只需要的rember填充地圖第一

mapping[widgetId] = this; 
XPAddAddWidgetCallBack(widgetId, &AboutWindowCallback); 

我假設XPWidgetID可以作爲一個地圖鍵和回調,有一個1對1的對應窗口小部件id和AboutWindow的實例之間。如果情況並非如此,則可能需要修改此解決方案。