Как я могу использовать cbindgen, чтобы вернуть и освободить коробку>? - PullRequest
1 голос
/ 09 октября 2019

У меня есть структура, возвращенная в C-код из Rust. Я понятия не имею, если это хороший способ сделать что-то, но он работает для восстановления структуры и освобождения памяти без утечек.

#[repr(C)]
pub struct s {
    // ...
}

#[repr(C)]
#[allow(clippy::box_vec)]
pub struct s_arr {
    arr: *const s,
    n: i8,
    vec: Box<Vec<s>>,
}

/// Frees memory that was returned to C code
pub unsafe extern "C" fn free_s_arr(a: *mut s_arr) {
    Box::from_raw(s_arr);
}

/// Generates an array for the C code
pub unsafe extern "C" fn gen_s_arr() -> *mut s_arr {
    let many_s: Vec<s> = Vec::new();
    // ... logic here

    Box::into_raw(Box::new(s_arr {
        arr: many_s.as_mut_ptr(),
        n: many_s.len() as i8,
        vec: many_s,
    }))
}

Заголовок C в настоящее время написан от руки, но я хотел попробоватьиз cbindgen. Ручное определение C для s_arr:

struct s_arr {
    struct s *arr;
    int8_t n;
    void *_;
};

cbindgen генерирует следующее для s_arr:

typedef struct Box_Vec_s Box_Vec_s;

typedef struct s_arr {
        const s *arr;
        int8_t n;
        Box_Vec_s vec;
} s_arr;

Это не работает, поскольку struct Box_Vec_s не определено. В идеале я бы просто хотел переопределить тип cbindgen, сгенерированный для vec, чтобы сделать его void *, поскольку он не требует изменений кода и, следовательно, не требует дополнительного тестирования, но я открыт для других предложений.

Я посмотрелчерез документацию cbindgen, хотя и не примеры, и ничего не смог найти.

1 Ответ

1 голос
/ 09 октября 2019

Ваш вопрос немного неясен, но я думаю, что, если я вас правильно понял, вы путаете две вещи и в результате получаете в результате темный переулок.

В C, динамический размермассив, как вы, наверное, знаете, идентифицируется двумя вещами:

  1. Его начальная позиция в виде указателя
  2. Его длина

Ржавчина следует тому жеусловное обозначение - Vec<_>, ниже капота, имеет ту же структуру (ну, почти. У него тоже есть емкость, но это не относится к делу).

Передача в штучной упаковке вектора сверху *Указатель 1015 * не только излишний, но и крайне неразумный. Привязки FFI могут быть умными, но они недостаточно умны, чтобы в большинстве случаев иметь дело со сложным типом в штучной упаковке.

Чтобы решить эту проблему, мы упростим ваши привязки. Я добавил один элемент в struct S, чтобы показать вам, как он работает. Я также очистил вашу границу FFI:

#[repr(C)]
#[no_mangle]
pub struct S {
    foo: u8
}

#[repr(C)]
pub struct s_arr {
    arr: *mut S,
    n: usize,
    cap: usize
}

// Retrieve the vector back
pub unsafe extern "C" fn recombine_s_arr(ptr: *mut S, n: usize, cap: usize) -> Vec<S> {
    Vec::from_raw_parts(ptr, n, cap)
}

#[no_mangle]
pub unsafe extern "C" fn gen_s_arr() -> s_arr {
    let mut many_s: Vec<S> = Vec::new();

    let output = s_arr {
        arr: many_s.as_mut_ptr(),
        n: many_s.len(),
        cap: many_s.capacity()
    };
    std::mem::forget(many_s);
    output
}

. Таким образом, cbindgen возвращает ожидаемые определения заголовка:

typedef struct {
  uint8_t foo;
} so58311426S;

typedef struct {
  so58311426S *arr;
  uintptr_t n;
  uintptr_t cap;
} so58311426s_arr;

so58311426s_arr gen_s_arr(void);

Это позволяет нам вызывать gen_s_arr() из C илиРастрите и извлеките структуру, которую можно использовать в обеих частях границы FFI (so58311426s_arr). Эта структура содержит все, что нам нужно, чтобы иметь возможность изменять наш массив S (ну, so58311426S в соответствии с cbindgen).

При прохождении через FFI вам необходимо убедиться в нескольких простых вещах:

  • Вы не можете передавать необработанные блоки или не примитивные типы;Вам почти повсеместно потребуется преобразовать в набор указателей или изменить свои определения, чтобы приспособить их (как я сделал здесь)
  • Вы наиболее определенно не пропускают необработанные векторы. В лучшем случае вы передаете срез, так как это примитивный тип (см. Пункт выше).
  • Вы обязательно std::mem::forget() делаете все, что не хотите освобождать, и не забывайте освобождать его. или измените его где-нибудь еще.

Я отредактирую этот вопрос через час;У меня есть самолет, чтобы сесть на него. Дайте мне знать, если что-то из этого потребует разъяснений, и я доберусь до него, как только окажусь в нужной стране: -)

...