I'm trying to make some dark magic with macros in Haxe, I have a class named Entity
and I want to add a pool with the static
and private
modifiers:
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;
}
}
I have a problem with the typePath
params, and passing a Class<T>
as an argument to the constructor. The compiler display this error:
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
Does anybody know how to solve it?
Building fields manually like that is somewhat tedious - I'd recommend to use class reification instead, where you can express the field as regular Haxe code:
macro class {
static var _pool = new Pool(/* clazz */);
}
This bypasses the "Invalid number of type parameters" issue entirely - just let type inference do the trick and omit the type parameter in new Pool()
.
The argument for the constructor call is of course variable, so we still need to use some expression reification. exp.Entity
is a field expression, so we have to use $p{}
. We can construct the type path needed for it by combining clazz.pack
and 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);
}
}
This generates the following code (as can be seen in exp/Entity.dump
with -D dump=pretty
):
static var _pool:exp.Pool<exp.Entity> = new exp.Pool(exp.Entity);
If you prefer adding fields by fields.push({...})
instead of using class reification, you can trigger type inference by using null
as a type in 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;
}
This is using @gama11 trick for path
. This generates the exact same code as @gama11 answer (and can be checked in the same way).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With