I'm trying to build Octave functions in Rust. Octave's API is in C++, so I've generated bindings using rust-bindgen. I'm currently working through the problems that occur when trying to generate bindings that include std::string
. It would be nice if I could leave it opaque and valid pointer to a C++ std::string
. Would it be possible to build a utility function on the C++ side any time I needed to pass in a C++ std::string
?
I was naive when I first attempted this. It is clearly wrong. A Rust std::ffi:CString
is for C strings, not C++ strings. I found this recent blog helpful when comparing the two. My first attempt looks like this:
#![allow(non_snake_case)]
#![allow(unused_variables)]
extern crate octh;
// https://thefullsnack.com/en/string-ffi-rust.html
use std::ffi::CString;
#[no_mangle]
pub unsafe extern "C" fn Ghelloworld (shl: *const octh::root::octave::dynamic_library, relative: bool) -> *mut octh::root::octave_dld_function {
let name = CString::new("helloworld").unwrap();
let pname = name.as_ptr() as *const octh::root::std::string;
std::mem::forget(pname);
let doc = CString::new("Hello World Help String").unwrap();
let pdoc = doc.as_ptr() as *const octh::root::std::string;
std::mem::forget(pdoc);
octh::root::octave_dld_function_create(Some(Fhelloworld), shl, pname, pdoc)
}
pub unsafe extern "C" fn Fhelloworld (args: *const octh::root::octave_value_list, nargout: ::std::os::raw::c_int) -> octh::root::octave_value_list {
let list_ptr = ::std::ptr::null_mut();
octh::root::octave_value_list_new(list_ptr);
::std::ptr::read(list_ptr)
}
I need to pass in the function name and documentation as strings to octave_dld_function_create
. I wish there was a CppString
that I could use instead. Any suggestions on how to proceed?
This is a classic FFI issue and the solution is to use a "hour-glass" design: Language A <=> Common ABI <=> Language B.
It could be possible, of course, to evolve bindgen so that it can faithfully reproduce a C++ ABI, but in practice it would require a full C++ compiler which is probably too much effort.
Using a "hour-glass" design, each of the languages with a difficult ABI use their own specialized toolchain to convert to a specific well-known ABI. In this case, it would be C++ <=> C <=> Rust.
A possible solution is to create a C wrapper library around the C++ API, and then use bindgen on that. This is what the LLVM and Clang projects do.
It is the simplest solution, and the Octavo project may very well be willing to integrate such an octavo-c facade in-tree (which is always best to guarantee it's up-to-date).
Another solution would be to create a C++ companion library for bindgen, which takes care of providing a C-ABI for common C++ types (such as std::string
). This would be a more difficult endeavor, especially since:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With