gpt4 book ai didi

python - 在 Python 中取消引用 FFI 指针以获取底层数组

转载 作者:太空狗 更新时间:2023-10-30 02:56:11 25 4
gpt4 key购买 nike

我有一个用 Rust 编写的 C FFI,名为 src/lib.rs,如下所示:

// compile with $ cargo build

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

use std::cmp::min;
use std::slice;

#[no_mangle]
pub extern "C" fn rle_new(values_data: *const int32_t, values_length: size_t) -> *mut Rle {
let values = unsafe { slice::from_raw_parts(values_data, values_length as usize).to_vec() };

return Box::into_raw(Box::new(Rle::new(values)));

}

#[no_mangle]
pub extern "C" fn rle_free(ptr: *mut Rle) {
if ptr.is_null() {
return;
}
unsafe {
Box::from_raw(ptr);
}
}

#[no_mangle]
pub extern "C" fn rle_values_size(rle: *mut Rle) -> int32_t {
unsafe { (*rle).values.len() as i32 }
}

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


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


impl Rle {
pub fn new(values: Vec<i32>) -> Self {
return Rle { values: values };
}
}

这是项目基础文件夹中我的 Cargo.toml:

[package]
name = "minimal_example"
version = "0.1.0"
authors = ["Dumbass"]

[dependencies]
libc = "0.2.16"

[lib]
crate-type = ["dylib"] # you might need a different type on linux/windows ?

这是调用 Rust 的 Python 代码,也放在 base 文件夹中:

import os
import sys, ctypes
from ctypes import c_char_p, c_uint32, Structure, POINTER, c_int32, c_size_t, pointer

class RleS(Structure):
pass

prefix = {'win32': ''}.get(sys.platform, 'lib')
extension = {'darwin': '.dylib', 'win32': '.dll'}.get(sys.platform, '.so')
libpath = os.environ.get("LD_LIBRARY_PATH", "target/debug") + "/"
libpath = libpath + prefix + "minimal_example" + extension

try:
lib = ctypes.cdll.LoadLibrary(libpath)
except OSError:
print("Library not found at " + libpath)
sys.exit()

lib.rle_new.restype = POINTER(RleS)

lib.rle_free.argtypes = (POINTER(RleS), )

lib.rle_values.argtypes = (POINTER(RleS), )
lib.rle_values.restypes = POINTER(c_int32)

lib.rle_values_size.argtypes = (POINTER(RleS), )
lib.rle_values_size.restypes = c_int32


class Rle:
def __init__(self, values):

values_length = len(values)

values_array = (c_int32 * len(values))(*values)

self.obj = lib.rle_new(values_array, c_size_t(values_length))

def __enter__(self):
return self

def __exit__(self, exc_type, exc_value, traceback):
lib.rle_free(self.obj)

def __str__(self):
values_size = lib.rle_values_size(self.obj)
print(values_size, "values_size") # prints correct value

values_pointer = lib.rle_values(self.obj)
print("values_pointer:", values_pointer)
ar = ctypes.cast(values_pointer, ctypes.POINTER(ctypes.c_int32)).contents
print(ar) # segfaults!

rle = Rle([1, 1, 2] * 10)
print(rle)

我有充分的理由相信 C 代码是正确的,因为 rle_values_sizerle_values 指的是同一个对象,即结构中的 Rust 向量,并且rle_values_size 函数有效。

但是,当我尝试取消引用 rle_values 给出的指针并将其作为数组读取时,我遇到了段错误。

我已经尝试了在 Stack Overflow 上找到的代码片段的每一个排列,但它会出现段错误。

为什么会崩溃?我做错了什么?

我添加了 Rust 标签,因为我可能以错误的方式获取向量的地址。

附言。如果有人也知道如何将其直接读入 numpy 数组,我也会赞成。

背景信息:How do I return an array in a pub extern "C" fn?

最佳答案

cast 应该是第一个警告标志。为什么必须从类型转换为应该相同的类型?这是因为有简单的错别字:

lib.rle_values.restype = POINTER(c_int32)    
lib.rle_values_size.restype = c_int32

请注意,它应该是restype,而不是restypes

def __str__(self):
values_size = lib.rle_values_size(self.obj)
print(values_size, "values_size")

values_pointer = lib.rle_values(self.obj)
print("values_pointer:", values_pointer)

thing = values_pointer[:values_size]
return str(thing)

使用as_mut_ptr也更好:

#[no_mangle]
pub extern "C" fn rle_values(rle: *mut Rle) -> *mut int32_t {
let mut rle = unsafe { &mut *rle };
rle.values.as_mut_ptr()
}

运行程序似乎有效:

$ LD_LIBRARY_PATH=$PWD/target/debug/ python3 main.py
new
30 values_size
values_pointer: <__main__.LP_c_int object at 0x10f124048>
[1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2]

我还建议:

  • 默认的 ctypes 返回值是一个 cint。不为 free 指定返回类型可能不是一个好主意,因为它应该是 void
  • 返回数据长度的无符号数; -53 项是什么意思?
  • unsafe block 的范围缩小到仅不安全的部分和确保它实际上安全的代码。
  • 说到这里,您可以检查每个函数中的 NULL 指针。

    #[no_mangle]
    pub extern "C" fn rle_values_size(rle: *mut Rle) -> int32_t {
    match unsafe { rle.as_ref() } {
    Some(rle) => rle.values.len() as i32,
    None => 0,
    }
    }

    #[no_mangle]
    pub extern "C" fn rle_values(rle: *mut Rle) -> *mut int32_t {
    match unsafe { rle.as_mut() } {
    Some(mut rle) => rle.values.as_mut_ptr(),
    None => ptr::null_mut(),
    }
    }

关于python - 在 Python 中取消引用 FFI 指针以获取底层数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40219711/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com