如何在发布商外部"C"中返回动态长度的向量fn? [英] How do I return an vector of dynamic length in a pub extern "C" fn?

查看:71
本文介绍了如何在发布商外部"C"中返回动态长度的向量fn?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在pub extern "C" fn中返回一个向量.由于向量具有任意长度,我想我需要使用

I want to return a vector in a pub extern "C" fn. Since a vector has an arbitrary length, I guess I need to return a struct with

  1. 指向向量的指针,

  1. the pointer to the vector, and

向量中的元素数量

我当前的代码是:

extern crate libc;
use self::libc::{size_t, int32_t, int64_t};

// struct to represent an array and its size
#[repr(C)]
pub struct array_and_size {
    values: int64_t, // this is probably not how you denote a pointer, right?
    size: int32_t,
}

// The vector I want to return the address of is already in a Boxed struct, 
// which I have a pointer to, so I guess the vector is on the heap already. 
// Dunno if this changes/simplifies anything?
#[no_mangle]
pub extern "C" fn rle_show_values(ptr: *mut Rle) -> array_and_size {
    let rle = unsafe {
        assert!(!ptr.is_null());
        &mut *ptr
    };

    // this is the Vec<i32> I want to return 
    // the address and length of
    let values = rle.values; 
    let length = values.len();

    array_and_size {
       values: Box::into_raw(Box::new(values)),
       size: length as i32,
       }
}

#[derive(Debug, PartialEq)]
pub struct Rle {
    pub values: Vec<i32>,
}

我得到的错误是

$ cargo test
   Compiling ranges v0.1.0 (file:///Users/users/havpryd/code/rust-ranges)
error[E0308]: mismatched types
  --> src/rle.rs:52:17
   |
52 |         values: Box::into_raw(Box::new(values)),
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected i64, found *-ptr
   |
   = note: expected type `i64`
   = note:    found type `*mut std::vec::Vec<i32>`

error: aborting due to previous error

error: Could not compile `ranges`.

To learn more, run the command again with --verbose.
-> exit code: 101

我发布了整本书,因为在非常有用的 Rust FFI Omnibus .

I posted the whole thing because I could not find an example of returning arrays/vectors in the eminently useful Rust FFI Omnibus.

这是从Rust返回未知大小的向量的最佳方法吗?如何解决剩余的编译错误?谢谢!

Is this the best way to return a vector of unknown size from Rust? How do I fix my remaining compile error? Thanks!

奖金q:如果我的载体在结构中的事实改变了答案,也许您还可以显示如果载体不在Boxed结构中的话该如何做(我认为这意味着它所拥有的载体在堆呢?)?我猜很多人在查询这个q时都不会把它们的向量装箱.

Bonus q: if the fact that my vector is in a struct changes the answer, perhaps you could also show how to do this if the vector was not in a Boxed struct already (which I think means the vector it owns is on the heap too)? I guess many people looking up this q will not have their vectors boxed already.

奖励q2:我只返回向量以查看其值(在Python中),但我不想让调用代码更改向量.但是我想没有办法将内存设为只读,并确保调用代码不会与向量混淆吗? const仅用于显示意图,对吧?

Bonus q2: I only return the vector to view its values (in Python), but I do not want to let the calling code change the vector. But I guess there is no way to make the memory read-only and ensure the calling code does not fudge with the vector? const is just for showing intent, right?

Ps:我不太了解C或Rust,所以我的尝试可能完全是WTF.

Ps: I do not know C or Rust well, so my attempt might be completely WTF.

推荐答案

pub struct array_and_size {
    values: int64_t, // this is probably not how you denote a pointer, right?
    size: int32_t,
}

首先,您是正确的.您想要values的类型是*mut int32_t.

First of all, you're correct. The type you want for values is *mut int32_t.

通常,请注意,有多种C编码样式,C常常不喜欢这样返回临时大小的数组结构.更常见的C API将是

In general, and note that there are a variety of C coding styles, C often doesn't "like" returning ad-hoc sized array structs like this. The more common C API would be

int32_t rle_values_size(RLE *rle);
int32_t *rle_values(RLE *rle);

(注意:实际上,许多内部程序确实使用大小数组结构,但这是迄今为止面向用户的库中最常见的,因为它与C中表示数组的最基本方式自动兼容.)

(Note: many internal programs do in fact use sized array structs, but this is by far the most common for user-facing libraries because it's automatically compatible with the most basic way of representing arrays in C).

在Rust中,这将转换为:

In Rust, this would translate to:

extern "C" fn rle_values_size(rle: *mut RLE) -> int32_t
extern "C" fn rle_values(rle: *mut RLE) -> *mut int32_t

size函数非常简单,只需返回即可返回数组

The size function is straightforward, to return the array, simply do

extern "C" fn rle_values(rle: *mut RLE) -> *mut int32_t {
    unsafe { &mut (*rle).values[0] }
}

这提供了指向Vec底层缓冲区的第一个元素的原始指针,该缓冲区实际上是所有C样式的数组.

This gives a raw pointer to the first element of the Vec's underlying buffer, which is all C-style arrays really are.

如果不是要给C提供对数据的引用,而要给予,最常见的选择是允许用户传递将数据克隆到的缓冲区:

If, instead of giving C a reference to your data you want to give C the data, the most common option would be to allow the user to pass in a buffer that you clone the data into:

extern "C" fn rle_values_buf(rle: *mut RLE, buf: *mut int32_t, len: int32_t) {
    use std::{slice,ptr}
    unsafe {
        // Make sure we don't overrun our buffer's length
        if len > (*rle).values.len() {
           len = (*rle).values.len()
        }
        ptr::copy_nonoverlapping(&(*rle).values[0], buf, len as usize);
    }
}

哪个来自C,看起来像

void rle_values_buf(RLE *rle, int32_t *buf, int32_t len);

这(浅)将您的数据复制到可能由C分配的缓冲区中,然后由C用户负责销毁该缓冲区.它还可以防止数组的多个可变副本同时浮动(假设您未实现返回指针的版本).

This (shallowly) copies your data into the presumably C-allocated buffer, which the C user is then responsible for destroying. It also prevents multiple mutable copies of your array from floating around at the same time (assuming you don't implement the version that returns a pointer).

请注意,您也可以将数组移动"到C中,但这不是特别推荐,它涉及使用mem::forget并期望C用户显式调用销毁函数,并且需要您和用户必须遵守一些可能很难围绕程序进行构造的纪律.

Note that you could sort of "move" the array into C as well, but it's not particularly recommended and involves the use mem::forget and expecting the C user to explicitly call a destruction function, as well as requiring both you and the user to obey some discipline that may be difficult to structure the program around.

如果要从C中接收一个数组,则实际上只需要输入与缓冲区起始和长度相对应的*mut i32i32即可.您可以使用from_raw_parts函数将其组合成一个切片,然后使用to_vec函数创建一个拥有的Vector,其中包含从Rust侧分配的值.如果您不打算拥有这些值,则可以简单地遍历通过from_raw_parts生成的切片.

If you want to receive an array from C, you essentially just ask for both a *mut i32 and i32 corresponding to the buffer start and length. You can assemble this into a slice using the from_raw_parts function, and then use the to_vec function to create an owned Vector containing the values allocated from the Rust side. If you don't plan on needing to own the values, you can simply pass around the slice you produced via from_raw_parts.

但是,必须从任一侧初始化所有值,通常初始化为零.否则,您将调用合法的未定义行为,这通常会导致分段错误(在使用GDB进行检查时,这些错误往往会令人沮丧地消失).

However, it is imperative that all values be initialized from either side, typically to zero. Otherwise you invoke legitimately undefined behavior which often results in segmentation faults (which tend to frustratingly disappear when inspected with GDB).

这篇关于如何在发布商外部"C"中返回动态长度的向量fn?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆