【问题标题】:Cast pointer to member function to normal pointer将指向成员函数的指针转换为普通指针
【发布时间】:2010-11-18 01:02:12
【问题描述】:

目前我有一个这样的类,为简单起见被缩短:

class MyClass {
    public: 
        MyClass();
        void* someFunc(void* param);
}

现在我需要调用这种类型的函数(不是任何类的成员,不幸的是我无法更改)但无论如何我都需要调用它:

void secondFunc(int a, int b, void *(*pCallback)(void*));

现在我需要传递一个实例的 someFunc 的地址。

一个不工作的样本:

MyClass demoInstance;
// some other calls
secondFunc( 1, 2, demoInstance::someFunc() );

我也尝试过像这样的演员:

(void* (*)(void*)) demoInstance::someFunc;
reinterpret_cast<(void* (*)(void*))>(demoInstance::someFunc);

如何使用类的成员函数作为参数调用此函数,以便此函数可以将其用作回调?

任何想法或评论表示赞赏。感谢致敬 托比亚斯

【问题讨论】:

  • 必须提供一个实例;否则它是未定义的行为。你想完成什么。
  • 什么指针作为参数传入回调函数?如果你能以某种方式指定它,你应该能够创建一个struct Foo { MyClass *a; void *b; };,然后使用void *MyCallback(void *foo_) { Foo *foo = static_cast&lt;Foo*&gt;(foo_); return foo-&gt;a-&gt;someFunc(foo-&gt;b); } 作为回调。

标签: c++ pointers casting


【解决方案1】:

你不能直接调用成员函数。 成员函数指针与函数指针的类型不同。

您需要以某种方式将其包装在兼容的函数中。但是,如果您的外部函数(以函数指针作为参数的函数)不是可重入的并且不提供额外的参数供函数指针使用,则您将无法传递成员所在的实例函数运行,因此您实际上无法进行调用。

【讨论】:

  • 好吧,外部函数是库的一部分,我看到的唯一方法是拥有这些函数指针的静态数组...所有函数由我自己定义,调用时只需调用相应对象的回调函数。真的不可能吗?
  • @Atmocreations:是的,这真的不可能。为了了解原因,请注意除了void* 参数之外,someFunc 还需要this 的值。就好像它有两个参数,其中一个只能通过调用成员函数来提供。 secondFunc不调用成员函数,代码应该如何判断this
  • @Atmocreations:谢天谢地。我希望你不会说,“但它可以在 Python/Lisp/其他有闭包的语言中工作”;-)
  • @Steve:C++ 现在有闭包。至少,这是苹果告诉我的。 ;p
  • @Jonathan:但是 C++0x lambda 的类型与函数指针的类型不同,我认为块也是如此,不是吗(从未使用过 clang)。所以这里不好,除非它们延长它们的升值的生命周期,否则它们无论如何都不是“正确的”闭包。它总是有闭包,你可以在函子中手动存储(引用)一大堆局部变量。
【解决方案2】:

C函数和C++成员函数的区别在于C函数使用cdecl调用约定,而成员函数使用thiscall调用约定(你不能甚至拿他们的地址!)。

据我了解,您实际上希望 secondFunc() 调用特定类实例的成员函数(我们称之为 this)。那么,特定类的所有实例的成员函数的地址都是相同的。为了将指针传递给对象,您需要一个侧通道。在这种情况下,它可能是静态变量。或者,如果您想要 MT 支持,则必须使用线程本地存储 (TLS),

这需要每个SomeFunc-type 成员进行一次回调,但无论如何您都需要一个调度程序。

【讨论】:

  • 静态SomeFunc_callback_wrapper如何访问实例obj
【解决方案3】:

有一种方法可以做到这一点。由于 C++ 名称被破坏,您不能直接将其用于非静态函数。但是,由于非静态函数与 C 函数具有相同的签名,您可以直接将它们用作回调。因此,对于非静态函数,您可以使用静态函数包装器。 This page 详细解释了这种方法。

【讨论】:

  • 首先谢谢。看起来很聪明,稍后会检查详细信息。
【解决方案4】:
class MyClass 
{
public: 
    MyClass();
    void* someFunc(void* param);
};

void* callback(void*)
{
    MyClass instance;
    instance.someFunc(0 /* or whatever */);
}

void foo()
{
    secondFunc( 1, 2, callback);
}

【讨论】:

    【解决方案5】:

    查看此示例:

    #include <iostream>
    
    class MyClass 
    {
    public: 
        MyClass()
        {
        }
    
        void* someFunc(void* param)
        {
            std::cout << "someFunc" << std::endl;
            return (void*)0;
        }
    };
    
    typedef void* (MyClass::*MemFun)(void*);
    
    void secondFunc(int a, int b, MemFun fn)
    {
        MyClass* ptr = 0;
        // This is dangerous! If the function someFunc do not operate the data in the class.
        // You can do this.
        (ptr->*fn)(0);
        std::cout << "Call me successfully!" << std::endl;
    }
    
    int main()
    {
        secondFunc(1, 2, &MyClass::someFunc);
    
        system("pause");
    
        return 0;
    }
    

    【讨论】:

      【解决方案6】:

      之前没有看到不能更改第二个功能的部分。

      用指向成员函数和对象的指针定义一个结构:

      struct MyData {
         MyStruct  *myInstance;
         (void *)(MyStruct::myFunction)(void *data);
         void * dataPointer ;
      }
      

      创建一个可以调用正确方法的函数:

      void *proxyFunc( MyData *data)
      {
        return (data->myInstance->*(data->myFunction))(data->dataPointer);
      }
      

      然后调用函数2为:

      MyData  dataP = { someInstance, &MyStruct::someFunc, &dataPtr };
      secondFunc(proxyFunc, &dataP);
      

      【讨论】:

        【解决方案7】:

        使用联合可以很容易

        #include <iostream>
        
        using namespace std;
        
        class Data {
        public:
            Data(const int data) : num(data) {
        
            }
        
            void printData() const {
                printf("%d", this->num);
            }
        
        private:
            int num;
        };
        
        union Test {
            decltype(&Data::printData) member_fun;
        
            void (* normal_function)(...);
        };
        
        int main() {
            Test t;
            t.member_fun = &Data::printData;
            Data data(10);
            (*t.normal_function)(&data);
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-22
          • 2016-10-30
          相关资源
          最近更新 更多