代码之家  ›  专栏  ›  技术社区  ›  josef.van.niekerk

将成员函数传递给函数调用的C++(C38 67)

c++
  •  1
  • josef.van.niekerk  · 技术社区  · 15 年前

    我正试图传递一个成员函数作为参数。基本上,我有一个名为aboutwindow的类,它的头看起来如下(为了简洁而修剪):

    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关键字。

    事先谢谢!

    6 回复  |  直到 15 年前
        1
  •  3
  •   Kornel Kisielewicz    15 年前

    我想你在这里用的是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-Plane提供的包装类示例“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);
    };
    
        2
  •  1
  •   R Samuel Klatchko    15 年前

    可以获取成员函数的地址,但需要将其分配给指向成员函数的指针:

    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);
    }
    
        3
  •  1
  •   Logan Capaldo    15 年前

    您可以使用全局映射来调度回调,因为您具有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);
     }
    

    要添加回调,只需重新编写以首先填充映射

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

    我假设xpwidgetid可以用作映射键,并且在widget id和aboutwindow实例之间有1到1的对应关系。如果不是这样,您可能需要修改这个解决方案。

        4
  •  1
  •   James    15 年前

    由于各种原因,不能传递非静态(即绑定)成员函数的地址。你可以通过 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));
    
        5
  •  1
  •   Loki Astari    15 年前

    通常回调至少接受一个传递给回调函数的用户指定参数。你只需要把它作为指向对象的指针。然后需要一个使用此指针并调用正确方法的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

        6
  •  0
  •   Uri    15 年前

    当传递函数(或成员函数)时,实际上是传递代码开头的地址。它没有绑定到特定实例(即同一类的两个实例没有不同的代码),因此您不能真正传递特定实例的版本。此外,由于C++是如何实现的,您不能真正获得多态情况下调用的任何版本。

    也就是说,可能有更多的OO方法来实现您试图做的事情,而不需要使用指向成员函数的指针。检查第33.2节 C++ FAQ 详情。