首页 > 解决方案 > 将泛型对象传递给 Rust 并在使用后传回 C++ 进行销毁

问题描述

我想将缓冲区从 C++ 发送到 Rust,在 Rust 中读取它们并在不再需要它们时在 C++ 上销毁它们。

这是我收到的功能:

#[no_mangle]
pub extern "C" fn smol_stack_smol_socket_send(
    smol_stack: &mut SmolStackType,
    socket_handle_key: usize,
    data: *mut u8,
    len: usize,
    endpoint: CIpEndpoint,
) -> u8 {

我想接收指向uint8_t数据的指针,但我也不认为我需要自己使用new uint8_t[size]. 例如,我可以用一个字符串来做:

std::string* s = new std::string("hello");

然后简单地传递s.c_str()uint8_t*Rust。我不能简单地调用delete[] s.c_str(),我需要删除sC++ 端的字符串。所以,我正在考虑向我的函数添加另外 2 个参数smol_stack_smol_socket_send。一个是指向拥有uint8_t缓冲区的对象的指针(在本例中是字符串,但也可以是另一个),另一个是指向接收该对象并进行析构的函数的指针。

我想到了类似在 Rust 中将字符串/对象作为 void 指针接收,但我不知道这是否可能。此外,第二个参数,即析构函数,也需要是一个作用于通用数据的函数,因为我可以传递一个与字符串不同的对象,它拥有数据。

这里有什么优雅和安全的解决方案?

标签: c++rust

解决方案


在我看来,您至少有两条可能的路径。哪一个适合你将在很大程度上取决于这些调用如何组合在一起。

最简单的方法是只考虑从 C++ 传递到 rust 的数据仍然由 C++ 拥有。iesmol_stack_smol_socket_send预计不会破坏传递给它的缓冲区。那份工作落到了调用者的头上smol_stack_smol_socket_send

锈代码可能如下所示:

#[no_mangle]
pub extern "C" fn simpler_function(
    data: *mut u8,
    len: usize,
) { .. }

然后 C++ 代码将如下所示:

void with_string() {
   std::string buffer("Hello World");
   simpler_function(buffer.c_str(), buffer.length());
   // C++ destroys the string here.
}

void with_newed_buffer() {
   char* buffer=new char[10];
   buffer[0]='A';
   buffer[1]=0;
   simpler_function(buffer, 2);
   delete [] buffer;
}

另一种方法是将回调传递给函数以处理销毁缓冲区

type Destructor = extern "C" fn(*mut c_void);

#[no_mangle]
pub extern "C" fn core_function(
    data: *mut u8,
    len: usize,
    calback_data: *mut c_void
    destructor: Destructor
) {
   ..
   // We're done with the data - destroy it
   destructor(callback_data);
}

现在在 C++ 中,代码看起来像这样......

void delete_buffer(void* buffer) {
    delete [] (char*)buffer;
}

void with_newed_buffer() {
   char* buffer=new char[10];
   buffer[0]='A';
   buffer[1]=0;
   simpler_function(buffer, 2, buffer, delete_buffer);
}

IMO 你应该总是喜欢第一种方法。这不起作用的唯一情况是当 rust 需要获取 C++ 对象的所有权(并因此控制生命周期)时。通常,这意味着将其存储在 rust collection 等的某个地方。


推荐阅读