Rust:保持相同帧率的宏不起作用
Rust: macro to keep same frame rate does not work
为了保持一定的帧率,我一直在使用 std::thread::sleep()
等待足够的时间。计算它睡了多少会弄乱代码,所以我试着为它制作一个宏。现在是这样,但它不起作用:
macro_rules! fps30 {
($($body: expr;);*) => {
let time = std::time::Instant::now()
$($body)*
let time_elapsed = time.elapsed().as_micros();
if FRAME_TIME_30FPS_IN_MICROS > time_elapsed {
let time_to_sleep = FRAME_TIME_30FPS_IN_MICROS - time_elapsed;
std::thread::sleep(std::time::Duration::from_micros(time_to_sleep as u64));
}
};
}
我想这样使用它:
loop {
fps30!{
// do everything I want to do in the loop
}
}
当我不将它实现为宏时(通过直接将代码粘贴到循环中)它可以工作,并且每秒保持 29 帧(我猜不是 30,因为睡眠计算给出的开销很小).它在编译状态期间给出的错误:no rules expected the token 'p'
,其中 p
是我在宏中使用的 object/struct 实例。
有什么建议吗?
问题在于对 ;
的处理:
$($($body: expr;);*)
如果您想接受 ;
分隔的表达式列表,您应该写 $($($body: expr;)*)
或 $($($body: expr);*)
。前者表示以 ;
结尾的表达式列表,而后者是 ;
分隔表达式的列表。
差异很细微但很重要。如果同时添加两者,则需要编写两个 ;;
来分隔每个表达式。
如果您接受 ;
终止表达式的列表会更好,所以:
$($($body: expr;)*)
然后你在宏的主体中有几个错误,也与 ;
有关。在 $body
:
扩展之前,您缺少 ;
let time = std::time::Instant::now();
并且您在 $body
本身的扩展中缺少 ;
,因为 ;
不是捕获的 expr
:
的一部分
$($body;)*
经过这些更改,它可以正常工作,除非您尝试:
fps30!{
if a {
}
if a {
}
}
因为if
表达式后面没有;
!!!您可以尝试切换到:
$($($body: expr);*)
但是也不行,现在因为表达式之间没有;
!
您可以接受一个 $body: block
,但您将需要再写几个 {}
。不理想...
如果您真的想接受任何类型的代码块,我建议您接受 令牌树 (tt
) 列表。并且在扩展它们时,将它们括在 {}
中,以防它不以 ;
.
结尾
macro_rules! fps30 {
($($body: tt)*) => {
let time = std::time::Instant::now();
{ $($body)* }
let time_elapsed = time.elapsed().as_micros();
//...
};
}
现在您的宏将接受任何类型的语法,并会在宏中笨拙地展开它。
您甚至可以添加 $body
具有类型和值的可能性,并使 fps30
计算为该值`:
let res = { $($body)* };
//...
res
作为一个额外的好处,如果你写了一个语法错误,它会在编译代码时失败,而不是在扩展宏时失败,这样更容易调试。
为了保持一定的帧率,我一直在使用 std::thread::sleep()
等待足够的时间。计算它睡了多少会弄乱代码,所以我试着为它制作一个宏。现在是这样,但它不起作用:
macro_rules! fps30 {
($($body: expr;);*) => {
let time = std::time::Instant::now()
$($body)*
let time_elapsed = time.elapsed().as_micros();
if FRAME_TIME_30FPS_IN_MICROS > time_elapsed {
let time_to_sleep = FRAME_TIME_30FPS_IN_MICROS - time_elapsed;
std::thread::sleep(std::time::Duration::from_micros(time_to_sleep as u64));
}
};
}
我想这样使用它:
loop {
fps30!{
// do everything I want to do in the loop
}
}
当我不将它实现为宏时(通过直接将代码粘贴到循环中)它可以工作,并且每秒保持 29 帧(我猜不是 30,因为睡眠计算给出的开销很小).它在编译状态期间给出的错误:no rules expected the token 'p'
,其中 p
是我在宏中使用的 object/struct 实例。
有什么建议吗?
问题在于对 ;
的处理:
$($($body: expr;);*)
如果您想接受 ;
分隔的表达式列表,您应该写 $($($body: expr;)*)
或 $($($body: expr);*)
。前者表示以 ;
结尾的表达式列表,而后者是 ;
分隔表达式的列表。
差异很细微但很重要。如果同时添加两者,则需要编写两个 ;;
来分隔每个表达式。
如果您接受 ;
终止表达式的列表会更好,所以:
$($($body: expr;)*)
然后你在宏的主体中有几个错误,也与 ;
有关。在 $body
:
;
let time = std::time::Instant::now();
并且您在 $body
本身的扩展中缺少 ;
,因为 ;
不是捕获的 expr
:
$($body;)*
经过这些更改,它可以正常工作,除非您尝试:
fps30!{
if a {
}
if a {
}
}
因为if
表达式后面没有;
!!!您可以尝试切换到:
$($($body: expr);*)
但是也不行,现在因为表达式之间没有;
!
您可以接受一个 $body: block
,但您将需要再写几个 {}
。不理想...
如果您真的想接受任何类型的代码块,我建议您接受 令牌树 (tt
) 列表。并且在扩展它们时,将它们括在 {}
中,以防它不以 ;
.
macro_rules! fps30 {
($($body: tt)*) => {
let time = std::time::Instant::now();
{ $($body)* }
let time_elapsed = time.elapsed().as_micros();
//...
};
}
现在您的宏将接受任何类型的语法,并会在宏中笨拙地展开它。
您甚至可以添加 $body
具有类型和值的可能性,并使 fps30
计算为该值`:
let res = { $($body)* };
//...
res
作为一个额外的好处,如果你写了一个语法错误,它会在编译代码时失败,而不是在扩展宏时失败,这样更容易调试。