I have a crate that holds a function pointer (defined in C), and calls some predefined arguments. I suspect that I cannot get the call to comply with the C calling convention. I have reviewed the relevant Rust docs, and believe that I am specifying the calling convention where appropriate...
A simple example to illustrate:
The Rust crate with a simple struct containing a buffer and function pointer to pack it:
pub type Callback = extern "C" fn(buffer: &mut [u8], current_index: u32, max_index: u32) -> u32;
const BUFFER_SIZE: usize = 32_767;
pub type DataBuffer = [u8; BUFFER_SIZE];
#[no_mangle]
pub struct Foo {
buffer: DataBuffer,
pub current_index: u32,
callback: Option<Callback>,
}
impl Foo {
fn new() -> Self {
Foo {
current_index: 0,
buffer: [0u8; BUFFER_SIZE],
callback: None,
}
}
fn add_callback(&mut self, callback: Callback) {
self.callback = Some(callback);
}
fn call_callback(&mut self) {
let increment = self.callback.expect("")(
self.buffer
.get_mut(self.current_index as usize..)
.expect(""),
self.current_index,
BUFFER_SIZE as u32,
);
self.current_index += increment;
}
}
#[no_mangle]
pub extern "C" fn FooNew() -> Box<Foo> {
Box::new(Foo::new())
}
#[no_mangle]
pub extern "C" fn FooAddCallback(foo: &mut Foo, callback: Callback) {
foo.add_callback(callback);
}
#[no_mangle]
pub extern "C" fn FooCallCallback(foo: &mut Foo) {
foo.call_callback();
}
#[no_mangle]
pub extern "C" fn FooPrint(foo: &mut Foo) {
for c in foo.buffer.iter() {
print!("{}", *c as char);
}
println!("\n");
}
and some corresponding C code to call it:
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Foo Foo;
extern Foo* FooNew();
extern void FooAddCallback(Foo* foo, void* callback);
extern void FooCallCallback(Foo* foo);
extern void FooPrint(Foo* foo);
unsigned int getHeaders(char* buffer, unsigned int current_index, unsigned int max_index)
{
if (current_index > max_index)
{
printf("\nError current_index(%d) > max_index(%d) !!\nThis is most likely a C-api byte order problem\n", current_index, max_index);
exit(-1);
}
strcpy(buffer, "~HEADER~\0");
return 9;
}
int main()
{
Foo* foo = FooNew();
FooAddCallback(foo, getHeaders);
FooCallCallback(foo);
FooCallCallback(foo);
FooCallCallback(foo);
FooPrint(foo);
return 0;
}
When run, the second two arguments to the callback get passed incorrectly (i.e. the parameter for current_index
gets passed to max_index
and vice-versa, in the example above its passing 0 to max_index
and 32767 to current_index
). Reversing the order of these args(seemingly making the calling order read incorrect) causes it to execute correctly...
I suspect I am just calling the callback wrong, or need to specify the calling convention differently.
Ah right, this question was mentioned. This is the direct inverse of their question, but the ethos is the same. Arrays/Slices cannot be directly passed via the FFI.