Hi,
I'm working on getting a plugin API to work nice with Rust and I have some questions on what approach to how to implement it in a clean fashion.
The API looks like this
struct Reader {
void* priv_data;
uint8_t (*readData)(void* data);
};
struct Writer {
void* priv_data;
void (*writeData)(void* data, uint8_t t);
};
typedef struct BackendPlugin
{
const char* name;
void* (*createInstance)();
void (*destroyInstance)(void* userData);
void (*update)(void* userData, Reader* inEvents, Writer* outEvents);
} BackendPlugin;
Each plugin is responsible to implement these functions and the first one that gets called from the outside is createInstance() It's expected that the plugin allocates some data for internal state that gets later on passed to the other functions.
A simple plugin implementation can look like this
struct Plugin1Data { int tempData; };
static void* create_instance1() { return (void*)malloc(sizeof(Plugin1Data)); }
static void destroy_instance1(void* data) { free(data); }
static void update_1(void* userData, Reader* reader, Writer* writer) {
Plugin1Data* pluginData = (Plugin1Data*)userData;
// do something here
}
static BackendPlugin myPlugin1 = { "plugin1",
create_instance1,
destroy_instance1,
update_1 };
// Then an array is being exported like that they plugin loader will handle.
// exported
BackendPlugin* backend_plugins[] = {
&myPlugin1,
// more plugins,
0, // end marker
};
So what I would like the Rust version to be is to handle the above a bit behind the back and provies a safe interface for the code that is being sent in (Reader and Writer for example) this isn't that hard and I can do something like this to wrap it
#[repr(C)]
pub struct CReader {
priv_data: *mut ::libc::c_void,
read_data: extern fn(data: *mut ::libc::c_void),
}
pub struct Reader {
c_reader_api: *mut CReader,
}
impl Reader {
pub fn read_data(&self) {
unsafe {
((*self.c_reader_api).read_data)((*self.c_reader_api).private_data)
}
}
}
So the way I would like the rust code to look is something like this
struct MyBackend {
some_data: i32,
}
pub trait Backend {
fn update(&mut self, reader: &Reader, writer: &mut Writer);
}
impl Backend for MyBackend {
fn update(&mut self, reader: &Reader, writer: &mut Writer)
{
reader.read_data();
writer.write_data(0);
self.some_data = 0;
}
}
backend_plugins ... // how should this be constructed?
Also I will need some "trampoline function(s)" to for example update
Now this reader can be used something like this (called from C)
#[no_mangle]
pub extern fn update_from_c(user_data: *mut libc::c_void, reader_api: *mut CReader, writer_api: *mut CWriter) {
let reader = Reader { api_from_c : api };
let writer = Writer { api_from_c : writer };
// call the real code Which should something like this (pseudo)
MyBackend* backend = user_data;
(*backend.update)(reader, writer);
}
Also this update_from_c is something I would like to hide from the plugin implementor as it's a implementation detail (and each update needs to cast to the correct type). Perhaps a macro can be used for this?
Sorry for a long post. I really looked around for similar uses but couldn't find any and it's hard to search for it.