Использование макросов haxe для создания экземпляра класса с параметрами

Я пытаюсь сделать темную магию с макросами в haxe, у меня есть класс с именем entity, и я хочу добавить пул как статический и частный атрибут:

бассейн.hx:

package exp;

class Pool<T> {
    public function new(clazz:Class<T>) {

    }
}

сущности.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 и передачей класса в качестве аргумента конструктору. Компилятор отображает эту ошибку:

exp / Entity.hx: 3: символы 1-7: недопустимое число параметр типа для exp.Бассейн

exp / Entity.hx: 4: строки 4-6: определяется в этом классе

кто-нибудь знает как ее решить?

спасибо заранее.

2 ответов


создание полей вручную, как это несколько утомительно - я бы рекомендовал использовать овеществления класс вместо этого, где вы можете выразить поле как обычный код на Haxe:

macro class {
    static var _pool = new Pool(/* clazz */);
}

это полностью обходит проблему "недопустимое количество параметров типа" - просто позвольте выводу типа сделать трюк и опустить параметр типа в new Pool().

аргумент для вызова конструктора, конечно, переменная, поэтому нам все равно нужно использовать некоторые выражение овеществление!--12-->. 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({...}) вместо использования reification класса можно вызвать вывод типа с помощью 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;
}

это использование @ gama11 трюк для path. Это генерирует тот же самый код, что и ответ @gama11 (и может быть проверено таким же образом).