c - 如何从 Rust 库访问全局 C 结构数组?
问题描述
我正在将一个静态库移植到 Rust,它将与一个提供全局数组的 C 应用程序链接。下面是结构体和数组的定义:
typedef struct glkunix_argumentlist_struct {
char *name;
int argtype;
char *desc;
} glkunix_argumentlist_t;
extern glkunix_argumentlist_t glkunix_arguments[];
没有长度参数,数组中的最后一个条目将是{NULL, 0, NULL}
( example ),您在遍历数组时对其进行测试。
我认为这是结构的正确 Rust 表示:
#[repr(C)]
struct GlkUnixArgument {
name: *const c_char,
argtype: c_int,
desc: *const c_char,
}
Rust Nomicon展示了如何为单个整数定义 extern。
我已经看到,如果您有一个长度参数,您可以使用它std::slice::from_raw_parts
来获取 slice,但我们还没有长度。我可以修改 C 代码以提供一个,但如果可以的话,我希望能够提供一个替代品。
我还没有看到如何从外部创建一个单一的 Rust 结构。虽然我确实只是有std::slice::from_raw_parts
长度为 1 的想法。
有没有更好的方法在 Rust 中更直接地定义外部结构?
解决方案
在使用以下命令创建切片之前,您必须计算 C 数组的长度from_raw_parts
:
use libc::{c_char, c_int};
#[repr(C)]
pub struct GlkUnixArgument {
name: *const c_char,
argtype: c_int,
desc: *const c_char,
}
pub unsafe fn glkunix_arguments() -> &'static [GlkUnixArgument] {
extern "C" {
pub static glkunix_arguments: *const GlkUnixArgument;
}
let len = (0..)
.take_while(|i| {
let arg = glkunix_arguments.offset(*i);
(*arg).name != std::ptr::null()
|| (*arg).argtype != 0
|| (*arg).desc != std::ptr::null()
})
.count();
std::slice::from_raw_parts(glkunix_arguments, len)
}
你甚至可以将 C 结构映射到 Rust 结构:
use libc::{c_char, c_int, strlen};
use std::{ptr, slice, str};
pub struct GlkUnixArgument {
pub name: &'static str,
pub argtype: i32,
pub desc: &'static str,
}
pub unsafe fn glkunix_arguments() -> Vec<GlkUnixArgument> {
extern "C" {
pub static glkunix_arguments: *const GlkUnixArgument_;
}
#[repr(C)]
pub struct GlkUnixArgument_ {
name: *const c_char,
argtype: c_int,
desc: *const c_char,
}
impl GlkUnixArgument_ {
fn is_not_empty(&self) -> bool {
self.name != ptr::null() || self.argtype != 0 || self.desc != ptr::null()
}
}
unsafe fn c_str_to_rust_str(s: *const i8) -> &'static str {
str::from_utf8(slice::from_raw_parts(s as *const u8, strlen(s))).unwrap()
}
let len = (0..)
.map(|i| glkunix_arguments.offset(i))
.take_while(|&arg| (*arg).is_not_empty())
.count();
slice::from_raw_parts(glkunix_arguments, len)
.iter()
.map(|args| GlkUnixArgument {
name: c_str_to_rust_str(args.name),
desc: c_str_to_rust_str(args.desc),
argtype: args.argtype,
})
.collect()
}
使用惰性构造可以确保您只执行一次操作。
推荐阅读
- java - 我在这里做错了什么,我该如何纠正?
- python - 将 Gmail 转换为 PDF:HTML 中的嵌入图像
- dart - How to fix cropped shadow in a Horizontal List View in Flutter
- websocket - Julia websockets - 什么是读保护和写保护?
- sql - SQL 到字符串的不同部分 - Oracle SQL
- sql - T-SQL。更好的是:加入然后组或组然后加入
- javascript - Is there javascript equivalent to R's use of ":" for returning a string of numbers between the numbers on either side of the colon?
- python - 使用 OR 运算符的 Itertools 置换
- wordpress - 如何在 Woocommerce 中单击复选框时隐藏或显示一些文本字段
- hibernate - DirectPropertyAccessor removed in hibernate 5.1 and above, alternative?