2010-01-03 101 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的实例之间。如果情况并非如此,则可能需要修改此解决方案。