Reputation: 490
I need a macro that will call functions with different numbers of arguments or a macro that will generate a valid argument list from its (repeating) parameters.
I am fine with explicitly giving the information about the number of arguments to the macro, but I can't figure out how to generate the argument list for the function - I always stumble on the macros returning expressions rather than token tree.
I made the following playground example:
macro_rules! call (
($f: expr, $($params:tt)*) => {
$f(make_params!($($params:tt)*))
};
);
macro_rules! make_params {
() => {};
(I $($params: tt)*) => {
1, make_params!($($params:tt)*)
};
}
fn foo(a: i32, b: i32, c: i32) {
println!("foo: {} {} {}", a, b, c);
}
fn bar(a: i32, b: i32) {
println!("bar: {} {}", a, b);
}
fn main() {
call!(foo, I I I);
call!(bar, I I);
}
The compiler complains with the following:
error: macro expansion ignores token `,` and any following
--> src/main.rs:10:10
|
10 | 1, make_params!($($params:tt)*)
| ^
|
note: caused by the macro expansion here; the usage of `make_params!` is likely invalid in expression context
--> src/main.rs:3:12
|
3 | $f(make_params!($($params:tt)*))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
How can I treat the return of make_params!
as a token stream (or such) rather than expression?
My real use case is a bit more involved than this toy example. My functions have multiple parameter types which are constructed in different ways. In my case, just making macros call1
, call2!
, ... does not seem like a good solution, as I would need the likes of call_IIOOI
, call_IIIO
, etc.
Upvotes: 4
Views: 4729
Reputation: 23463
You need to build the function call progressively as you go and only emit it at once in the end:
macro_rules! call (
($f: expr, $($params:tt)*) => {
make_call!($f, () $($params)*)
};
);
macro_rules! make_call {
($f: expr, ($($args:tt)*)) => { $f($($args)*) };
($f: expr, () I $($params:tt)*) => {
make_call!($f, (1) $($params)*)
};
($f: expr, ($($args:tt)*) I $($params:tt)*) => {
make_call!($f, ($($args)*, 1) $($params)*)
};
}
Upvotes: 4