I have a crate that produces a cdylib. Its interface is that there's a single function exported, and it's called with a function table, and it modifies the function table with pointers to its own (internal) functions. A copy of the original table also needs to be kept around. The exported function is only called once.
What is the proper way to deal with this? This is a simplified version of what I have with the newly-stable MaybeUninit:
use core::mem::MaybeUninit;
use core::ptr;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Table {
pub foo: Option<extern "C" fn()>,
}
static mut TABLE: MaybeUninit<Table> = MaybeUninit::uninit();
#[no_mangle]
pub extern "C" fn init(table: *mut Table) {
unsafe {
if let Some(table) = table.as_mut() {
ptr::write(TABLE.as_mut_ptr(), *table);
table.foo = Some(foo);
}
}
}
extern "C" fn foo() {
let table = unsafe { &*TABLE.as_ptr() };
(table.foo.unwrap())();
}
Is there some undefined behavior in there as far as the compiler is concerned? As far as runtime is concerned, TABLE
cannot be uninitialized when foo
is called.
Edit: While here: what would be a proper way to avoid defining the table as a struct of Option
s to avoid the unwrap()
in foo
? The initial call is guaranteed to contain a full table. Even if I want to be extra careful, the init function could avoid writing to the table if it's not complete in the first place. How do you go about that? Define two types? One with Options and one without?