How can I add 'unsafe extern "C"' to a fn type in a declarative macro?

Viewed 27

I would like to be able to pass a fn type as argument into macro_rules, and then generate code that adds unsafe extern "C" to the type.

For example, I would like:

define_ext_fn_type!(fn(i32), my_function_type);

to generate:

type my_function_type = unsafe extern "C" fn(i32);

but my attempt doesn't work:

macro_rules! define_ext_fn_type {
    ($t:ty, $name:ident) => {
        type $name = unsafe extern "C" $t;
    };
}

define_ext_fn_type!(fn(i32), my_function);
error: expected `fn`, found `fn(i32)`
 --> src/lib.rs:3:40
  |
3 |         type $name = unsafe extern "C" $t;
  |                                        ^^ expected `fn`
...
7 | define_ext_fn_type!(fn(i32), my_function);
  | ----------------------------------------- in this macro invocation
  |
  = note: this error originates in the macro `define_ext_fn_type` (in Nightly builds, run with -Z macro-backtrace for more info)

I understand that macros are not just text replacement, and that I can't just modify the type stored in $t by adding a couple of keywords in front of it, but how would I achieve this? Or is this simply not possible with macro_rules and requires proc macros?

1 Answers

You cannot do that with macro_rules! (although a proc macro can unwrap the ty fragement), but you can capture the raw parts of fn type:

macro_rules! define_ext_fn_type {
    (fn( $($arg:ty),* $(,)? ) $( -> $ret:ty )?, $name:ident) => {
        type $name = unsafe extern "C" fn( $($arg),* ) $( -> $ret )?;
    };
}
Related