Hi all,
I am developing a new crate to automatically build bindings to Ruby in Rust. Things are mostly working great, but I am running into an issue with C variadic arguments. In Ruby land, many callback functions accept ANYARGS
to represent an arbitrary list of arguments. The code looks something like this:
#ifdef __cplusplus
#define ANYARGS ...
#else
#define ANYARGS
#endif
void rb_define_module_function(VALUE module_class, const char *name_of_function, VALUE (*func)(ANYARGS), int arity);
In this C code, defining a module function in ruby specifies you pass a pointer to the function (which accepts ANYARGS
, and the arity of that function.
The problem
The issue I encounter is that bindgen (correctly) interprets this as a function which accepts no arguments. However, it does take arguments IRL. Here is the rust code that is generated:
extern "C" {
pub fn rb_define_module_function(
arg1: VALUE,
arg2: *const ::libc::c_char,
arg3: ::core::option::Option<unsafe extern "C" fn() -> VALUE>,
arg4: ::libc::c_int,
);
}
Ideal Ergonomics
Ideally, I could codegen Rust code that would allow this code to "Just Work ™️", meaning Rust would accept it as a valid compilation.
extern crate rb_sys;
use rb_sys::*;
use std::ffi::{CString, CStr};
use std::os::raw::{c_long};
#[no_mangle]
unsafe extern "C" fn pub_reverse(_klass: VALUE, mut input: VALUE) -> VALUE {
// do some stuff...
}
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn Init_rust_ruby_example() {
let name = CString::new("RustRubyExample").unwrap();
let function_name = CString::new("reverse").unwrap();
unsafe {
let klass = rb_define_module(name.as_ptr());
rb_define_module_function(klass, function_name.as_ptr(), Some(pub_reverse), 1)
}
}
Currently, I have only been able to achieve this by using mem::transmute
or simply making the callback type a *const c_void
. Both options seem not great.
Does anyone have any potential ideas for making this better?