我将C库的subscribe
函数包装到更高级别的C ++类的方法中。在该方法内部,使用作为参考传递的局部subscribe
变量(char*
)调用基础C char key[10]
。现在的问题是-我刚刚发现的问题-由于key
是局部变量,因此其值不受保护。我可以传递它的引用,但是一旦保留作用域,该内存将是空闲的。我从未遇到过通过回调调用undefined
的这种行为-调试后,我发现key
的值已更改。
我尝试使用new char[10]
似乎很有效。但是我想这不是我应该去的方式。
什么是正确的解决方案? 更新:现在已通过替换为string
来解决。
更新
接口功能:
IoT_Error_t aws_iot_mqtt_subscribe(AWS_IoT_Client *pClient, const char *pTopicName, uint16_t topicNameLen,
QoS qos, pApplicationHandler_t pApplicationHandler, void *pApplicationHandlerData)
包装器:
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(key); // error, exit
callback(json);
}
);
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
包装器的主要任务:
{
...
aws->subscribe(
topic,
[&] (const std::string &json) -> void {
ESP_LOGI(TAG, "got json: %s", json.c_str());
}
);
...
}
更新#2
c_style_callback
内部的回调lambda无法访问callback
和key
的正确值。如何保护这两个文件不被覆盖?将它们“包装”在unique_ptr
内?返回task
给呼叫者以供参考?另外,辅助函数get_pvoid()
的返回值指向用户数据,它是lambda函数,也许应该对其进行保护?
答案 0 :(得分:2)
执行此操作的方法是在堆(new
,malloc
)上分配内存。您有几种选择:
new
内存,则可以将指针包装到 std::unique_ptr
或std::shared_ptr
中,然后安全地传递。当最后一个实例超出范围时,内存将自动释放。可以使用get()
直接访问指针。您必须确保没有其他人释放您的记忆!malloc
和free
或new
和delete
。在这种情况下,您可以通过构造函数分配内存,并通过析构函数释放内存,同时将原始指针传递给C接口。缺点是您必须实现复制/移动语义以确保不释放两次,并且必须正确跟踪存储此指针的所有副本。由于涉及的复杂性,如果可以的话,我建议您使用unique/shared_ptr
。还可以将自定义删除器传递到shared_ptr
,因此您也可以将其与free
一起用作删除器。