2014-09-16 3 views
8

Я пытаюсь создать динамическую библиотеку в Rust, которая экспортирует структуру как символ, который будет загружен в программу на языке C через dlopen().Создание статической структуры C, содержащей строки

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

Это код Rust (test.rs), составленный с "rustc --crate типа dylib test.rs":

#[repr(C)] 
pub struct PluginDesc { 
    name: &'static str, 
    version: &'static str, 
    description: &'static str 
} 


#[no_mangle] 
pub static PLUGIN_DESC: PluginDesc = PluginDesc { 
    name: "Test Plugin\0", 
    version: "1.0\0", 
    description: "Test Rust Plugin\0" 
}; 

и здесь является программа C, которая пытается загрузить библиотеку (test.c), составленный с "GCC test.c -ldl -o тест":

#include <dlfcn.h> 
#include <stdio.h> 


typedef struct { 
    const char *name; 
    const char *version; 
    const char *description; 
} plugin_desc; 


int main(int argc, char **argv) { 
    void *handle; 
    plugin_desc *desc; 

    handle = dlopen("./libtest.so", RTLD_LOCAL | RTLD_LAZY); 
    if (!handle) { 
     printf("failed to dlopen: %s\n", dlerror()); 
     return 1; 
    } 

    desc = (plugin_desc *) dlsym(handle, "PLUGIN_DESC"); 
    if (!desc) { 
     printf("failed to dlsym: %s\n", dlerror()); 
     return 1; 
    } 

    printf("name: %p\n", desc->name); 
    printf("version: %p\n", desc->version); 
    printf("description: %p\n", desc->description); 

    return 0; 
} 

Это выход:

name: 0x7fa59ef8d750 
version: 0xc 
description: 0x7fa59ef8d75c 

Как вы можете видеть, объявление dress of desc-> version - это фактически 0xc (12), которая является длиной первой строки. Таким образом, похоже, что структура, которая упакована в библиотеку, также содержит длину строки после адреса памяти.

Я использую здесь неправильный тип строки? Как вы можете видеть, мне пришлось также сделать строки NULL завершенными вручную. Я попытался использовать оболочку CString, но в этом случае это не работает («статическим элементам не разрешено иметь деструкторы»).

Я бегу последнюю Rust каждую ночь на Linux:

$ rustc --version 
rustc 0.12.0-pre-nightly (f8426e2e2 2014-09-16 02:26:01 +0000) 
+0

Вы пробовали положить '* i8' указатели на структуры вместо этого? – nneonneo

+1

Похоже, что ржавые строки - это не просто 'char *'. Есть ли .h-файл, содержащий определение строк ржавчины для связывания с C? – EOF

ответ

2

Схема среза (&[T] или &str) является указателем затем длиной, как задокументировано the Slice struct of the std::raw module. Вот почему чтение поля version из вашего кода на C показывает длину значения поля name. (Обратите внимание, однако, что точная компоновка памяти срезов не считается стабильной, поэтому она может измениться в более поздней версии. В любом случае вы должны не передать типы данных, зависящие от ржавчины, на C, а только передавать примитивные типы - которые включает сырые указатели - и тип аннотированные с #[repr(C)])

EDIT:. к сожалению, кажется, нет никакого способа сделать это в Русте сейчас. Есть функции для получения исходных указателей из срезов, но function calls are not allowed in static initializers. Как было предложено sellibitze в комментариях, вы должны определить эту переменную в исходном файле C.

+1

Действительно ли байт-литералы (типа '& [u8]') действительно принуждают к '* const u8'? – huon

+0

К сожалению, это тоже не сработает. Я получил ошибку «ожидаемый» * const i8', нашел '& 'static [u8]' (ожидаемый i8, найденный вектор) ", но при изменении типа на" & "static [u8]" он скомпилировал его такая же проблема, как и исходный код. – chrippa

+0

@chrippa: На кусочках есть метод 'as_ptr'. Но я не думаю, что вы можете назвать это как часть инициализации статики. Единственное решение, которое я вижу, это написать файл C, скомпилировать его и связать с остальной частью вашего кода Rust. – sellibitze

3

Короткий ответ заключается в том, что вы не можете статически выделять такую ​​структуру. Будущая ржавчина, вероятно, получит эту способность.

Что вы можете сделать, это статически выделять структуру, содержащую нулевые указатели, и устанавливать эти нулевые указатели на что-то полезное при вызове функции. Rust имеет static mut. Он требует небезопасный код, is не threadsafe вообще и является (насколько мне известно) считается код запаха.

Прямо здесь я считаю обходным путем тот факт, что нет способа превратить &[T] в *const T в статике.

static S: &'static [u8] = b"http://example.org/eg-amp_rust\n\0"; 
static mut desc: LV2Descriptor = LV2Descriptor { 
    amp_uri: 0 as *const libc::c_char, // ptr::null() isn't const fn (yet) 
}; 

#[no_mangle] 
pub extern fn lv2_descriptor(index: i32) -> *const LV2Descriptor { 
    let ptr = S.as_ptr() as *const libc::c_char; 
    unsafe { 
     desc.amp_uri = ptr; 
     &desc as *const LV2Descriptor 
    } 
} 

answer copied from duplicate question

Смежные вопросы