【问题标题】:How do I use bind to pass a member function as a function pointer?如何使用 bind 将成员函数作为函数指针传递?
【发布时间】:2015-12-11 13:47:20
【问题描述】:

我正在尝试将成员函数作为函数指针传递,这样我就不需要依赖单例或全局函数来处理 Qt 5 中的 Qt 消息。据我所知,我的 std::function 是类型正确,签名正确,并且 bind 应该允许我插入隐式 this 指针,本质上将成员函数作为全局/非拥有函数传递。

void ProgramMessageHandler::setAsMessageHandlerForProgram() {
    std::function<void(QtMsgType, const QMessageLogContext &, const QString &)> funcPtr;

    funcPtr = std::bind(handleMessages, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);

    qInstallMessageHandler(funkPtr);
}

这不会编译。我可以成功创建我的 funcPtr 变量,但将其传递给 qInstallMessageHandler 函数会导致以下情况:

log\ProgramMessageManager.cpp:16: error: cannot convert 'std::function<void(QtMsgType, const QMessageLogContext&, const QString&)>' to 'QtMessageHandler {aka void (*)(QtMsgType, const QMessageLogContext&, const QString&)}' for argument '1' to 'void (* qInstallMessageHandler(QtMessageHandler))(QtMsgType, const QMessageLogContext&, const QString&)'
 oldHandler = qInstallMessageHandler(hackedPointerToHandleFunction);
                                                                  ^

我已阅读:

how to pass a member function as a function pointer?

How do you pass a member function pointer?

How to pass a member function as a parameter to a function that doesn't expect it?

Get function pointer from std::function when using std::bind

但没有人帮助过我。

编辑:

所以有人说,如果没有单例或全局函数,这是不可能的……但为什么呢?具有相同签名的成员函数和全局函数之间的唯一区别是它们具有相同的签名,有一个隐含的this 指针作为成员函数的第一个参数。

知道了,为什么我不能使用 std::bind 来弥补这个差距,通过说“我知道被调用的函数在所有其他函数之前接受 this 参数,强制它在那里,成为知道的东西关于this。这比单例要干净得多,而全局函数只是废话。

【问题讨论】:

  • 函数指针(与函数对象不同)可以没有自己的状态,因此在this 指针中无处“堵塞”。
  • @ChrisDrew 但我的std::function 不是在跟踪它吗?我的印象是std::bind 能够通过跟踪 extra 信息将不同的东西绑定在一起,例如this 指针...
  • 是的,std::function 可以跟踪它。但是qInstallMessageHandler 不需要std::function 它需要一个函数指针。
  • @ChrisDrew 那么在std::function 中是否没有指向函数的函数指针,该函数在调用时会将调用 ++附加信息传递给我的成员函数?
  • Bind 在语法上与您想要的不同,因为它返回一个对象而不是函数指针。您可以使用 bind 存储 memberFunction + 实例对,但为了激活 memberFunction 调用,您必须在 std::function 对象上调用成员运算符 (),这与通过函数指针调用函数不同。这就是您的编译器抱怨的原因。从理论上讲,您可以将对 operator() 的调用存储在另一个函数 X 中并将 X 传递给 qInstallMessageHandler,但同样,您有一个要避免的全局函数。

标签: qt c++11 stdbind


【解决方案1】:

您正在尝试传递 类实例,其中需要 函数指针,但不存在此类转换。

你可以这样做:

class ProgramMessageHandler
{
  static void  myMessageHandler(QtMsgType, const QMessageLogContext &, const QString &);
};

在 main.cpp 中(例如):

...
qInstallMessageHandler(ProgramMessageHander::myMessageHandler);
...

您可能仍然需要处理单例类中的一些问题,但我认为有一个消息处理程序或至少一个调度程序会以某种方式将不同的消息重定向到适当的处理程序是有道理的。希望这会有所帮助

【讨论】:

  • 我希望通过使用std::bind,我可以将我的实例函数指针公开为函数指针,然后在调用后将this 参数插入。毕竟std::function&lt;void(QtMsgType, const QMessageLogContext &amp;, const QString &amp;)&gt; 具有正确的函数签名(据我所知,不是实例签名)。
  • @Troyseph std::bind 不返回函数指针,而是返回函数对象。
  • @ChrisDrew 我无法从中获取函数指针?我的问题是我的std::function 不能是临时的吗?
  • @Troyseph:不,如果可能的话,C++ 标准不会费心引入std::functionstd::bind 会立即返回一个函数指针。哎呀,一个 member 函数指针通常比普通函数指针大,因为它需要嵌入virtual-ness 信息。它甚至都装不下。
【解决方案2】:

std::bind 的结果是一个函数对象,正如错误消息所说,您不能将函数对象转换为函数指针。函数指针不能像 std::function 那样存储 this 指针,你不能只是“强制它在那里”。

做一些接近你想要的事情的唯一方法是使用单例来存储this指针:

class ProgramMessageHandler {
private:
  static ProgramMessageHandler* currentHandler;
  void handleMessagesImpl(const std::string& message);
public:
  void setAsMessageHandlerForProgram(){
    currentHandler = this;
    qInstallMessageHandler(handleMessages);
  }
  static void handleMessages(const std::string& message) { 
    if (currentHandler) 
      currentHandler->handleMessagesImpl(message);
  }
};

ProgramMessageHandler* ProgramMessageHandler::currentHandler = nullptr;

int main() {
  ProgramMessageHandler handler;
  handler.setAsMessageHandlerForProgram();

  // trigger messages...
}

Live demo.

【讨论】:

  • 几乎就是我确定的解决方案,为什么 Qt 在 2015 年的 C++ 库中使用 C 风格的语法让我感到厌烦……非常烦人!
  • 我想我真正希望的是function 对象拥有一个函数指针,我可以正常传递它,它通过bind 恶作剧绑定到我的成员函数......唉,事实并非如此。
  • 对于您关于他们为什么使用这种语法的问题,我假设只是因为它为他们节省了重写大量源代码的工作...... qInstallMessageHandler 是旧函数 qInstallMsgHandler 的替代品,它是在 2011 年 12 月发布的 qt 4.8 中引入的,仅在 C++ 11 获得批准后的几个月(2011 年 8 月)。
  • “为什么”很简单。直到最近,Qt 才支持使用不支持 C++/11 的编译器进行构建,我认为 5.7 是第一个要求这样做的。并且在一个发布周期内,承诺了源代码兼容性,因此在 Qt 6 之前不能对其进行修改。然后由于二进制兼容性问题,Qt 是否可以在其 ABI 中使用 std::function 之类的 std 仍在讨论中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-29
  • 2020-06-09
  • 2021-11-29
  • 1970-01-01
相关资源
最近更新 更多