Я пытаюсь использовать ржавчину для взаимодействия с древней платформой, которая ожидает, что функции будут объявлены и извлечены в определенном формате C-Style.

char *func(int argc, char *argv[])

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

Пока у меня есть следующее:

#[no_mangle]
pub extern "C" fn my_function(argc: isize, argv: *const c_char) -> (CString) {

    let output: CString = CString::new("Test Output").expect("CString::new failed");

    return output;
}

Что правильно возвращает мою тестовую строку через интерфейс, но я не могу понять, как разобрать argv в пригодный для использования формат, или если моя реализация argv даже должным образом совместима с указанным форматом. Я не знаком с указателями, и я даже не уверен, с чего начать, чтобы понять это.

1
DingleFlop 22 Фев 2021 в 03:21

1 ответ

Лучший ответ

С существующим кодом у вас есть несколько основных проблем:

  1. argv: *const c_char неверно, потому что исходный код C представляет собой массив указателей, а не указатель на символ. Правильный тип -

    argv: *const *const c_char
    
  2. Вы не можете вернуть CString, потому что это не тот тип, с которым C будет знать, что делать. Вам нужно вернуть *mut c_char, как это сделала функция C. Для этого вы используете output.into_raw() < / a>, чтобы развернуть строку и получить базовый указатель *mut c_char. Это также означает, что вы даете этому коду C случайный указатель, и он может читать его как строку C, но не может освободить эту память, поэтому вы либо утекаете эту память, либо вам нужна вторая функция для передачи возвращаем строку в Rust для последующего освобождения с помощью from_raw. В этом случае я использую *mut c_char, потому что это то, что возвращает into_raw.

  3. Вам следует последовательно использовать типы c_ для всего в интерфейсе C, включая argc, например

    my_function(argc: c_int, argv: *const *const c_char) -> *mut c_char
    

Чтобы обработать аргументы, вы хотите перебрать значения argc, чтобы обернуть каждый указатель в CStr, с которым вы можете работать, не беспокоясь о небезопасном коде.

Все вместе:

#[no_mangle]
pub extern "C" fn my_function(argc: c_int, argv: *const *const c_char) -> *mut c_char {
    
    let argv: Vec<_> = (0..argc)
        .map(|i| unsafe { CStr::from_ptr(*argv.add(i as usize)) })
        .collect();

    // Whatever processing you need to do.

    let output = CString::new("Test Output").expect("CString::new failed");

    return output.into_raw();
}

И потенциально

#[no_mangle]
pub extern "C" fn my_function_free(s: *mut c_char) {
    unsafe {
        CString::from_raw(s);
    }
}
1
loganfsmyth 22 Фев 2021 в 02:09