我正在尝试从c ++代码返回结构数组到swift代码。
Swift代码:
var DB_HOST = process.env.DB_HOST;
var DB_USER = process.env.DB_USER;
var DB_PASS = process.env.DB_PASS;
var DB_NAME = process.env.DB_NAME;
C ++代码:
struct CPoint {
let x: CDouble
let y: CDouble
}
struct CStruct {
let p1: CPoint
let d: CDouble
let p2: CPoint
let i: CInt
}
func get_structs() {
let cnt = ... //Getting size from c++ code
var buf = [CStruct](count: cnt, repeatedValue: CStruct(p1: CPoint(x: 0, y: 0), d: 0, p2: CPoint(x: 0, y: 0), i: 0))
let addr = UnsafeMutableBufferPointer(start: &buf, count: buf.count).baseAddress
get_structs_c(addr)
for cstruct in buf {
//First cstruct is OK. Next are corrupted.
}
}
代码很简单,但结果是损坏的值保存到typedef struct Point {
double x;
double y;
}
typedef struct Struct {
Point p1;
double d;
Point p2;
int i;
}
void get_structs_c(void *buf) {
Struct * structs = (Struct *) buf;
const std::vector<const Struct *> vec = ... // getting values no matter from where
for (int i = 0; i < vec.size(); i++) {
const Struct * s = vec.at(i);
structs[i] = Struct{ s->p1, s->d, s->p2, s->i};
}
}
。
但是,如果我从buf
和i
中移除CStruct
字段,则会返回正确的值,或者如果我从{{1}更改Struct
的类型}和i
到CInt
和int
,然后也会返回正确的值。所以,可能是CDouble
桥接的一些问题。
我检查了Double
和int
的大小,看起来是44个字节。
感谢任何帮助,提前谢谢!
UPD 1:只有当struct的大小与8个字节成比例时才能正常工作。
UPD 2 我检查了内存寻址,发现swift的CStruct
告诉了
Struct
的大小为 44 字节,BUT&amp; struct [1] - &amp; struct [0] = 48 !
如果以更坏的方式重新排序结构字段:
sizeof
然后它自动对齐,struct CStruct {
let p1: CPoint
let d: CDouble
let p2: CPoint
let i: CInt
}
给出大小为48,它可以正常工作。
这种默认不兼容是否正常?
答案 0 :(得分:1)
您的get_structs_c
函数本质上是危险的,因为您没有传入缓冲区的大小,因此这是一个缓冲区溢出漏洞!你真正需要的是这样的签名:
struct StructList {
Struct* items;
size_t size;
};
StructList* CreateAndTransferOwnershipFromC();
或者:
size_t CopyFromC(Struct* output_buffer, size_t buffer_size);
这里最可能发生的是你的C ++和Swift版本之间的大小不匹配,其中一些缓冲区没有被填充或你正在超出缓冲区。
如果失败,您可能会遇到alignment问题。这里更安全(但可能效率更低)的解决方案是在二进制编码字符串中序列化/反序列化数据,例如使用protocol buffers,可以更容易地保证两端的二进制兼容性。否则,您可能需要使用其中一个GCC compiler directives进行对齐/打包以确保其正确无误。