Reputation: 2078
I have the following C code which is compiled as a .so
:
void (*vlog_startup_routines[])() = {
hello_register,
0
};
In Rust, I can declare functions with #[no_mangle]
. How would I expose a symbol called vlog_startup_routines
which is an array containing function pointers and also zero terminated?
Upvotes: 4
Views: 172
Reputation: 65935
You need to define a static
item whose type is an array. When defining a static
item, unfortunately, we need to specify the size of that array (as of Rust 1.13.0).
Function pointers in Rust are not considered unsafe to call (unless you have an unsafe fn
). However, a null pointer would not be safe to call, so Rust doesn't allow creating a null function pointer. But there's a trick: when T
is a pointer (whichever kind of pointer, including fat pointers and function pointers), Option<T>
has the same size as T
1, and None
is simply represented as a null pointer. Thus, we can define an array of Option<fn()>
values to get the desired result.
1 For other types, Option<T>
would be larger than T
to store the discriminant.
#[no_mangle]
#[allow(non_upper_case_globals)]
pub static vlog_startup_routines: [Option<fn()>; 2] = [
Some(hello_register),
None
];
If having to specify the array size annoys you, then you can use a macro that computes it for you. As a bonus, this macro adds the trailing None
and wraps each function in Some
.
macro_rules! one_for {
($_x:tt) => (1)
}
macro_rules! vlog_startup_routines {
($($func:expr,)*) => {
#[no_mangle]
#[allow(non_upper_case_globals)]
pub static vlog_startup_routines: [Option<fn()>; $(one_for!($func) +)* 1] = [
$(Some($func),)*
None
];
}
}
vlog_startup_routines! {
hello_register,
}
Note: the one_for
macro exists because we need to reference one of the parameter symbols in a repeating pattern (you can have multiple distinct repetitions, so the compiler needs to know which one you're referring to) but we don't care about its value.
Upvotes: 2