Как передать каждый элемент слайса в качестве отдельного аргумента в функцию C с переменным числом аргументов? - PullRequest
0 голосов
/ 12 мая 2018

Я создаю модуль Redis в Rust.Я нашел несколько хороших примеров, но я застрял при работе с интерфейсом C-функции, которая должна принимать переменные аргументы.

В Redis Module C SDK есть функция с именем RedisModule_Call, которая принимает несколько конкретных аргументов, а затем n аргументы, представляющие команду Redis.Из документации Redis Module SDK (на языке C): первые три аргумента

RedisModuleCallReply *reply;
reply = RedisModule_Call(ctx,"INCR","sc",argv[1],"10");

RedisModule_Call являются конкретными, но остальные представляют команды Redis, которые могут легко иметь сотни аргументов.

В Rust я следую шаблонам в Redis-Cell , который является модулем Redis, реализованным (успешно) в Rust.Модуль фантастический, но имеет очень ограниченный способ решения этой конкретной проблемы.По сути, он принимает до трех аргументов в несколько грубой форме:

pub fn call(&self, command: &str, args: &[&str]) -> Result<Reply, CellError> {
   // ... code ... 
   let raw_reply = match args.len() {
        1 => raw::call1::call(/* ... */),
        2 => raw::call2::call(/* ... */),
        // ...

Эти call1 и call2 функции являются практически просто заглушками, которые обрабатывают различные длины аргументов:

pub mod call2 {
    use redis::raw;

    pub fn call(
        ctx: *mut raw::RedisModuleCtx,
        cmdname: *const u8,
        fmt: *const u8,
        arg0: *mut raw::RedisModuleString,
        arg1: *mut raw::RedisModuleString,
    ) -> *mut raw::RedisModuleCallReply {
        unsafe { RedisModule_Call(ctx, cmdname, fmt, arg0, arg1) }
    }

    #[allow(improper_ctypes)]
    extern "C" {
        pub static RedisModule_Call: extern "C" fn(
            ctx: *mut raw::RedisModuleCtx,
            cmdname: *const u8,
            fmt: *const u8,
            arg0: *mut raw::RedisModuleString,
            arg1: *mut raw::RedisModuleString,
        ) -> *mut raw::RedisModuleCallReply;
    }
}

Мне нужно иметь возможность передавать n аргументы, при этом n определяется во время выполнения, поэтому этот метод жесткого кодирования не практичен.Я знаю, что Rust имеет ограниченную поддержку функций с переменными значениями, и я немного читал о RFC 2137, но я не уверен, что это применимо.

Я ищу способ применить вектор аргумента к концу RedisModule_Call или что-то вроде синтаксиса расширения для аргументов.Я относительно новичок в Rust, но я искал и искал, и я не могу найти какой-либо способ поцарапать этот зуд в Rust.

Чтобы уточнить - я могу передать аргументы в RedisModule_Call (которыйэто не проблема, но я не могу найти синтаксический способ передать переменное число аргументов в Rust в функцию C.Я пытаюсь сделать что-то вроде этого:

impl Redis {
    pub fn call(&self, command: &str, args: &[&str]) -> Result<Reply, CellError> {
        /* ... */

       unsafe { RedisModule_Call(ctx, cmdname, fmt, ...args) }
       /* ... */ 

Где ...args - это какая-то черная магия, которая позволяет аргументам представлять 1 аргумент или 100, что эквивалентно RedisModule_Call(ctx, cmdname, fmt, args[0], args[1] /* ... and so on */).

1 Ответ

0 голосов
/ 12 мая 2018

Вы не , по крайней мере, пока, и я бы держал пари, вероятно, никогда.

Чтобы сделать это, вам понадобятся две ключевые способности, обе из которых находятся вне вашего контроля:

  1. Redis должен предоставить функцию, которая принимает аргумент va_list, а не просто ....

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

  2. Rust должен предоставить способ построения аргумента va_list.

    Несмотря на то, что RFC 2137 вводит тип VaList, предлагаемый API не предоставляет способ создания в нем одного или заданных значений.

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


Что вы можете сделать вместо этого? Предполагая, что вы реализуете код, который использует переменные аргументы, вы можете удалить вариант из вашего вызова. Коллекция элементов в C - это просто указатель и длина, поэтому передайте это вместо:

extern "C" {
    fn call(n_args: i32, ...);
}

fn x(args: &[i32]) {
    unsafe { call(2, args.len(), args.as_ptr()) };
}

Если у вас не было контроля над тем, что читает код на другой стороне, одна из возможных (читай: ужасная ) идея состоит в том, чтобы сопоставить паттерн на каком-то «достаточно большом» подмножестве среза и отправить его. Выключить в функцию variadic:

extern "C" {
    fn call(n_args: i32, ...);
}

fn x(args: &[i32]) {
    unsafe {
        match args {
            [] => call(0),
            [a] => call(1, a),
            [a, b] => call(2, a, b),
            _ => panic!("Didn't implement this yet"),
        }
    }
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...