Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use C preprocessor macros with Rust's FFI?

I'm writing some code that interfaces an existing library written in C. In my Rust code I'd like to be able to use values from CPP macros. If I have a C include.h that looks like this:

#define INIT_FLAG 0x00000001

I'd like to be able to use it in Rust like this:

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

I've looked at other FFI code and I see a lot of people duplicating these values in Rust instead of getting them from the FFI. This seems a little brittle, and I'd also like to be able to handle more complicated things that are defined via CPP macros. Running cpp over my Rust files would only work if I'm sure my CPP macros are only used for simple things.

like image 269
jdeseno Avatar asked Jan 31 '14 16:01

jdeseno


2 Answers

It is impossible, and I don't think it will be possible in the future. C macros bring too many problems with them. If you want to run cpp over your Rust sources, you can do it manually.

If you don't want to do it and if there is a lot of constants and you also don't want to copy their values from C code to Rust you can make a C wrapper which will provide global variables with these values:

#define INIT_FLAG 0x00000001

...

const int init_flag = INIT_FLAG;

You compile this file, create a static library from it and link to it as usual:

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

Rust source:

use std::libc;

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

Rust source is nearly identical to what you tried to achieve. You will need C glue object file, however.

like image 107
Vladimir Matveev Avatar answered Nov 16 '22 16:11

Vladimir Matveev


That's merely impossible because a C macro constant doesn't represent any object or entity at runtime. That's because the cpp preprocessor performs macro expansion (and handles the rest directives) even before compilation takes place. Consider the following snippet:

#define INIT_FLAG 0x00000001

/* some code */

unsigned dummy() { return INIT_FLAG; }

/* some other code */

Running cpp on the snippet yields preprocessed code (so called compilation unit, or translation unit) which has all occurences of INIT_FLAG replaced by the literal 0x00000001:

unsigned dummy() { return 0x00000001; }

The compilation unit then gets compiled, resulting in the object file, but now there's no trace of INIT_FLAG in it. Therefore, you cannot refer to INIT_FLAG when linking against the object file: it simply doesn't contain such symbol.

like image 25
nameless Avatar answered Nov 16 '22 17:11

nameless