被FFI做局了(1) —— #[repr(C)]#
因为实际上对C/C++的了解可能还没对jvav的了解深,所以在相关基础知识上踩了不少坑,特别是这些坑不是不知道,单纯是没死磕过八股,不会看到对应的情况就条件反射开始吟诵,大多数是忘了有这么一出。
而且rust写多了,能编译通过就能跑的观念已经变成思想钢印了,写点别的难免有惯性。
FFI#
根据需求,我们要把已有的Rust代码套一层皮,然后做成C++ SDK给上游使用。 上游给出的接口有一个A和一个foo。
// api.h
extern "C" {
struct A {
uint32_t x;
uint64_t y;
uint32_t z;
};
int32_t foo(A& a);
}
我们实现一下Rust函数,调用一下我们的业务逻辑。
#[repr(C)]
#[derive(Debug, Serialize)]
struct A {
x: u32,
y: u64,
z: u32,
}
pub unsafe extern "C" fn foo(a: &A) -> i32 {
let bytes: Vec<u8> = serde_json::to_vec(a).unwrap();
if rust_logic(bytes).is_err() {
return -1;
}
0
}
不赖,编译完测一下跑通了,万事大吉。 但是上游加字段了。
struct A {
uint32_t x;
uint32_t x2; // new
uint64_t y;
uint32_t z;
};
这扯不扯,但是你都叫我哥们了,那我就加上吧。
x2名字比较长,加后面比较好看,而且主打一个先来后到,反正没改api.h,又不是不能用。
#[repr(C)]
#[derive(Debug, Serialize)]
struct A {
x: u32,
y: u64,
z: u32,
x2: u32, // new
}
编译完,懒得跑了,反正业务逻辑没变。给上游,core~
这扯不扯,啥没改编译都过了咋就core了呢?
内存布局#
Rust#
熟悉rust的都比较清楚,一般的情况下,rust会自动对结构体的字段进行排序来进行对齐。所以没事干就按名字长度啥的排一排字段顺序。 例如以下两种定义实际上是等效的内存布局
// size = 32 (0x20), align = 0x8
struct RA {
x: u32,
y: String,
z: u32,
}
// 24 + 4 + 4
struct RB {
y: String,
x: u32,
z: u32,
}
其中 String 的size是24B,在Rust中String实际上是封装了一层Vec<u8>,而Vec<T>简单来说包含了三部分
struct SimpleVec<T> {
ptr: *const T,
capacity: usize,
length: usize,
}
指针的大小是usize,后两者的类型也是usize,在64位系统上长度都是8B。
自动优化后,两个u32被紧凑的排列在了一起,使得实际布局类似于(实际上并不一定是)struct B呈现的顺序。
更详细的信息可以阅读 https://rustcc.cn/article?id=98adb067-30c8-4ce9-a4df-bfa5b6122c2e
C#
C语言的内存布局是比较简单的,结构体的字段按照定义的顺序排列,大小和对齐方式由字段类型决定。 例如以下两种定义的内存布局并不等效:
// size = 24 (0x18), align = 0x8
// 4(+4) + 1(+7) + 4(+4)
typedef struct CA {
uint32_t x;
const char *y;
uint32_t z;
} CA;
// size = 16 (0x10), align = 0x8
// 1(+7) + 4 + 4
typedef struct CB {
const char *y;
uint32_t x;
uint32_t z;
} CB;
可以精确控制,大道至简。
#[repr(C)]#
那我问你,你的struct A脑袋怎么有个尖尖的#[repr(C)],怎么就给忘了呢?
既然按C的布局来,就得保证字段顺序也得一样。
#[repr(C)]
#[derive(Debug, Serialize)]
struct A {
x: u32,
x2: u32, // same place
y: u64,
z: u32,
}
The interaction of repr(C) with Rust’s more exotic data layout features must be kept in mind. Due to its dual purpose as “for FFI” and “for layout control”
欸我++怎么rust这么坏,专坑没写过C的门外汉。你这么大一个FFI章节和repr(C)也没写C不会自动排序对齐啊,偷偷说个for layout control糊弄大学生说是。
🤦 被rust的FFI做局了。
