C ++ Lambda-错误:没有匹配的函数可调用

时间:2018-09-05 16:52:36

标签: c++ c++11 lambda closures

我正在尝试将lambda作为参数传递给函数,但是一旦尝试访问在外部声明的lambda内部的变量,构建就会失败:error: no matching function for call to 'AWS::subscribe(char [128], mainTask(void*)::<lambda(AWS_IoT_Client*, char*, uint16_t, IoT_Publish_Message_Params*, void*)>)'

我当时认为[&]将负责捕获变量。我也尝试过[=][someVar][&someVar]

我正在使用C ++ 11。

char someVar[128];

aws->subscribe(
  topic,
  [&] (AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, IoT_Publish_Message_Params *params, void *pData) {
    char *text = (char *)params->payload;
    sprintf(someVar, "%s", text);
  }
);

从AWS库中:

void AWS::subscribe(const char *topic,
                    pApplicationHandler_t iot_subscribe_callback_handler) {
  m_error =
      ::aws_iot_mqtt_subscribe(&m_client, topic, (uint16_t)std::strlen(topic),
                              QOS1, iot_subscribe_callback_handler, NULL);

}

2 个答案:

答案 0 :(得分:4)

问题在于AWS::subscribe函数需要一个函数指针,而不是lambda。无需捕获的lambda可以转换为函数指针,但是具有捕获(即状态)的lambda不能。

您可以在签名中看到针对此问题的“常规”解决方案:有一个void*参数应将所有特定于回调的数据打包到其中。大概这是您当前设置为aws_iot_mqtt_subscribe的{​​{1}}的最后一个参数(最好使用NULL btw)。

这比使用lambda更为丑陋,但它基本上是C兼容库接口的唯一选择:

nullptr

答案 1 :(得分:2)

void AWS::subscribe(const char *topic,
                    pApplicationHandler_t iot_subscribe_callback_handler,
                    void* ptr) {
  m_error =
      ::aws_iot_mqtt_subscribe(&m_client, topic, (uint16_t)std::strlen(topic),
                              QOS1, iot_subscribe_callback_handler, ptr);

}

然后是一个实用程序类型:

namespace utils {
  template<class F>
  struct c_style_callback_t {
    F f;
    template<class...Args>
    static void(*get_callback())(Args..., void*) {
      return [](Args...args, void* fptr)->void {
        (*static_cast<F*>(fptr))(std::forward<Args>(args)...);
      };
    }
    void* get_pvoid() {
      return std::addressof(f);
    }
  };
  template<class F>
  c_style_callback_t< std::decay_t<F> >
  c_style_callback( F&& f ) { return {std::forward<F>(f)}; }
}

现在我们可以做:

auto task = utils::c_style_callback(
  [&] (AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, IoT_Publish_Message_Params *params) {
    char *text = (char *)params->payload;
    sprintf(someVar, "%s", text);
  }
);
aws->subscribe(
  topic,
  task.get_callback<AWS_IoT_Client*, char*, uint16_t, IoT_Publish_Message_Params*>(),
  task.get_pvoid()
);

Live example


据我了解,您可以修改AWS类,我会这样做:

template<class Handler>
void AWS::subscribe(const char *topic,
                    Handler iot_subscribe_callback_handler) {
  auto task = utils::c_style_callback(iot_subscribe_callback_handler);

  m_error =
    ::aws_iot_mqtt_subscribe(
      &m_client,
      topic,
      (uint16_t)std::strlen(topic),
       QOS1,
      task.get_callback<AWS_IoT_Client*, char*, uint16_t, IoT_Publish_Message_Params*>(),
      task.get_pvoid()
    );
}

其中subscribe现在接受带有签名void(AWS_IoT_Client*, char*, uint16_t, IoT_Publish_Message_Params*)的lambda。