2014年11月8日
上一篇提供了一个生成类的方法,但是,还是不够灵活,只能对类中的内容做一些替换,有更灵活的方法吗?
我们来想一想类的组成。一个类由很多个字段组成,每个字段可以是属性(变量),也可以是方法(函数)。
Haxe就提供了这样的方法:使用一个包含所有字段的数组来生成类。
还是来先看例子。
import haxe.macro.Context;
import haxe.macro.Expr;
class TypeBuildingMacro {
macro static public function
build(fieldName:String):Array<Field> {
var fields = Context.getBuildFields();
var newField = {
name: fieldName,
doc: null,
meta: [],
access: [AStatic, APublic],
kind: FVar(macro : String,
macro "my default"),
pos: Context.currentPos()
};
fields.push(newField);
return fields;
}
}
这段代码做了以下过程:获得了类原本的Field
数组,加入了一个自定义的变量的Field
:名字为函数传入的参数,内容为"my default"
的字符串,属性为static
和public
。如果想了解Field这个类型的具体结构,可以阅读Haxe源代码。
然后,用下面的方式来使用这个函数
@:build(TypeBuildingMacro.build("myFunc"))
class Main {
static public function main() {
trace(Main.myFunc); // my default
}
}
在生成的类中,我们可以找到这样的代码:
static
{
haxe.root.Main.myFunc = "my default";
}
//........
public static java.lang.String myFunc;
通过这样的方法,无论是生成一个全新的类,还是给现有的类加方法,都可以完成了。
下面一个例子是给已有的类加入toString函数,来自于我们的haxe-import-csv项目,略作修改。
macro public static function build():Array<Field> return
{
var fields = Context.getBuildFields();
var toStringDefExpr = macro function():String return $ { toStringExprMaker() };
fields.push( {
name: "toString",
doc: null,
meta: [{ name: ":overload", pos: Context.currentPos() }],
access: [APublic, AOverride],
kind: FFun(switch(toStringDefExpr.expr) {
case EFunction(_, f):f;
default: throw "Unreachable code!";
}),
pos: Context.currentPos()
});
fields;
}
private static inline function toStringExprMaker():Expr return
{
var fields = Context.getBuildFields();
var toStringExprBuilder:Expr = macro $v{Context.getLocalClass().get().name} + "(";
for (field in fields)
{
if (field.name == "new")
{
var constructFunction = switch(field.kind)
{
case FFun(f):
{
f;
}
default:
{
throw "Unreachable code!";
}
}
var isFirst:Bool = true;
for (arg in constructFunction.args)
{
if (isFirst)
{
toStringExprBuilder = macro $toStringExprBuilder + $v {arg.name} + "(" + Std.string( $i { arg.name } ) + ")";
isFirst = false;
}
else
{
toStringExprBuilder = macro $toStringExprBuilder + ", " + $v {arg.name} + "(" + Std.string( $i { arg.name } ) + ")";
}
}
break;
}
}
macro $toStringExprBuilder + ")";
}
这一部分相关的官方文档链接在这里。