0

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.

sam
  • 488
  • 8
  • 21
  • 1
    `extern "C"` — that's the calling convention; there's no other "configuration" for it. – Shepmaster Apr 15 '19 at 19:28
  • *`&mut [u8]`* — this is **not** a type that can be passed to C. C has no concept of slices. See [*The Rust FFI Omnibus* - Rust functions with slice arguments](http://jakegoulding.com/rust-ffi-omnibus/slice_arguments/). – Shepmaster Apr 15 '19 at 19:29
  • I believe your question is answered by the answers of [How can I get an array or a slice from a raw pointer?](https://stackoverflow.com/q/27150652/155423). If you disagree, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Apr 15 '19 at 19:32

0 Answers0