将 C++ 'extern "C" __declspec(dllexport)' 结构转换为 Rust 的问题

Problems Translating C++ 'extern "C" __declspec(dllexport)' struct to 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[=13=]", //this doesn't work, how can I fill this char array?
    author: "qbx2 / lukasaldersley[=13=]", //same here
    supportEmail: "something@something.something[=13=]", //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' 但我的 google-fu 不够好,无法在那里找到任何有用的信息,因为我只是一直在寻找有关电子游戏 Rust 中物品的信息。

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

现在的两个问题是如何填充这些字符数组(我不能只传递一个指针)以及如何创建这个结构的实例作为我可以导出的全局变量(或者 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 位值(这不是 strictly true 但 Rust 不支持它所在的架构't).所以 nameauthorsupportEmail 字段应为 u8 数组。


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

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

参见:


您可以使用 byte string 从文字初始化 u8 数组并取消引用它:*b"..."。不幸的是,没有像 C++ 中那样的 shorthand 用于对数组的未确定部分进行零填充,因此您将得到:

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

老实说,这一点都不好。您可以使用一些为您填充数组的函数来清理它,但是,初始化 static 变量需要 const 函数,这些函数在 Rust 中仍然相对不成熟,因此我们不能使用 for loops or traits 帮助我们:

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;

playground 上查看完整内容。