I have the following C code with a zero-terminated array of function pointers:
#include <stdio.h>
void hello_register(void) {
printf("hello_register called\n");
}
void (*vlog_startup_routines[])() = {
hello_register,
0
};
This code is compiled and linked to my Rust program using a Cargo build script. How can I call each of the function pointers in the array from Rust?
The problem here is that
vlog_startup_routines
is not a pointer. If you declare it as a pointer; it is an array. The symbol resolves to the address of the first item of the array. In C, if you have:then at the linker level, the symbol
i
is the address of the location containing the value 7,a
is also the address of a location containing an integer value (8), andp
is the address of a location containing a pointer to an integer. Another way of saying it is that the linker symbol is always the address of the variable.If you declare it as:
you're saying that
vlog_startup_routines
is a variable containing a function pointer, more like the Cvoid *vlog_startup_routines
.it's dereferencing taking the value stored at the address
vlog_startup_routines
, which is indeed the first pointer.The correct (nearly) code is:
Note that I use
Option<extern "C" fn()>
for the nullable function pointer as described here.This outputs, for me:
The reason I say "nearly" is that I'm not sure how to say it's an unknown-sized array. :-)
A combination of the previous two answers looks nicer:
The symbol
vlog_startup_routines
is not a pointer to a function pointer, it's an array of function pointers. When you use the namevlog_startup_routines
in C code, the array lvalue is coerced to a pointer. That doesn't mean that the variable stores a pointer!To most closely express this in Rust, we can define
vlog_startup_routines
as an array. The problem is that we don't know how many elements are in the array because it's NULL-terminated. To prevent any accidental misuse, we set the length to zero and only access elements though offsets of the raw pointer.We use
Option<extern "C" fn()>
for the nullable function pointer as described in the FFI chapter of The Rust Programming Language.You can call a single function pointer easily enough:
However, note that we and the C compiler are doing some trickery here: the array and the first element of the array have the same value:
To work around this, we grab a reference to the initial function and then use that to iterate though each of the function pointers. I've renamed
vlog_startup_routines
just to prevent any accidental misuse of it.This all feels pretty janky, so I wouldn't be surprised if there were a better solution available, but this does work.