5

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)

と が同じオブジェクト、つまり構造体内の Rust ベクトルを参照し、rle_values_size関数が機能するため、C コードが正しいと信じる十分な理由があります。rle_valuesrle_values_size

ただし、指定されたポインターを逆参照しrle_valuesて配列として読み取ろうとすると、segfaults が発生します。

Stack Overflow で見つけたコード スニペットのすべての順列を試しましたが、セグメンテーション フォールトが発生します。

なぜこれがクラッシュするのですか?私は何を間違っていますか?

ベクトルのアドレスを間違った方法で取得している可能性があるため、Rust タグを追加しました。

Ps。誰かがこれをnumpy配列に直接読み込む方法も知っているなら、私もそれを支持します。

背景情報: pub extern "C" fn で配列を返すにはどうすればよいですか?

4

1 に答える 1

5

これ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 の戻り値は acintです。の戻り値の型を指定しないことfreeは、おそらく良い考えではありませんvoid
  • データの長さの符号なし数値を返します。-53 項目は何を意味するのでしょうか?
  • ブロックの範囲を、unsafe安全でない部分と実際に安全であることを保証するコードだけに減らします。
  • そういえば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(),
        }
    }
    
于 2016-10-26T13:57:14.963 に答える