翻译C++的问题;外部;C"__declspec(dllexport)'结构转换为Rust



我目前正在尝试重建和更新一个用Rust编写的项目(更具体地说,它是Skyrim的SKSE64插件:https://github.com/lukasaldersley/sse-mod-skyrim-search-se从qbx2分叉(我面临的最后一个问题是,该库现在需要从我们的库中导出一个结构来进行版本检查。

我尝试了很多可能很愚蠢的方法来实现这一点,但我无法实现。

c++代码如下:

struct SKSEPluginVersionData
{
enum
{
kVersion = 1,
};
enum
{
// set this if you are using a (potential at this time of writing) post-AE version of the Address Library
kVersionIndependent_AddressLibraryPostAE = 1 << 0,
// set this if you exclusively use signature matching to find your addresses and have NO HARDCODED ADDRESSES
kVersionIndependent_Signatures = 1 << 1,
};
UInt32  dataVersion;            // set to kVersion
UInt32  pluginVersion;          // version number of your plugin
char    name[256];              // null-terminated ASCII plugin name
char    author[256];            // null-terminated ASCII plugin author name (can be empty)
char    supportEmail[256];      // null-terminated ASCII support email address (can be empty)
// version compatibility
UInt32  versionIndependence;    // set to one of the kVersionIndependent_ enums or zero
UInt32  compatibleVersions[16]; // zero-terminated list of RUNTIME_VERSION_ defines your plugin is compatible with
UInt32  seVersionRequired;      // minimum version of the script extender required, compared against PACKED_SKSE_VERSION
// you probably should just set this to 0 unless you know what you are doing
};
#define RUNTIME_VERSION_1_6_318 0x010613E0
extern "C" {
__declspec(dllexport) SKSEPluginVersionData SKSEPlugin_Version =
{
SKSEPluginVersionData::kVersion,
1,
"Skyrim Search",
"qbx2",
"",
0,  // not version independent
{ RUNTIME_VERSION_1_6_318, 0 }, // RUNTIME_VERSION_1_6_318 is 
0,  // works with any version of the script extender. you probably do not need to put anything here
};
};

到目前为止,我在Rust中想到的是:

enum KVersionenum {
KVersion=1,
}
#[repr(C)]
pub struct SKSEPluginVersionData {
dataVersion: u32,
pluginVersion: u32,
name: [char;256],
author: [char;256],
supportEmail: [char;256],
versionIndependence: u32,
compatibleVersions: [u32;16],
seVersionRequired: u32,
}
//0x010613E0 is RUNTIME_VERSION_1_6_318
//how can I do this OUTSIDE of a method and how can I make it public to the dll? is that even possible?
let SKSEPlugin_Version = SKSEPluginVersionData {
dataVersion: KVersionenum::KVersion as u32,
pluginVersion: 1,
name: "Skyrim Search", //this doesn't work, how can I fill this char array?
author: "qbx2 / lukasaldersley", //same here
supportEmail: "something@something.something", //and here
versionIndependence: 0,
compatibleVersions: [0x010613E0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], //I'm sure this is a horrible way of doing this
seVersionRequired: 0,
};

当我试图在函数外使用let thingy时,编译器抱怨期望"item",但我的谷歌功能不够好,无法在那里找到任何有用的信息,因为我一直在视频游戏Rust中找到有关项目的信息。

对于car array/string问题,我遇到了std:ffi的东西,完全迷失在它的文档中,但据我所知,它只会处理指针,这不是我需要的。

现在的两个问题是如何填充这些char数组(我不能只传递指针(,以及如何创建这个结构的实例作为全局变量(或者Rust如何调用它(,因为let name=something{…}不起作用,所以我可以导出它。

据我所知,将函数导出到dll会是这样的,但我认为它对该结构的工作方式不同。

#[no_mangle]
pub extern "C" fn SKSEPlugin_Query(skse: *const SKSEInterface, info: *mut PluginInfo) -> bool {...}

有可能做到这一点吗?

有人能在这里帮我吗,或者至少给我指一个正确的方向?请注意,我是Rust的绝对初学者,显然我错误地认为添加一个结构不会那么复杂。

首先,Rust中的char是32位值,而它在C++中是8位值(严格来说这不是真的,但Rust不支持它不支持的体系结构(。所以nameauthorsupportEmail字段应该是u8数组。


您可以在公共static变量上使用#[no_mangle]导出全局值:

#[no_mangle]
pub static SKSEPlugin_Version: SKSEPluginVersionData = SKSEPluginVersionData {
...
};

请参阅:Rust常量/静态可以暴露在C中吗?


您可以使用字节字符串从文字初始化u8数组并取消对其的引用:*b"..."。不幸的是,没有像C++中那样的缩写来对数组中未确定的部分进行零填充,所以只剩下:

name: *b"Skyrim Search",
author: *b"qbx2 / lukasaldersley",
supportEmail: *b"something@something.something",
compatibleVersions: [0x010613E0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],

老实说,这一点都不好。你可以用一些为你填充数组的函数来解决这个问题,然而,初始化static变量需要const函数,而这些函数在Rust中还相对不成熟,所以我们不能使用for循环或特征来帮助我们:

const fn zero_pad_u8<const N: usize, const M: usize>(arr: &[u8; N]) -> [u8; M] {
let mut m = [0; M];
let mut i = 0;
while i < N {
m[i] = arr[i];
i += 1;
}
m
}
const fn zero_pad_u32<const N: usize, const M: usize>(arr: &[u32; N]) -> [u32; M] {
let mut m = [0; M];
let mut i = 0;
while i < N {
m[i] = arr[i];
i += 1;
}
m
}
...
name: zero_pad_u8(b"Skyrim Search"),
author: zero_pad_u8(b"qbx2 / lukasaldersley"),
supportEmail: zero_pad_u8(b"something@something.something"),
compatibleVersions: zero_pad_u32(&[0x010613E0]),

仍然不是那么好,但至少它是可控的。可能有一个板条箱可以为你做这件事。


最后,您不必使用与C++中使用的字段命名约定相同的字段命名惯例,因为这只是顺序和类型很重要,所以我建议使用snake_case,但如果您确实希望保持相同的名称以保持一致性,则可以将#[allow(non_snake_case)]属性放在SKSEPluginVersionData上以抑制编译器警告。

我还建议为这个神奇的值设置一个常数,而不仅仅是一个注释:

const RUNTIME_VERSION_1_6_318: u32 = 0x010613E0;

在操场上看完整的东西。

最新更新