Skip to content

[feature] Add support for reverse bindgen function implementation #3310

@TroyKomodo

Description

@TroyKomodo

Problem

The goal is to have c headers be the source of truth and then have bindgen generate function signatures that can be implemented by rust code.

the following code is generated by this header

/**
 * @brief Set the last error message
 * @param message Error message
 */
void error_set_last_message(const char* message);

/**
 * @brief Get the last error message
 * @return Last error message or NULL if no error occurred
 */
const char* error_last_message();
unsafe extern "C" {
    #[doc = " @brief Set the last error message\n @param message Error message"]
    pub fn error_set_last_message(message: *const ::std::os::raw::c_char);
}
unsafe extern "C" {
    #[doc = " @brief Get the last error message\n @return Last error message or NULL if no error occurred"]
    pub fn error_last_message() -> *const ::std::os::raw::c_char;
}

I would like bindgen to generate something like the following

pub trait error_api {
    #[doc = " @brief Set the last error message\n @param message Error message"]
    unsafe extern "C" fn error_set_last_message(message: *const ::std::os::raw::c_char);
    #[doc = " @brief Get the last error message\n @return Last error message or NULL if no error occurred"]
    unsafe extern "C" fn error_last_message() -> *const ::std::os::raw::c_char;
}

With some sort of builder setting like

let bindings = bindgen::builder()
    .function_impl_trait("error_.*", "error_api");

Then using this would be like

pub struct Extern;

impl error_api for Extern {
    #[unsafe(no_mangle)]
    unsafe extern "C" fn error_set_last_message(message: *const ::std::os::raw::c_char) {
        ...
    }

    #[unsafe(no_mangle)]
    unsafe extern "C" fn error_last_message() -> *const ::std::os::raw::c_char {
        ...
    }
}

Motivation

A lot of code today is written in c++ / c with a public c-api that is exported so that other languages / customers can interact with the underlying code without having its source. In an effort to move more code over to rust having some way of using the c headers as a source of truth would make it easier to incrementally port legacy code bases over to rust.

Alternative solutions

  1. Using a tool like cbindgen to generate the header files from the rust code. While this solution is good i would still prefer to have the headers be the source of truth for the definitions of the functions.

  2. Bindgen can be extended to generate something like the following instead of the extern "C" blocks.

#[doc = " @brief Set the last error message\n @param message Error message"]
#[repr(transparent)]
pub struct error_set_last_message(pub unsafe extern "C" fn(message: *const ::std::os::raw::c_char));

#[doc = " @brief Get the last error message\n @return Last error message or NULL if no error occurred"]
#[repr(transparent)]
pub struct error_last_message(pub unsafe extern "C" fn() -> *const ::std::os::raw::c_char);

And then its up to the library author to implement and expose these functions and can use these types to validate they are the same as the header. Also has the advantage of allowing bindgen to be used with dlopen


Related rust-lang/rust#58493

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions