如何在结束结束后留出空间以允许未来的ABI更改?
问题描述:
我想打电话给WSALookupServiceNext
,这是一个extern C
函数就像一个简化的接口:如何在结束结束后留出空间以允许未来的ABI更改?
你给它指向一个Foo
结构和nbytes = ::std::mem::size_of::<Foo>()
,如果一切正常,它填补了结构。
如果程序后ABI的变化被编译,就像这样:
#[repr(C)]
pub struct Foo {
version: u32,
a: u32,
b: f64,
}
然后函数表示失败,nbytes
将包含Foo
正确的大小。我应该如何处理这种情况?
在C或C++,我将简化我的生活,做:
union {
struct Foo foo;
char buf[4000];//should be enough for 10 years
} a;
uint32_t nbytes = sizeof(a);
if (get_foo(&a.foo, &nbytes) != 0) {
return -1;
}
这是可能的锈?如果不是,我应该如何分配空间大小>size_of::<Foo>()
,但指向Foo
? Foo
由bindgen生成,所以我无法更改其声明并将字节添加到结尾。
我正在使用Rust 1.15.1。
答
每日构建锈病已经untagged unions,所以你可以在相同的解决方法,你会在C/C++使用。
#![feature(untagged_unions)]
#[repr(C)]
pub struct Foo {
version: u32,
a: u32,
b: f64,
}
#[repr(C)]
union PaddedFoo {
foo: Foo,
_padding: [u8; 4000],
}
fn main() {
println!("{}", std::mem::size_of::<Foo>());
println!("{}", std::mem::size_of::<PaddedFoo>());
}
答
您可以使用一个枚举(也称为标记联合),它已经包含一些类型标记空间,并且访问内部值不太方便。 Nightly builds of Rust have untagged unions,但这还不稳定,阅读或借阅它的领域需要不安全的代码。正如注释表明你可以手动分配。该直接的方法是使用一个结构,其中包括结构,分配外:
#[repr(C)]
pub struct Foo {
version: u32,
a: u32,
}
#[repr(C)]
pub struct FooPadded {
foo: Foo,
_reserved: [u8; 4000],
}
“*如果ABI改变了,问题*”......您是不是要说“API”?我是否正确地理解了你的想法:你想让你的Rust代码在C库中的不同版本的类型中工作?我认为你不应该那样做。你的Rust代码应该以结构的确切版本为目标,而不是像这样使用黑客来使其成为“一种工作”。如果您希望Rust代码与不同版本的C库一起使用,请使用货运功能和条件编译。 –
@LukasKalbertodt很难决定API或ABI,但是由于C二进制更新后,您可以在不做任何源代码修改的情况下重建Rust程序,并继续工作,我称之为ABI。我无法定位准确版本,我希望我的代码能够尽可能多地使用Windows版本。因此,在我编写代码以使用Windows 10 SDK之后,我希望我的代码能够与Windows 11 SDK一起工作,无需进行修改和重建。可以在'C'上,为什么不在'Rust'上? – user1244932
您也可以在Rust中添加一个哑元数组:'_filler_dummy:[u8; 4000],'。我只是说我认为这不是解决这个问题的好办法。但是再一次,我不太了解FFI的指导方针,所以也许别人可以澄清。 –