articles

rgsl release 2.0: Huge cleanup and rewrite

The new rgsl version is now here! As a reminder, rgsl is a binding of the C GSL library (a mathematics library). It's been a work that took me months to finish but it's finally here!

GSL versions supported

The first big change of this new version is that I decided to drop the support of GSL < 2.0. It was just too hard to maintain two major versions at once and reduced the overall quality because of the compromises I had to make. This is now over and I'm pretty pleased with the result.

With this change, I added the support of the 2.X versions through the cargo features. If you want to use the 2.2 functions/types, you can specify it by using the v2_2 feature. Please note that if you enable the v2_2 feature, it'll also enable all the previous versions (so v2_1 in this case).

FFI part

Another big change is that I decided to split the FFI part from the library. In case the library doesn't implement a type/function yet, you'll still be able to use it. I have put all the FFI items into the gsl-sys crate to make it easier.

But that's not all! Considering the huge amount of FFI items provided by the GSL library, I decided it was time to generate it automatically by using bindgen. I had to modify a few things to make the output more like what I wanted but it has been a pretty great experience overall and it now allows me to regenerate the FFI items by simply running a binary (you can find how it works here).

Thanks to this, it allowed me to realize that a lot of FFI functions were expecting const pointers whereas they didn't in the previous version because I simply made typos. Errare human est.

Code cleanup/rewrite

rgsl is old (for a Rust crate). I started working on it back in 2014. Some parts of the code were not updated since then, so I guess it's obvious that Rust has improved a lot since then. Part of this release was to actually update the code to make it follow the new best practices.

I also added a few macros which allowed me to implement some traits automatically that I use internally and also make types declarations consistent between each others.

But the biggest improvement comes from the callback rework. Let's say things simply: the previous code was terrible. At least, it allowed me to realize how much my Rust knowledge and expertise improved since then. Now, you can use closures directly and everything works smoothly!

For the interested ones, here are the internal explanations on how it works:

Using Rust closure as a C function pointer

As you may guess, it's not possible to directly give a closure as a C function pointer. You have to give a function pointer in any case. The interesting part is that it's pretty common for C callbacks to allow you to give a void* type which can contain literally everything. And that's exactly what we do: this is our closure.

Now let's see how it's done exactly:

Runpub fn some_func<F: Fn(f64) -> f64>(value: f64, callback: F) {
    unsafe extern "C" fn trampoline<F: Fn(f64) -> f64>(
        x: f64,
        params: *mut ::std::os::raw::c_void,
    ) -> f64 {
        let f: &F = &*(params as *const F);
        f(x)
    }

    let callback = sys::gsl_function_struct {
        function: Some(trampoline::<F>),
        params: &callback as *const _ as *mut _,
    };

    unsafe { sys::some_c_func(&callback) };
}

Explanations now! unsafe extern "C" fn trampoline is the callback that the C code will call. Inside it, we'll simply get back our closure and call it.

In this case, this is a bit special because GSL handles its callbacks with a struct which contains the void* parameter as well as the function pointer. But the idea remains exactly the same. The whole trick being to call a callback which will in turn calls the closure. However, it's only possible because of the void* parameter. If there isn't one, it cannot work.

Conclusion

This update took a lot of my time and energy but I like the results. I hope it'll make your life easier!

Posted on the 09/11/2020 at 14:00 by @GuillaumeGomez

Next article

New doc comment handling in rustdoc

Previous article

geos 7.0 release: More type safety, update dependencies and use std TryFrom
Back to articles list
RSS feedRSS feed