如何在Qt中创建一个处理程序?

3

我正在使用C++中的Qt版本4.8开发应用程序。

我需要使用一个需要指向自由函数的指针的C函数:

void handler(int dummy)

根据函数内部发生的情况,我需要更改GUI中显示的值。

因此,我一直在研究如何将成员函数转换为自由函数。我已经发现只有当我的成员函数是静态的时才能这样做。

但如果将其设为静态的话,我将无法更改任何小部件。

那么,我该如何解决这个问题呢?我该如何将非静态成员函数传递给自由函数的指针?如果不可能,有什么其他方法可以解决这个问题?

若有任何建议(除了使用另一个库),我将不胜感激。


你需要将函数传递给'C接口',还是可以使用模板?如果模板可行,你可以使用一个函数对象。 - Zaiborg
不行,我不能使用模板。它必须是一个函数。 - user2738748
“dummy”参数的值从哪里来? - Ilya
2个回答

2

由于C函数需要一个参数,您可以将该参数用作函数对象数组的索引:

class MyWidget : public QWidget {
  using vector_t = QVector<std::function<void()>>;
  static vector_t m_functions;
public:
  using function_type = void(*)(int);
  using size_type = vector_t::size_type;

  static function_type getHandler() {
    return +[](int index){ MyWidget::m_functions[index]; };
  }
  template <typename F> static size_type getIndexFor(F && fun) {
    m_functions.append(std::forward<F>(fun));
    return m_functions.size() - 1;
  }
  //...
  size_type getIndexForSetTitle();
};

getHandler函数返回处理函数的地址。 getIndexFor函数返回传递给处理函数的参数,以便运行给定的函数对象。 函数对象可以封装对小部件方法的调用。 在这里,我们将在小部件上调用setWindowTitle

MyWidget::size_type MyWidget::getIndexForSetTitle() {
  static size_type index = getIndexFor([this]{ setWindowTitle("Foo"); });
  return index;
}

不错!但你确定能够控制dummy参数的值吗?它可能会被C库设置...?此外,你确定他的编译器能理解这个结构吗(他使用的是Qt 4.8,对于编译器他没有说明)? - Ilya
@Ilya,“dummy”参数是一个习语。否则,API将无法使用。此外,Qt 4.8不会阻止您使用现代编译器。例如,在Qt 4.8和MSVC 2012下,上述代码完全可以正常工作。 - Kuba hasn't forgotten Monica
Qt 4.8不会阻止您使用现代编译器,但它也不能防止您被困在旧的工具链中(例如某些嵌入式设备)。 - Ilya
@Ilya 当然。这就是为什么我们需要听取原帖作者的意见 :) - Kuba hasn't forgotten Monica
@KubaOber,谢谢您。这是一个非常好的解决方案。我正在使用g++和C++11标准。不幸的是,我现在无法检查我的编译器版本。但是,您的代码片段是否表明我只能针对每个要获取索引的函数使用一次getIndexFor函数? - user2738748
@user2738748 这是正确的,因为 F 可以是一个闭包,每次调用 getIndexFor 时,闭包都是一个不同的对象。你需要自己存储索引。如果你想限制 F 的类型,或者专门为非闭包定制 getIndexFor,当然可以这样做! - Kuba hasn't forgotten Monica

1

是的,C++不允许将成员函数作为函数指针传递给C库。

在这种情况下,常见的方法是使用一个辅助函数来充当C库和我们的类实例之间的桥梁。这个辅助函数是一个自由函数,它接受当前类实例的指针,并在其内部调用相应的成员方法。

void handler_helper(int data){
    MyClass *obj = (MyClass *) data;
    obj->handler(); // This is the corresponding member method
}

在从C库调用函数时,您应将handler_helper作为函数指针传递,并将(int) this作为其参数。

假设指针占用4个字节是相当危险的。我会说(int)this可能是未定义行为。 - Ilya
使用 intptr_t,这是一个足够大以容纳指针的整数类型。 - Oktalist
我知道,将指针转换为int并不是将其传递给函数的正确方式。在问题帖子中,该函数的原型采用int,因此在这种情况下,我必须将指针作为int传递。但是,在我看过的大多数C库中,始终接受类型为void *的自由变量作为传递给函数的参数。 - frogatto
@Oktalist,如果我必须使用int作为参数,那么我该如何使用intptr_t? - user2738748

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接