离开范围后如何保护局部变量的值?

How to protect a value of a local variable after leaving scope?

本文关键字:局部变量 保护 范围 何保护 离开      更新时间:2023-10-16

我将 C 库的subscribe函数包装到更高级别的 C++ 类方法中。在该方法中,底层 Csubscribe被调用,并带有一个作为引用传入的局部char*变量 (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 无法访问正确的callbackkey值。如何保护这 2 个不被覆盖?将它们"包裹"在unique_ptr内?将task返回给呼叫者以供参考?此外,帮助程序的返回值get_pvoid()指向用户数据,即lambda函数,也许应该受到保护?

执行此操作的方法是在堆上分配内存(newmalloc(。您有以下几种选择:

  1. 首选:如果new内存,可以将指针包装成std::unique_ptrstd::shared_ptr,并安全地传递。当最后一个实例超出范围时,内存会自动释放。可以通过get()直接访问指针。你必须确保没有其他人释放你的记忆!
  2. 使用 RAII(资源获取即初始化(包装器类型,该包装器类型执行mallocfree,或newdelete。在这种情况下,通过构造函数分配内存,并通过析构函数释放内存,同时将原始指针传递给 C 接口。缺点是您必须实现复制/移动语义以确保不会发布两次,并且正确跟踪存储此指针的所有副本。由于这涉及的复杂性,如果可以的话,我建议您使用unique/shared_ptr。也可以 将自定义删除器传递给shared_ptr,因此您也可以将其与free一起使用作为删除器。
  3. 您可以使用新的原始指针,但必须确保只删除一次。