Help with bindgen, Ruby, and variadic C callbacks

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 ...
#define ANYARGS

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};

unsafe extern "C" fn pub_reverse(_klass: VALUE, mut input: VALUE) -> VALUE {
  // do some stuff...

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?

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.