【问题标题】:Use class member functions as callbacks?使用类成员函数作为回调?
【发布时间】:2011-10-25 07:00:25
【问题描述】:

我需要一个成员函数来传递给第三方外部方法:

box_self_intersection_d(mycallback);

box_self_intersection_d 是第三方外部静态方法,我无法修改。 mycallback 是我想将它传递给box_self_intersection_d 的方法,它是一个类函数,并且正在访问这个类中的一些成员(对这个类和mycallback 具有完全控制权)

我是否可以将类成员函数用作回调而不将它们声明为静态函数?

编辑:mycallback 的签名是(const box &boxA, const box &boxB),其中box 是来自第三方提供商的特殊类。

signature for box_self_intersection_d

void box_self_intersection_d(RandomAccessIterator begin,RandomAccessIterator end,Callback callback)

【问题讨论】:

  • 这种方法有意义吗?请记住,非静态成员函数仅在类instance 中才有意义。因此,您需要以某种方式将成员函数 实例都传递给回调,但是哪个实例?
  • 如果方法是固定的并且无法更改,那么问题中的一些信息就是该方法的签名是什么,因为它决定了问题的约束。
  • 这个问题有解决办法吗?我面临着完全相同的问题,但尽管一些答案被投票赞成,但没有一个被标记为已接受的解决方案。

标签: c++


【解决方案1】:

如果函数box_self_intersection_d接受一个函数作为参数,而mycallback是一个类MyClass的方法,你可以使用boost::bind

box_self_intersection_d( boost::bind( &MyClass::mycallback, myClassInstance ) );

其中myClassInstance 是类MyClass 的实例。

【讨论】:

  • 出于好奇,这仅适用于static T MyClass::mycallback(MyClass* self)(实现为return self->mycallback())还是仅适用于更常见的T MyClass::mycallback()?我对 boost 不熟悉,所以我想知道您是否需要额外的包装器(从我在您需要的在线文档中可以看到)。
  • @bitmask:我不确定这个问题,但如果你只提到boost::bind,是的,它可以绑定一个不带参数和/或静态的非静态成员函数成员函数,它采用指向 MyClass 的类型指针的单个参数。我担心这不是问题的真正答案,因为box_self_intersection_d 的签名似乎无法更改,我怀疑它需要一个函子。
  • @bitmask 是:boost::bind 两者都适用。特别是带有非静态成员函数void MyClass::mycallback()(我认为原来的问题是这样的)
【解决方案2】:

如果回调接受用户定义数据的 void*,您可以使用静态包装函数将 void* 参数转换为类类型并调用您的成员函数。

例子:

static void Foo::callback_method(void* data) {
    static_cast<Foo*>(data)->mycallback();
}

void Foo::register_my_callback() {
    box_self_intersection_d(&Foo::callback_method, this);
}

大多数正常的回调库都允许您将此 void* 参数传递给函数,以此作为其中包含用户定义数据的一种方式。如果没有,您将需要使用脏方法:

static Foo* Foo::callback_object;
static void Foo::callback_method() {
    callback_object->mycallback();
}

void Foo::register_my_callback() {
    callback_object = this;
    box_self_intersection_d(&Foo::callback_method);
}

一般来说,如果你需要传递一个函数,没有其他办法:要么你有一个像 void* 这样的数据侧通道,但你的库提供者似乎已经省略了它(显然是库),或者您需要通过全局变量传输 this 指针。

【讨论】:

  • 你能说得更清楚些吗?请记住,调用回调的方法不在我的控制之下
【解决方案3】:

有几种可能的解决方法。你可以看这里:http://www.newty.de/fpt/callback.html#member

简而言之,您可以:

  • 声明一个静态的“包装方法”并将类的实例传递给它,
  • 或者将指向对象的指针存储为全局变量。

希望对你有帮助,

【讨论】:

  • 我认为这两种解决方案都不适用于我的情况。对于 1,它需要我修改 mycallback 方法签名,这是不可能的。对于 2,好吧...假设我讨厌全局变量,因为潜在的线程问题。
【解决方案4】:

你还没有提供签名 box_self_intersection_d()

一般来说,如果签名是

void box_self_intersection_d( void *cb );

甚至

void box_self_intersection_d( void (*cb)(const box&, const box&) );

那么你不能将一个指向成员函数的指针传递给它。

原因是sizeof(a_member_function) 不同于 sizeof(a_function_pointer)。如果是这种情况,我认为你不得不使用 thiton 的解决方案,并创建一个静态函数。

【讨论】:

  • box_self_intersection_d的签名无效box_self_intersection_d ( RandomAccessIterator begin, RandomAccessIterator end, Callback callback)
【解决方案5】:

由于是CGAL,所以回调实际上是一个模板参数。
它唯一的限制是“回调必须是 BinaryFunction 概念”。
也就是说,它可以是任何具有适当参数的“可调用”。

这包括任何具有void operator() (const box&amp;, const box&amp;) 成员函数的对象。
在您的类中实现该函数并为回调传递*this 可能是最简单的解决方案。

【讨论】:

    【解决方案6】:

    我可以想到一个可怕的解决方案,这意味着将“this”和函数代码复制/推送到调用堆栈(或其他一些可以写入和可执行的调用者分配的段),并传递地址函数的库。然后,回调函数可以找到自己的代码地址,使用偏移量/指针算术提取“this”。并调用成员函数。应该适用于多个线程。

    我特此申请今年的“Gruesome Hack”奖,以表彰一种解决方案,该解决方案让开发人员感到身体不适,但如果项目经理用猎枪指着你的头,它可能仍然有效。

    Rgds, 马丁

    【讨论】:

    • 是的,(在其他人抱怨之前),调用者堆栈不适合任何同步回调 - 需要一个单独的 R/E 段,可以在回调中取消分配。更混乱:((
    猜你喜欢
    • 2010-11-03
    • 1970-01-01
    • 2013-04-09
    • 1970-01-01
    • 2011-01-01
    • 1970-01-01
    • 2017-05-03
    • 2020-11-14
    • 1970-01-01
    相关资源
    最近更新 更多