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

时间:2018-09-13 18:43:22

标签: c++

我将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无法访问callbackkey的正确值。如何保护这两个文件不被覆盖?将它们“包装”在unique_ptr内?返回task给呼叫者以供参考?另外,辅助函数get_pvoid()的返回值指向用户数据,它是lambda函数,也许应该对其进行保护?

1 个答案:

答案 0 :(得分:2)

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

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