Является ли использование типа `[my_struct]` правильным способом передачи массива структур C в функцию Rust? - PullRequest
0 голосов
/ 29 октября 2018

C файл:

typedef struct point {
    int x;
    int y;
} point;

typedef struct points {
    int count;
    point *array_of_points;
} points;

Файл ржавчины:

#[derive(Debug)]
#[repr(C)]
pub struct point {
    x: c_int,
    y: c_int,
}

#[derive(Debug)]
#[repr(C)]
pub struct points {
    count: c_int,
    array_of_points: [point],
}

#[no_mangle]
pub fn do_something(all_points: &points) {
    for i in 0..all_points.count {
        let crr_point = &all_points.array_of_points[i as usize];
        println!("{:?}", crr_point);
    }
}

В моем C-файле я выделяю много структурных точек и добавляю их к array_of_points, затем вызываю функцию do_something.

Как получить каждое очко в array_of_points в Rust?

Правильно ли я определил массив array_of_points в Rust?

Когда я запускаю его, появляется странный результат:

point { x: 0, y: -952095696 }   
point { x: 32674, y: 101 }   

и т. Д.

Ответы [ 2 ]

0 голосов
/ 30 октября 2018

Примечание. Этот ответ немного устарел, он предлагает вам использовать другой макет данных для кода C.

Вы можете изменить свою структуру C на что-то вроде этого:

typedef struct point {
    int x;
    int y;
} point;

typedef struct points {
    size_t len;
    point points[];
} points;

Это называется гибкий элемент массива , очень приятная и неизвестная функция C, которая позволяет вам сделать только одно выделение. Типичный вариант использования соответствует вашему случаю.

Кроме того, даже в C int не подходит тип для представления размера, вы должны использовать size_t.

Вы также должны использовать bindgen для обработки FAM, это обеспечивает полезную функцию, такую ​​как as_slice().

С учетом следующего кода C:

typedef struct point {
    int x;
    int y;
} point;

typedef struct points {
    size_t len;
    point points[];
} points;

struct points *new_points(size_t len) {
  struct points *points = malloc(sizeof *points + sizeof *points->points * len);
  if (points) {
    points->len = len;
  }
  return points;
}

В настоящее время генерируется:

#[repr(C)]
#[derive(Default)]
pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);

impl<T> __IncompleteArrayField<T> {
    #[inline]
    pub fn new() -> Self {
        __IncompleteArrayField(::std::marker::PhantomData)
    }
    #[inline]
    pub unsafe fn as_ptr(&self) -> *const T {
        ::std::mem::transmute(self)
    }
    #[inline]
    pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
        ::std::mem::transmute(self)
    }
    #[inline]
    pub unsafe fn as_slice(&self, len: usize) -> &[T] {
        ::std::slice::from_raw_parts(self.as_ptr(), len)
    }
    #[inline]
    pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
        ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
    }
}
impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
        fmt.write_str("__IncompleteArrayField")
    }
}
impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
    #[inline]
    fn clone(&self) -> Self {
        Self::new()
    }
}
impl<T> ::std::marker::Copy for __IncompleteArrayField<T> {}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct point {
    pub x: ::std::os::raw::c_int,
    pub y: ::std::os::raw::c_int,
}

#[repr(C)]
#[derive(Debug)]
pub struct points {
    pub len: usize,
    pub points: __IncompleteArrayField<point>,
}

extern "C" {
    pub fn new_points(len: usize) -> *mut points;
}

Некоторые строки пропущены

С этим переплетом вы можете сделать в Rust side:

#[no_mangle]
pub fn print_points(points: &points) {
    for point in unsafe { points.points.as_slice(points.len) } {
        println!("{:?}", point);
    }
}

as_ptr() позволяют избежать накладных расходов на создание временного среза, так что делайте как хотите.

А на стороне C:

#include <stdlib.h>

typedef struct point {
    int x;
    int y;
} point;

typedef struct points {
    size_t len;
    point points[];
} points;

struct points *new_points(size_t len);
void print_points(struct points *points);

int main(void) {
  struct points *points = new_points(42);

  int x = 0;
  for (size_t i = 0; i < points->len; i++, x++) {
    points->points[i] = (struct point){ .x = x, .y = -x};
  }
  print_points(points);
}

Однако помните, что ничто из этого не является гарантией, вы можете столкнуться с совершенно неопределенным поведением, будьте осторожны.


#[derive(Debug)]
#[repr(C)]
pub struct points {
    count: c_int,
    array_of_points: [point],
}

Вы сообщаете компилятору, что array_of_points является допустимым фрагментом, но это не так, ваш код:

#[no_mangle]
pub fn do_something(all_points: &points) {
    for i in 0..all_points.count {
        let crr_point = &all_points.array_of_points[i as usize];
        println!("{:?}", crr_point);
    }
}

абсолютно неопределенное поведение. Я не думаю, что есть способ создать такую ​​вещь на стороне C, Я не нашел один .

0 голосов
/ 29 октября 2018

Это неопределенное поведение. В версии Rust этого типа элемент array_of_points типа point* был преобразован в фрагмент Rust без размера [point], который не является ни эквивалентным, ни совместимы. Добавляя элемент типа [point], вы предлагаете, чтобы point имел переменное число конечных point объектов непосредственно после его первого члена count. Это также делает points тип без размера (или тип с динамическим размером).

Структура памяти points в C должна быть следующей:

[ int, point* ]
           |
            -->[ point, point, ...] // dynamically allocated

Но это определение Rust делало это:

[ int, point, point, ... ]          // unknown compile time size

Член в points должен быть определен с необработанным указателем:

#[derive(Debug)]
#[repr(C)]
pub struct points {
    count: c_int,
    array_of_points: *mut point,
}

Тогда do_something следует либо разыменовать указатель на смещение, чтобы получить каждую точку:

#[no_mangle]
pub fn do_something(all_points: &points) {
    for i in 0..all_points.count {
        unsafe {
            let crr_point = &*all_points.array_of_points.offset(i as isize);
            println!("{:?}", crr_point);
        }
    }
}

Или создать правильный кусочек ржавчины из заданных частей в points:

#[no_mangle]
pub fn do_something(all_points: &points) {
    let point_array = unsafe {
        std::slice::from_raw_parts(all_points.array_of_points, all_points.count as usize)
    };
    for crr_point in point_array {
        println!("{:?}", crr_point);
    }
}

Обратите внимание, как вам требуется unsafe код в любом из этих случаев.

Смотри также:

...