使用 Haxe 宏实例化带参数的 class
Using Haxe macros to instantiate a class with parameters
我想在 Haxe 中用宏制作一些黑魔法,我有一个名为 Entity
的 class,我想添加一个带有 static
和 private
修饰符:
Pool.hx
:
package exp;
class Pool<T> {
public function new(clazz:Class<T>) {
}
}
Entity.hx
:
package exp;
@:build(exp.PoolBuilder.build())
class Entity {
public function new(){}
}
PoolBuilder.hx
:
package exp;
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
class PoolBuilder {
static public macro function build() : Array<Field> {
var fields = Context.getBuildFields();
var clazz = Context.getLocalClass();
var typePath = { name:"Pool", pack:["exp"], params: [TPType(TPath({name: "Entity", pack: ["exp"]}))] }
var pool = macro new $typePath(/* clazz? */);
fields.push({
name: "_pool",
access: [APrivate, AStatic],
pos: Context.currentPos(),
kind: FVar(macro: exp.Pool, pool)
});
return fields;
}
}
我对 typePath
参数有疑问,将 Class<T>
作为参数传递给构造函数。编译器显示此错误:
exp/Entity.hx:3: characters 1-7 : Invalid number of type parameters for exp.Pool
exp/Entity.hx:4: lines 4-6 : Defined in this class
有人知道怎么解决吗?
像这样手动构建字段有点乏味 - 我建议改用 class reification,您可以在其中将字段表示为常规的 Haxe 代码:
macro class {
static var _pool = new Pool(/* clazz */);
}
这完全绕过了 "Invalid number of type parameters" 问题 - 只需让类型推断来解决问题并省略 new Pool()
.
中的类型参数
构造函数调用的参数当然是可变的,所以我们仍然需要使用一些expression reification。 exp.Entity
是一个字段表达式,所以我们要用$p{}
。我们可以通过组合 clazz.pack
和 clazz.name
:
来构建它需要的类型路径
class PoolBuilder {
static public macro function build():Array<Field> {
var fields = Context.getBuildFields();
var clazz = Context.getLocalClass().get();
var path = clazz.pack.concat([clazz.name]); // ["exp", "Entity"]
var extraFields = (macro class {
static var _pool = new Pool($p{path});
}).fields;
return fields.concat(extraFields);
}
}
这会生成以下代码(可以在 exp/Entity.dump
和 -D dump=pretty
中看到):
static var _pool:exp.Pool<exp.Entity> = new exp.Pool(exp.Entity);
如果您更喜欢通过 fields.push({...})
添加字段而不是使用 class 具体化,您可以通过使用 null
作为 FVar(null, pool)
中的类型来触发类型推断:
static public macro function build() : Array<Field> {
var fields = Context.getBuildFields();
var clazz = Context.getLocalClass().get();
var path = clazz.pack.concat([clazz.name]); // ["exp", "Entity"]
var pool = macro new exp.Pool($p{path});
fields.push({
name: "_pool",
access: [APrivate, AStatic],
pos: Context.currentPos(),
kind: FVar(null, pool)
});
return fields;
}
这是对 path
使用 @gama11 技巧。这会生成与@gama11 答案完全相同的代码(并且可以用相同的方式检查)。
我想在 Haxe 中用宏制作一些黑魔法,我有一个名为 Entity
的 class,我想添加一个带有 static
和 private
修饰符:
Pool.hx
:
package exp;
class Pool<T> {
public function new(clazz:Class<T>) {
}
}
Entity.hx
:
package exp;
@:build(exp.PoolBuilder.build())
class Entity {
public function new(){}
}
PoolBuilder.hx
:
package exp;
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
class PoolBuilder {
static public macro function build() : Array<Field> {
var fields = Context.getBuildFields();
var clazz = Context.getLocalClass();
var typePath = { name:"Pool", pack:["exp"], params: [TPType(TPath({name: "Entity", pack: ["exp"]}))] }
var pool = macro new $typePath(/* clazz? */);
fields.push({
name: "_pool",
access: [APrivate, AStatic],
pos: Context.currentPos(),
kind: FVar(macro: exp.Pool, pool)
});
return fields;
}
}
我对 typePath
参数有疑问,将 Class<T>
作为参数传递给构造函数。编译器显示此错误:
exp/Entity.hx:3: characters 1-7 : Invalid number of type parameters for exp.Pool
exp/Entity.hx:4: lines 4-6 : Defined in this class
有人知道怎么解决吗?
像这样手动构建字段有点乏味 - 我建议改用 class reification,您可以在其中将字段表示为常规的 Haxe 代码:
macro class {
static var _pool = new Pool(/* clazz */);
}
这完全绕过了 "Invalid number of type parameters" 问题 - 只需让类型推断来解决问题并省略 new Pool()
.
构造函数调用的参数当然是可变的,所以我们仍然需要使用一些expression reification。 exp.Entity
是一个字段表达式,所以我们要用$p{}
。我们可以通过组合 clazz.pack
和 clazz.name
:
class PoolBuilder {
static public macro function build():Array<Field> {
var fields = Context.getBuildFields();
var clazz = Context.getLocalClass().get();
var path = clazz.pack.concat([clazz.name]); // ["exp", "Entity"]
var extraFields = (macro class {
static var _pool = new Pool($p{path});
}).fields;
return fields.concat(extraFields);
}
}
这会生成以下代码(可以在 exp/Entity.dump
和 -D dump=pretty
中看到):
static var _pool:exp.Pool<exp.Entity> = new exp.Pool(exp.Entity);
如果您更喜欢通过 fields.push({...})
添加字段而不是使用 class 具体化,您可以通过使用 null
作为 FVar(null, pool)
中的类型来触发类型推断:
static public macro function build() : Array<Field> {
var fields = Context.getBuildFields();
var clazz = Context.getLocalClass().get();
var path = clazz.pack.concat([clazz.name]); // ["exp", "Entity"]
var pool = macro new exp.Pool($p{path});
fields.push({
name: "_pool",
access: [APrivate, AStatic],
pos: Context.currentPos(),
kind: FVar(null, pool)
});
return fields;
}
这是对 path
使用 @gama11 技巧。这会生成与@gama11 答案完全相同的代码(并且可以用相同的方式检查)。