Подтвердить что ты не робот

Создать интерфейс для указателей функций C в Rust

Возможно, я не правильно описал заголовок вопроса, отредактируйте его, если нужно.

Я пытаюсь привязать интерфейс Rust к библиотеке LXC, который написан на C.

Я успешно вызвал простые функции, такие как lxc_get_version или lxc_container_new, но я не могу получить доступ к функциям, описанным в блоке struct lxc_container.

Вот часть моего кода:

#[link(name = "lxc")]
extern {
    // LXC part
    fn lxc_get_version() -> *const c_char;
    fn lxc_container_new(name: *const c_char, configpath: *const c_char) -> LxcContainer;

    // LXC container parts
    fn is_defined(container: &LxcContainer) -> bool; 
}

И вот ошибка:

note: test.o: In function `LxcContainer::is_defined::heb2f16a250ac7940Vba':
test.0.rs:(.text._ZN12LxcContainer10is_defined20heb2f16a250ac7940VbaE+0x3e): undefined reference to `is_defined'

EDIT: Я справился с этими функциями внутри C-структур, называемых указателями функций. Я попытался сделать google что-то вроде "Rust C function pointer", но без везения.

4b9b3361

Ответ 1

Когда вы увидите что-то вроде этого (в C):

struct S {
    void (*f)(int, long)
}

это означает, что struct S содержит поле под названием f, которое является указателем на функцию. Это не значит, что сама библиотека предоставляет функцию под названием f. Например, это действительно:

void some_function_1(int x, long y) { ... }

void some_function_2(int a, long b) { ... }

int main() {
    struct S s1; s1.f = some_function_1;
    struct S s2; s2.f = some_function_2;
}

Здесь struct instance s1 содержит указатель на some_function_1, а s2 содержит указатель на some_function_2.

Когда вы пишете привязку FFI в Rust для некоторой библиотеки C, вы обычно определяете копии Rust для структур C. Некоторые инструменты, такие как rust-bindgen, могут сделать это автоматически. В вашем случае вам придется написать что-то вроде этого:

#[repr(C)]
struct LxcContainer {
    name: *mut c_char,
    configfile: *mut c_char,
    // ...
    numthreads: c_int,
    // ...
    is_defined_f: extern fn(c: *mut LxcContainer) -> bool,
    state_f: extern fn(c: *mut LxcContainer) -> *const c_char,
    // ...
}

То есть, странные типы указателей функций C соответствуют типам указателей функций extern fn в Rust. Вы также можете написать extern "C" fn(...) -> ..., но "C" определитель по умолчанию, поэтому он не требуется.

Вам нужно будет написать что-то вроде этого, чтобы вызвать эти функции:

impl LxcContainer {
    fn is_defined_f(&mut self) -> bool {
        unsafe {
            (self.is_defined_f)(self as *mut LxcContainer)
        }
    }
}

Вам нужно указать ссылку на необработанный указатель, а также скопировать self.is_defined_f в круглые скобки, чтобы устранить неоднозначность между вызовом метода и доступом к полю.

Подробнее о FFI в Rust здесь. Однако указатели на функции объясняются очень кратко.