Skip to content

Instantly share code, notes, and snippets.

@triptec
Last active February 22, 2019 08:31
Show Gist options
  • Save triptec/2bccf170cb0fd3dda05129143bcf9629 to your computer and use it in GitHub Desktop.
Save triptec/2bccf170cb0fd3dda05129143bcf9629 to your computer and use it in GitHub Desktop.
Calling a variadic function dynamically from rust

So I need to create a function wrapping a variadic c function (https://jcupitt.github.io/libvips/API/current/libvips-resample.html#vips-thumbnail) in rust.
This function takes some arguments and lastly a "NULL-terminated list of optional named arguments".
c function:

int vips_thumbnail (const char *filename, VipsImage **out, int width, ...); 

example calling it from rust with a optional height parameter:

vips_thumbnail(some_path.as_ptr(), &mut out_ptr, width as i32, "height\0".as_ptr(), height as i32, null() as *const c_char);

Simple enough.

But as I'd like to make a wrapping function I'd like to have those optional options in a struct like:

pub struct VipsThumbnailOptions {  
    pub height: Option<i32>,   
    pub size: Option<VipsSize>,  
    pub auto_rotate: Option<bool>,  
    pub crop: Option<VipsInteresting>,  
    pub linear: Option<bool>,  
    pub import_profile: Option<String>,  
    pub export_profile: Option<String>,  
    pub intent: Option<VipsIntent>,  
}  

and then in my wrapping function only pass the options that aren't None.

First thought:

if options.height.is_none() && options.size.is_none() && options.auto_rotate.is_none() && options.crop.is_none() && options.linear.is_none() && options.import_profile.is_none() && options.export_profile.is_none() && options.intent.is_none() {  
    vips_thumbnail(some_path.as_ptr(), &mut out_ptr, null() as *const c_char);
}
if options.height.is_some() && options.size.is_none() && options.auto_rotate.is_none() && options.crop.is_none() && options.linear.is_none() && options.import_profile.is_none() && options.export_profile.is_none() && options.intent.is_none() {
    vips_thumbnail(some_path.as_ptr(), &mut out_ptr, width as i32, "height\0".as_ptr(), options.height.unwrap() as i32, null() as *const c_char);
}
...

That would be the most naive way I think, I guess one could check for one option at the time and build a big tree of if else and make less checks but I'd say it wouldn't be fun to write either way, with those 8 options it would be 255 combinations and I believe there are other functions that take more, say 16 would mean 2^16=65536 combinations. So, my first question, is it possible write a macro to generate those if statements and function calls with something like call_vips_thumbnail_with_variadic_args!(some_path, out_ptr, width, height, size, auto_rotate, crop, linear, import_profile, export_profile, intent);

I think that even if it's possible to generate it and something I would like to some extent this might not be a sustainable solution, it is quite a few if statements event if generated.

So if this isn't possible what's next?
Well I found this (https://bsteinb.github.io/rsmpi/libffi/low/fn.prep_cif_var.html), seems that could be used to do the work and the closest example would be in the test suite of libffi (https://github.com/libffi/libffi/blob/65da63abc843fe448aaa86015d094cf016f325ba/testsuite/libffi.call/va_1.c) but doing it in rust is something I stuggle with. Also, is using libffi good?, performat? etc.
But perhaps mixing those methods? Like do the macro for the most used options say up to 6-8 and then if there's no match on those do the libffi way.

So my second question, is there any examples of using prep_cif_var in rust? If not, is there anyone who might want to help me create an example of doing just that?

Oh then there's the question of "have I missed something simpler, better or whatever"?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment