模仿 CraftingInterpeter 的 BINARY_OP 宏
Mimicking CraftingInterpeter's BINARY_OP macro
在尝试遵循优秀的 Crafting Interpreters book 但使用 Rust 而不是 C 时遇到了这个问题。
这本书构建了一个基于堆栈的虚拟机,其中我有一个简单的 Rust 版本,看起来像这样:
struct VM {
stack: Vec<Value>
}
impl VM {
//...
pub fn interpret(op: OpCode) {
match op {
Op::Add => {
let r = self.stack.pop();
let l = self.stack.pop();
self.stack.push(l + r);
}
// Repeated for -, *, and /
}
}
}
这本书使用 C 风格的宏来防止 copy/pasting 所有 binop 使用相同的代码。
#define BINOP(op) \
do { \
double r = pop(); \
double l = pop(); \
push(l op r); \
} while (false)
void interpret(VM *vm, OpCode op) {
switch op {
case OP_ADD: BINOP(+); break;
case OP_SUB: BINOP(-); break;
case OP_MUL: BINOP(*); break;
case OP_DIV: BINOP(/); break;
}
我尝试使用 Rust 宏做类似的事情
macro_rules! binop {
($op:tt) => {
{
let l = self.stack.pop();
let r = self.stack.pop();
self.stack.push(l $op r) };
}
}
// ... inside VM impl ...
pub fn interpret(&mut self, op: OpCode) {
match op {
Op::Add => binop!(+)
// Repeated for -, *, and /
}
}
但是宏确实知道 self
是什么,我不确定让宏知道 self
的最佳方法是什么。
有没有办法在 Rust 中实现类似于 C 宏的东西?
您可以将 self
作为宏的参数传递:
macro_rules! binop {
($self:expr, $op:tt) => {{
let l = $self.stack.pop();
let r = $self.stack.pop();
$self.stack.push(l $op r);
}};
}
或者如果您在别处不需要它,您可以在内部函数定义宏:
pub fn interpret(&mut self, op: OpCode) {
macro_rules! binop {
($op:tt) => {{
let l = self.stack.pop();
let r = self.stack.pop();
self.stack.push(l $op r);
}};
}
match op {
Op::Add => binop!(+)
// Repeated for -, *, and /
}
}
另一种选择是使用闭包而不是宏:
impl VM {
pub fn interpret(&mut self, op: OpCode) {
match op {
OpCode::Add => self.binop(|l, r| l + r),
OpCode::Sub => self.binop(|l, r| l - r),
OpCode::Mul => self.binop(|l, r| l * r),
OpCode::Div => self.binop(|l, r| l / r),
}
}
pub fn binop(&mut self, f: impl Fn(Value, Value) -> Value) {
// Of course, in a real case you should handle unwrap:
let l = self.stack.pop().unwrap();
let r = self.stack.pop().unwrap();
self.stack.push(f(l, r));
}
}
在尝试遵循优秀的 Crafting Interpreters book 但使用 Rust 而不是 C 时遇到了这个问题。
这本书构建了一个基于堆栈的虚拟机,其中我有一个简单的 Rust 版本,看起来像这样:
struct VM {
stack: Vec<Value>
}
impl VM {
//...
pub fn interpret(op: OpCode) {
match op {
Op::Add => {
let r = self.stack.pop();
let l = self.stack.pop();
self.stack.push(l + r);
}
// Repeated for -, *, and /
}
}
}
这本书使用 C 风格的宏来防止 copy/pasting 所有 binop 使用相同的代码。
#define BINOP(op) \
do { \
double r = pop(); \
double l = pop(); \
push(l op r); \
} while (false)
void interpret(VM *vm, OpCode op) {
switch op {
case OP_ADD: BINOP(+); break;
case OP_SUB: BINOP(-); break;
case OP_MUL: BINOP(*); break;
case OP_DIV: BINOP(/); break;
}
我尝试使用 Rust 宏做类似的事情
macro_rules! binop {
($op:tt) => {
{
let l = self.stack.pop();
let r = self.stack.pop();
self.stack.push(l $op r) };
}
}
// ... inside VM impl ...
pub fn interpret(&mut self, op: OpCode) {
match op {
Op::Add => binop!(+)
// Repeated for -, *, and /
}
}
但是宏确实知道 self
是什么,我不确定让宏知道 self
的最佳方法是什么。
有没有办法在 Rust 中实现类似于 C 宏的东西?
您可以将 self
作为宏的参数传递:
macro_rules! binop {
($self:expr, $op:tt) => {{
let l = $self.stack.pop();
let r = $self.stack.pop();
$self.stack.push(l $op r);
}};
}
或者如果您在别处不需要它,您可以在内部函数定义宏:
pub fn interpret(&mut self, op: OpCode) {
macro_rules! binop {
($op:tt) => {{
let l = self.stack.pop();
let r = self.stack.pop();
self.stack.push(l $op r);
}};
}
match op {
Op::Add => binop!(+)
// Repeated for -, *, and /
}
}
另一种选择是使用闭包而不是宏:
impl VM {
pub fn interpret(&mut self, op: OpCode) {
match op {
OpCode::Add => self.binop(|l, r| l + r),
OpCode::Sub => self.binop(|l, r| l - r),
OpCode::Mul => self.binop(|l, r| l * r),
OpCode::Div => self.binop(|l, r| l / r),
}
}
pub fn binop(&mut self, f: impl Fn(Value, Value) -> Value) {
// Of course, in a real case you should handle unwrap:
let l = self.stack.pop().unwrap();
let r = self.stack.pop().unwrap();
self.stack.push(f(l, r));
}
}