C++ (C3867) Passing a member function to a function call
I'm trying to pass a member function as an argument. Basically I have a class called AboutWindow, and the header looks as this (trimmed for brevity):
class AboutWindow
{
private:
AboutWindow(void);
~AboutWindow(void);
public:
int AboutWindowCallback(XPWidgetMessage inMessage, XPWidgetID inWidget, long inParam1, long inParam2);
};
and in the source I am trying to pass the AboutWindowCallback member function as a (pointer to / reference to) the function.
It looks something like this:
XPAddWidgetCallback(widgetId, this->AboutWindowCallback);
but I am getting the following IntelliSense warning:
A pointer to a bound function may only 开发者_开发知识库 be used to call the function
Is it possible to pass the member function to XPAddWidgetCallback. Note it has to be that specific function, of that specific instance, as inside the AboutWindowCallback function, the this keyword is used.
Thanks in advance!
I assume you're using x-plane here.
Unfortunately XPAddWidgetCallback expects a callback in the form of
int callback(XPWidgetMessage inMessage, XPWidgetID inWidget,
long inParam1, long inParam2)
You provided a class member function. It would work if this were a global function. So this :
class AboutWindow
{
private:
AboutWindow(void);
~AboutWindow(void);
};
int AboutWindowCallback(XPWidgetMessage inMessage, XPWidgetID inWidget,
long inParam1, long inParam2);
Would be correct.
However, it's not nice to have to have global functions for that, as noted in this thread on x-plane.
To circumvent that, have a look at the Wrapper class example "XPCWidget.cpp" that is supplied with x-plane, or use tr1/boost bind to wrap the callback.
XPAddWidgetCallback( std::tr1::bind( &AboutWindow::AboutWindowCallback, this ) );
You can also make the function in question static
:
class AboutWindow
{
private:
AboutWindow(void);
~AboutWindow(void);
static int AboutWindowCallback(XPWidgetMessage inMessage, XPWidgetID inWidget,
long inParam1, long inParam2);
};
You can get the address of a member function, but you need to assign it to pointer to member function:
int (AboutWindow::*func)(XPWidgetMessage, XPWidgetID, long, long);
func = &AboutWindow::AboutWindowCallback;
The member function pointer is only a pointer to the member function but knows nothing about the instance. To call it, you then need to provide an instance of the object:
AboutWindow aw;
(aw.*func)(msg, id, l1, l2);
AboutWindow *paw = &aw;
(paw->*func)(msg, id, l1, l2);
Because of this, member function pointers are not compatible with regular function pointers. Unless the code you are calling knows you are passing a member function, you can't use them.
What you will need to do instead is use a non-member function as a thunk to your member function. Something like:
int thunk(XPWidgetMessage msg, XPWidgetID id, long l1, long l2)
{
paw->AboutWindowCallback(msg, id, l1, l2);
}
The problem here is how to get access to the object you are using. You can use a global although that is not very clean. Ideally, this API gives you use of either of the two longs as a context pointer. In that case, you will pass the pointer to your AboutWindow instance and later get it back:
int thunk(XPWidgetMessage msg, XPWidgetID id, long l1, long l2)
{
AboutWindow *paw = reinterpret_cast<AboutWindow *>(l2);
paw->AboutWindowCallback(msg, id, l1, l2);
}
You can use a global map to dispatch the callback, since you have the 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);
}
To add a callback you just need to rember to populate the map first
mapping[widgetId] = this;
XPAddAddWidgetCallBack(widgetId, &AboutWindowCallback);
I'm assuming XPWidgetID can be used as a map key, and that there is a 1 to 1 correspondence between widget ids and instances of AboutWindow. If that is not the case, you may have to modify this solution.
For various reasons you can't pass around addresses of non-static (ie, bound) member functions. You could just pass the this
pointer instead, or make a function object that stores this this
pointer and knows which method to call, ie:
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;
};
then:
XPAddWidgetCallback(widgetId, CallAboutWindowCallBack(this));
Normally Callbacks take at least one user specified parameter that is passed to the callback function. You just need to make this the pointer to the object. You then need a C callback function that uses this pointer and calls the correct method.
Unfortunately XPlane does not seem to have this functionality.
But you can register a value with the widget that can then be used in the callback.
Unfortunately it doe's not use a void* parameter but a long so technically it is not portable (but realistically it will work, though you should add a check to your build system).
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.
}
See documentation about XPWidgetPropertyID to see what number to use. I used 1101 sort of randomly you should do the appropriate research: see here http://www.xsquawkbox.net/xpsdk/mediawiki/XPWidgetPropertyID
When you pass a function (or a member function), you are essentially passing the address of the start of the code. That is not bound to a specific instance (i.e., two instances of the same class would not have different code) so you can't really pass the version of a specific instance. In addition, because of how C++ is implemented, you can't really get whatever version would be invoked in polymorphic situations.
That being said, there are probably more OO ways to achieve what you're trying to do without resorting to pointers to member functions. Check sec 33.2 of the C++ FAQ for details.
精彩评论