多数时候,宏的参数是表示为一个 enum Expr的实例的表达式。这样,它们被解析但没有类型化,意味着它们可以是符合Haxe的语法规则的任何内容。宏然后可以检视它们的结构,或者使用 haxe.macro.Context.typeof() 方法(尝试)得到它们的类型。
重要的是要理解宏的参数不保证被评估,所以任何预期的副作用不保证会出现。另一方面,同样重要的是理解一个参数表达式可能被一个宏复制和多次用在返回的表达式中:
import haxe.macro.Expr;
class Main {
static public function main() {
var x = 0;
var b = add(x++);
trace(x); // 2
}
macro static function add(e:Expr) {
return macro $e + $e;
}
}
宏 add 被调用,x++作为参数,并因此使用表达式具体化(第9.3.1节)返回 x++ + x++ ,使 x 被增加两倍。
由于Expr兼容任何可能的输入,Haxe提供了一个类型 haxe.macro.ExprOf 。大多数情况下,这个类型和Expr完全相同的,但是它允许限制接受的表达式的类型。这在结合宏和静态扩展(第6.3节)时可以提供帮助:
import haxe.macro.Expr;
using Main;
class Main {
static public function main() {
identity("foo");
identity(1);
"foo".identity();
// Int has no field identity
//1.identity();
}
macro static function identity(e:ExprOf<String>) {
return e;
}
}
两个对 identity 的直接调用被接受,即使参数声明为 ExprOf 。这可能有点出乎意料,Int 1 被接受,但是它是关于宏参数(第9.2节)中解释的一个合乎逻辑的结论:参数表达式从不被类型化,所以它不可能让编译器使用合一(第3.5节)检查它们的兼容性。
下两行使用静态扩展(注意 using Main)的则有所不同:对于这些它是强制首先类型化左侧(“foo” 和 1)来理解identity字段访问。这使它可以检查参数类型对应的类型,使 1.identity() 不把 Main.identity() 作为一个适用的字段。
一个宏可以被声明接受常数(第5.2节)参数:
class Main {
static public function main() {
const("foo", 1, 1.5, true);
}
macro static function const(s:String, i:Int, f:Float, b:Bool) {
trace(s);
trace(i);
trace(f);
trace(b);
return macro null;
}
}
通过这些,就不需要绕过表达式,因为编译器可以直接使用提供的常数。
如果一个宏最后的参数是 Array类型,宏可以以数组形式接受一个任意数量的额外参数:
import haxe.macro.Expr;
class Main {
static public function main() {
myMacro("foo", a, b, c);
}
macro static function myMacro(e1:Expr, extra:Array<Expr>) {
for (e in extra) {
trace(e);
}
return macro null;
}
}