如何在 Rust 的 FFI 中使用 C 预处理器宏?

Posted

技术标签:

【中文标题】如何在 Rust 的 FFI 中使用 C 预处理器宏?【英文标题】:How do I use C preprocessor macros with Rust's FFI? 【发布时间】:2014-02-24 11:27:28 【问题描述】:

我正在编写一些代码来连接用 C 编写的现有库。在我的 Rust 代码中,我希望能够使用来自 CPP 宏的值。如果我有一个如下所示的 C include.h:

#define INIT_FLAG 0x00000001

我希望能够像这样在 Rust 中使用它:

#[link(name="mylib")]
extern 
    pub static init_flag: c_int = INIT_FLAG;

我查看了其他 FFI 代码并且看到了很多人 在 Rust 中复制这些值,而不是从 FFI 中获取它们。 这似乎有点脆弱,我也希望能够处理 通过 CPP 宏定义的更复杂的东西。 只有在我确定我的 Rust 文件上运行 cpp 才会起作用 CPP 宏只用于简单的事情。

【问题讨论】:

【参考方案1】:

这是不可能的,我认为将来也不可能。 C 宏给它们带来了太多的问题。如果你想在你的 Rust 源代码上运行 cpp,你可以手动完成。

如果您不想这样做,并且如果有很多常量,并且您也不想将它们的值从 C 代码复制到 Rust,您可以创建一个 C 包装器,它将为全局变量提供这些值:

#define INIT_FLAG 0x00000001

...

const int init_flag = INIT_FLAG;

你编译这个文件,从中创建一个静态库并像往常一样链接到它:

$ gcc -c init_flag.c
$ ar r libinitflag.a init_flag.o

锈源:

use std::libc;

#[link(name="initflag", kind="static")]
extern 
    pub static init_flag: libc::c_int;

Rust 源几乎与您尝试实现的目标相同。但是,您将需要 C 粘合对象文件。

【讨论】:

我试过这样做,使用 build.rs 和 cc::Build::new() 但它似乎不喜欢链接器的工作方式。所有 -L 和 -l 标志看起来都不错,但它在链接时对静态变量有未定义的引用。 您可能需要将代码更改为 extern const int,因为 const ints 是私有的 @xcvr 不知道你是什么意思。在 C 中,所有项目都暴露在目标文件中,除非它们被标记为staticextern 只需要从 C 代码中引用 other 项,不需要“导出”任何东西,因为没有 static 所有项都被“导出”。 我的错误。这是 C++ 与 C 不同的地方之一。【参考方案2】:

这是不可能的,因为 C 宏常量在运行时不代表任何对象或实体。这是因为 cpp 预处理器甚至在编译发生之前就执行宏扩展(并处理其余指令)。考虑以下 sn-p:

#define INIT_FLAG 0x00000001

/* some code */

unsigned dummy()  return INIT_FLAG; 

/* some other code */

在 sn-p 上运行cpp 会产生预处理代码(所谓的编译单元,或翻译单元),所有出现的INIT_FLAG 都被替换为文字0x00000001:

unsigned dummy()  return 0x00000001; 

编译单元随后被编译,生成目标文件,但现在其中没有INIT_FLAG 的踪迹。因此,在链接目标文件时不能引用INIT_FLAG:它根本不包含这样的符号。

【讨论】:

以上是关于如何在 Rust 的 FFI 中使用 C 预处理器宏?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 C 字符串转换为 Rust 字符串并通过 FFI 转换回来?

如何将 Rust `Vec<T>` 暴露给 FFI?

通过 FFI 调用 Rust 函数时访问冲突

在 WSL 中学习 Rust ffi

如何创建 Rust 回调函数以传递给 FFI 函数?

如何从 NodeJS 中的 Rust FFI 函数返回字符串值?