Как вы разыгрываете целое число в Rust на char * в C - PullRequest
0 голосов
/ 26 мая 2019

У меня есть следующий вариант использования.Структура в C выглядит следующим образом:

typedef struct _zend_internal_arg_info {
    const char *name;
    zend_type type;
    zend_uchar pass_by_reference;
    zend_bool is_variadic;
} zend_internal_arg_info;

Обычно поле name содержит имя функции.Но из-за внутренней реализации это поле также может содержать целое число.На C-конце есть макрос, выполняющий приведение типа:

(const char*)(unsigned long int)(1)

То, что я хочу сделать, - это выполнить то же приведение на конце Rust.В итоге я получил следующий код:

fn create_null_argument(required_args: u8, return_reference: bool) -> ZendFunctionArgument {
    let required_args_ref = Box::into_raw(Box::new(required_args as i8)) as *const i8;
    ZendFunctionArgument {
        arg: php_bindings::_zend_internal_arg_info {
            name: required_args_ref,
            type_: 0,
            pass_by_reference: if return_reference { 1 } else { 0 },
            is_variadic: 0,
        },
    }
}

Кажется, это работает со следующим тестом:

let arguments_ptr = ZendFunctionArguments::new(5, true).into_raw();
unsafe {
    let arguments: Vec<php_bindings::_zend_internal_arg_info> = Vec::from_raw_parts(arguments_ptr as *mut _, 1, 1);
    let required_args = *arguments[0].name;
    assert_eq!(5, required_args);
}

К сожалению, в конце PHP (когда код выполняется) значение полностьюслучайным образом при каждом исполнении.Здесь я хочу спросить, правильный ли способ кастовки i8 по сравнению с кастингом на C-конце ((const char*)(unsigned long int)(1))?

---- EDIT -----

Еще немного подробностей.Сгенерированное связывание PHP:

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct _zend_internal_arg_info {
    pub name: *const ::std::os::raw::c_char,
    pub type_: zend_type,
    pub pass_by_reference: zend_uchar,
    pub is_variadic: zend_bool,
}

Вот неиспользуемая структура с некоторым описанием этой искаженной логики:

/* the following structure repeats the layout of zend_internal_arg_info,
 * but its fields have different meaning. It's used as the first element of
 * arg_info array to define properties of internal functions.
 * It's also used for the return type.
 */
typedef struct _zend_internal_function_info {
    zend_uintptr_t required_num_args;
    zend_type type;
    zend_bool return_reference;
    zend_bool _is_variadic;
} zend_internal_function_info;

Вот весь макрос beig, используемый в c:

#define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args)  \
    static const zend_internal_arg_info name[] = { \
        { (const char*)(zend_uintptr_t)(required_num_args), 0, return_reference, 0 },

И zend_uintptr_t - это:

typedef uintptr_t zend_uintptr_t;

, а затем:

typedef unsigned long int   uintptr_t;

и структура:

pub struct ZendFunctionArgument {
    arg: php_bindings::_zend_internal_arg_info,
}

1 Ответ

2 голосов
/ 27 мая 2019

Ваш код не , преобразующий целое число в указатель - как говорит Stargateur в комментариях, правильный способ сделать это проще required_args as *const ::std::os::raw::c_char.

Вместо этого вы выделяете required_args в куче с помощью Box::new, а затем берете его из рук диспетчера памяти с помощью into_raw - давая указатель на значение required_args,который, пока вы не очистите его вручную, будет жить вечно.

Это похоже на работу со следующим тестом

Поскольку вы разыменовываете указатель в кучу, выполняя*arguments[0].name, и значение в этом указателе действительно 5.

К сожалению, в конце PHP (при выполнении кода) значение является абсолютно случайным при каждом выполнении.

Это неудивительно.Поскольку конечным результатом вашего кода является то, что значение поля является просто указателем в кучу, оно будет меняться при каждом запуске кода.

...