jck
jck

Reputation: 2078

Exposing a C symbol containing a zero terminated array of function pointers from Rust to C

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

Answers (1)

Francis Gagné
Francis Gagné

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 T1, 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

Related Questions