定义将参数传递给函数的宏
Defining a macro that passes params to a function
这是 的跟进。我正在尝试编写一个宏 call!
,它将参数作为一个向量并将它们传递给一个函数。例如,
fn add(x:u32, y:u32) -> u32 { x + y }
let params: [u32; 2] = [2 ,3];
assert_eq!(call!(&add, params), 5);
对于静态数量的参数,宏很简单:
macro_rules! call {
($function:expr, $params:expr) => {
$function($params[0], $params[1])
};
}
我不知道如何让它适用于可变数量的参数。我已经尝试了一些东西,但它们都 运行 变成了两个问题之一:
- 我尝试在宏之间传递
$params[0],
等不完整的代码片段,这是不允许的。
- 我尝试匹配长度并得到
expected <x> parameters
个错误。
宏的问题是它们没有任何类型信息,您需要知道函数有多少参数,或者数组有多长(并假设两者相等)。因此,如果您想纯粹使用宏来执行此操作,则必须明确提供该信息,例如call!(&add, params, 2)
.
但是,您可以使用特征来解决这个问题,因为它们 具有类型信息。您可以创建特征 FnExpandArgs
:
// Helper trait for calling a function with a list of arguments.
trait FnExpandArgs<Args> {
// Return type.
type Output;
/// Call function with an argument list.
fn call_expand_args(&self, args: Args) -> Self::Output;
}
然后你可以为你需要的每个参数数量实现它。不幸的是,每个数字都需要自己的实现,所以会有点冗长。
// Example implementation for 2 arguments
impl<F, T, R> FnExpandArgs<[T; 2]> for F
where
F: Fn(T, T) -> R
{
type Output = R;
fn call_expand_args(&self, args: [T; 2]) -> R {
// Expand array of arguments
let [arg0, arg1] = args;
// Call function
self(arg0, arg1)
}
}
并且,假设您在作用域中具有特征,您现在可以在任何采用相同参数的函数上调用它:
fn add(x:u32, y:u32) -> u32 { x + y }
let params: [u32; 2] = [2 ,3];
assert_eq!(add.call_expand_args(params), 5);
你也可以基于此实现你想要的宏,如果你真的想要的话,虽然我不确定它会增加更多的价值:
marco_rules! call {
($function:expr, $params:expr) => {
FnExpandArgs::call_expand_args(&$function, $params)
}
}
fn add(x:u32, y:u32) -> u32 { x + y }
let params: [u32; 2] = [2 ,3];
assert_eq!(call!(add, params), 5);
此方法的另一个优点是您还可以为数组以外的类型实现它,例如元组:
impl<F, T0, T1, R> FnExpandArgs<(T0, T1)> for F
where
F: Fn(T0, T1) -> R
{
type Output = R;
fn call_expand_args(&self, args: (T0, T1)) -> R {
let (arg0, arg1) = args;
self(arg0, arg1)
}
}
fn shift(x:u32, y:u8) -> u32 { x << y }
let params: (u32, u8) = (2, 3);
assert_eq!(shift.call_expand_args(params), 16);
嗯,这是一个可能的解决方案。不是最漂亮的,但它可以工作。基本上,在宏调用时,我们会发送一些宏可以处理的参数并推断出正确的函数调用。
macro_rules! call {
($function:expr, 1, $params:expr) => {
$function($params[0])
};
($function:expr, 2, $params:expr) => {
$function($params[0], $params[1])
};
($function:expr, 3, $params:expr) => {
$function($params[0], $params[1], $params[2])
};
}
fn main(){
fn add(x:u32, y:u32) -> u32 { x + y }
fn incr(x:u32) -> u32 { x + 1 }
let params1: [u32; 1] = [2];
let mut params2: [u32; 2] = [2,3];
assert_eq!(call!(&incr, 1, params1), 3);
assert_eq!(call!(&add, 2, params2), 5);
params2[0]=4;
assert_eq!(call!(&add, 2, params2), 7);
}
这是 call!
,它将参数作为一个向量并将它们传递给一个函数。例如,
fn add(x:u32, y:u32) -> u32 { x + y }
let params: [u32; 2] = [2 ,3];
assert_eq!(call!(&add, params), 5);
对于静态数量的参数,宏很简单:
macro_rules! call {
($function:expr, $params:expr) => {
$function($params[0], $params[1])
};
}
我不知道如何让它适用于可变数量的参数。我已经尝试了一些东西,但它们都 运行 变成了两个问题之一:
- 我尝试在宏之间传递
$params[0],
等不完整的代码片段,这是不允许的。 - 我尝试匹配长度并得到
expected <x> parameters
个错误。
宏的问题是它们没有任何类型信息,您需要知道函数有多少参数,或者数组有多长(并假设两者相等)。因此,如果您想纯粹使用宏来执行此操作,则必须明确提供该信息,例如call!(&add, params, 2)
.
但是,您可以使用特征来解决这个问题,因为它们 具有类型信息。您可以创建特征 FnExpandArgs
:
// Helper trait for calling a function with a list of arguments.
trait FnExpandArgs<Args> {
// Return type.
type Output;
/// Call function with an argument list.
fn call_expand_args(&self, args: Args) -> Self::Output;
}
然后你可以为你需要的每个参数数量实现它。不幸的是,每个数字都需要自己的实现,所以会有点冗长。
// Example implementation for 2 arguments
impl<F, T, R> FnExpandArgs<[T; 2]> for F
where
F: Fn(T, T) -> R
{
type Output = R;
fn call_expand_args(&self, args: [T; 2]) -> R {
// Expand array of arguments
let [arg0, arg1] = args;
// Call function
self(arg0, arg1)
}
}
并且,假设您在作用域中具有特征,您现在可以在任何采用相同参数的函数上调用它:
fn add(x:u32, y:u32) -> u32 { x + y }
let params: [u32; 2] = [2 ,3];
assert_eq!(add.call_expand_args(params), 5);
你也可以基于此实现你想要的宏,如果你真的想要的话,虽然我不确定它会增加更多的价值:
marco_rules! call {
($function:expr, $params:expr) => {
FnExpandArgs::call_expand_args(&$function, $params)
}
}
fn add(x:u32, y:u32) -> u32 { x + y }
let params: [u32; 2] = [2 ,3];
assert_eq!(call!(add, params), 5);
此方法的另一个优点是您还可以为数组以外的类型实现它,例如元组:
impl<F, T0, T1, R> FnExpandArgs<(T0, T1)> for F
where
F: Fn(T0, T1) -> R
{
type Output = R;
fn call_expand_args(&self, args: (T0, T1)) -> R {
let (arg0, arg1) = args;
self(arg0, arg1)
}
}
fn shift(x:u32, y:u8) -> u32 { x << y }
let params: (u32, u8) = (2, 3);
assert_eq!(shift.call_expand_args(params), 16);
嗯,这是一个可能的解决方案。不是最漂亮的,但它可以工作。基本上,在宏调用时,我们会发送一些宏可以处理的参数并推断出正确的函数调用。
macro_rules! call {
($function:expr, 1, $params:expr) => {
$function($params[0])
};
($function:expr, 2, $params:expr) => {
$function($params[0], $params[1])
};
($function:expr, 3, $params:expr) => {
$function($params[0], $params[1], $params[2])
};
}
fn main(){
fn add(x:u32, y:u32) -> u32 { x + y }
fn incr(x:u32) -> u32 { x + 1 }
let params1: [u32; 1] = [2];
let mut params2: [u32; 2] = [2,3];
assert_eq!(call!(&incr, 1, params1), 3);
assert_eq!(call!(&add, 2, params2), 5);
params2[0]=4;
assert_eq!(call!(&add, 2, params2), 7);
}