为什么捕获 lambda 不捕获变量?

Why capturing lambda does not capture variables?

本文关键字:变量 lambda 为什么      更新时间:2023-10-16

我不明白为什么传递给c_style_callback的回调lambda无法访问callbackkey的正确值。如何从 lambda 获得这 2 个?

我尝试使用显式复制[callback=callback, key=key],没有帮助。

这是一个名为subscribe的 C 方法的C++包装器实现。

我认为以下代码涵盖了我的问题,如果需要其他内容,请告诉我。

包装器,问题位于此处。请检查行尾的评论:

std::function<void()> AWS::subscribe(const std::string &topic, std::function<void(const std::string&)> callback, QoS qos) {
ESP_LOGI(TAG, "subscribe: %s", topic.c_str());
std::string key("Test...");
auto task = c_style_callback(
[=] (AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, IoT_Publish_Message_Params *params) {
std::string json;
json.assign((char *)params->payload, (size_t)params->payloadLen);
ESP_LOGI(TAG, "subscribe cb payload=%s", json.c_str()); // works
ESP_LOGI(TAG, "key '%s'", key.c_str()); // undefined behaviour
callback(json);// error, exit
}
);
m_error = ::aws_iot_mqtt_subscribe(
&m_client,
key.c_str(),
key.length(),
qos,
task.get_callback<AWS_IoT_Client*, char*, uint16_t, IoT_Publish_Message_Params*>(),
task.get_pvoid()
);
if (m_error != SUCCESS) {
ESP_LOGD(TAG, "subscribe: error=%d", m_error);
return nullptr;
}
return [=] () {
ESP_LOGI(TAG, "unsubscribe %s", key.c_str());  // works
callback(key); // works
};
} // subscribe

c_style_callback实用功能:

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)}; }

调用subscribe包装器的主要任务 - 这只是为了给示例提供上下文,我如何尝试使用subscribe的 C++ 包装器:

{
...
aws->subscribe(
topic,
[] (const std::string &json) -> void {
ESP_LOGI(TAG, "got json: %s", json.c_str());
}
);
...
}

更新:

更多关于aws_iot_mqtt_subscribe

/**
* @brief Subscribe to an MQTT topic.
*
* Called to send a subscribe message to the broker requesting a subscription
* to an MQTT topic.
* @note Call is blocking.  The call returns after the receipt of the SUBACK control packet.
*
* @param pClient Reference to the IoT Client
* @param pTopicName Topic Name to publish to
* @param topicNameLen Length of the topic name
* @param pApplicationHandler_t Reference to the handler function for this subscription
* @param pApplicationHandlerData Data to be passed as argument to the application handler callback
*
* @return An IoT Error Type defining successful/failed subscription
*/
IoT_Error_t aws_iot_mqtt_subscribe(AWS_IoT_Client *pClient, const char *pTopicName, uint16_t topicNameLen,
QoS qos, pApplicationHandler_t pApplicationHandler, void *pApplicationHandlerData);

我假设aws_iot_mqtt_subscribe存储其参数以供以后引用 - 调用函数,以响应稍后某个时间点的某个事件,函数作为其倒数第二个参数传递,指针作为其最后一个参数传递。

使用task.get_pvoid()获得的指针指向task的数据成员。反过来,task是一个局部变量 - 当subscribe与其数据成员一起返回时,它被销毁,因此指针变得悬空。

稍后,c_style_callback_t::get_callback制造的函数接收到该不再有效的指针并尝试取消引用它。因此,程序通过在其生存期结束后访问对象来表现出未定义的行为。