2

I'm want to be able to avoid code duplication when writing Rust template code to work with variadic number of template parameters, like so:

fn to_c_function_0<C>(closure: Box<C>) -> unsafe extern "C" fn(*mut c_void)
where
    C: FnMut(),
{
    unimplemented!()
}

fn to_c_function_1<C, Arg0>(closure: Box<C>) -> unsafe extern "C" fn(*mut c_void, Arg0)
where
    C: FnMut(Arg0),
{
    unimplemented!()
}

fn to_c_function_2<C, Arg0, Arg1>(closure: Box<C>) -> unsafe extern "C" fn(*mut c_void, Arg0, Arg1)
where
    C: FnMut(Arg0, Arg1),
{
    unimplemented!()
}

// and so on...

Is it possible to reduce code duplication without macros? How?

I was thinking it could be possible with a tuple of arguments (similar to Is it possible to emulate variadic generics in Rust?) but don't know if that's possible


Edit:

The tuple of arguments solutions doesn't seem to work

use std::os::raw::*;

type CFunction0 = unsafe extern "C" fn(*mut c_void);
type CFunction2 = unsafe extern "C" fn(*mut c_void, c_int, *const c_char);

fn to_c_function<C, Arg>(closure: Box<C>) -> unsafe extern "C" fn(*mut c_void, Arg)
where
    C: FnMut(Arg),
{
    unsafe extern "C" fn call<Arg>(data: *mut c_void, arg0: Arg) {}
    call::<Arg>
}

fn main() {
    // all lines below fail to compile
    let c_0_a: CFunction0 = to_c_function(Box::new(|()| {}));
    let c_0_b: CFunction0 = to_c_function(Box::new(|| {}));
    let c_1_a: CFunction2 = to_c_function(Box::new(|(x, y)| {}));
    let c_1_b: CFunction2 = to_c_function(Box::new(|x, y| {}));
}

2
  • Macros are a very good idea to reduce duplication here, why don't you want to use them? Commented Sep 20 at 16:45
  • @ChayimFriedman I suppose only because, coming from C++, they don't seem to be natural for me yet. Thanks! Commented Sep 22 at 8:08

1 Answer 1

2

You can certainly use macros to implement all the functions you need up to an arbitrary number of arguments, but they will all be named differently. Let me propose an alternative: provide a single function that takes a boxed closure with a single argument.

fn to_c_function<C, Arg>(closure: Box<C>) -> unsafe extern "C" fn(*mut c_void, Arg0)
where
    C: FnMut(Arg),
{
    unimplemented!()
}

Arg can be any valid type here, so:

  • If no argument is needed, Arg can be ().
  • If one argument is needed, just make it Arg.
  • If you need multiple arguments, you can have Arg be a tuple of however many values you need.

It's not ideal, but I think it's more idiomatic than ~10 differently-named functions.

Sign up to request clarification or add additional context in comments.

3 Comments

If only I could "unpack" the tuple, it would be great. But for my use case it doesn't work. I will edit the original post to show it.
Wait, are the arguments here being provided by C, e.g. is C calling the closure? The original question made it look more like a function was being passed to C for C to pass back to Rust later.
Yes, C will call the closure.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.