如何在 Haxe 宏函数中声明实例化

Mar*_*nol 5 macros haxe

我想创建一个为我生成此代码的宏:

if (myEntity.get(Attack) == null) myEntity.add(new Attack());
if (myEntity.get(Confused) == null) myEntity.add(new Confused());
if (myEntity.get(Defend) == null) myEntity.add(new Defend());
if (myEntity.get(Offense) == null) myEntity.add(new Offense());
Run Code Online (Sandbox Code Playgroud)

在代码中,我想像这样声明/使用它:

EntityMacroUtils.addComponents(myEntity, Attack, Confused, Defend, Offense);
Run Code Online (Sandbox Code Playgroud)

当前的宏函数如下所示:

macro public static function addComponents(entity:ExprOf<Entity>, components:Array<ExprOf<Class<Component>>>):Expr
{
    var exprs:Array<Expr> = [];
    for (componentClass in components)
    {
        var instance = macro $e { new $componentClass() }; // problem is here
        var expr = macro if ($entity.get($componentClass) == null) $entity.add(instance);
        exprs.push(expr);
    }
    return macro $b{ exprs };
}
Run Code Online (Sandbox Code Playgroud)

这个宏函数不正确,我收到错误:

EntityMacroUtils.hx:17:字符 22-43:未找到类型:$componentClass

问题是我不知道如何定义new $componentClass(). 我将如何解决这个问题?

我也想避免Type.createInstance输出代码中使用。

Phi*_*ppe 5

以编程方式生成实例化代码的一种方法是使用“老派”枚举 AST 构建(兼容 Haxe 3.0.1+):

// new pack.age.TheClass()
return {
    expr:ENew({name:"TheClass", pack:["pack", "age"], params:[]}, []),
    pos:Context.currentPos()
};
Run Code Online (Sandbox Code Playgroud)

使用具体化的改进语法是可能的:

// new pack.age.TheClass()
var typePath = { name:"TheClass", pack:["pack", "age"], params:[] };
return macro new $typePath();
Run Code Online (Sandbox Code Playgroud)

现在,为了方便的“实例化助手”函数语法,我们需要做一些扭曲以从我们在宏函数中收到的表达式中提取类型路径:

// new Foo(), new pack.Bar(), new pack.age.Baz()
instantiate(Foo, pack.Bar, pack.age.Baz);

macro static function instantiate(list:Array<Expr>)
{
    var news = [for (what in list) {
        var tp = makeTypePath(what);
        macro new $tp();
    }];
    return macro $b{news};
}

#if macro
static function makeTypePath(of:Expr, ?path:Array<String>):TypePath 
{
    switch (of.expr)
    {
        case EConst(CIdent(name)):
            if (path != null) {
                path.unshift(name);
                name = path.pop();
            }
            else path = [];
            return { name:name, pack:path, params:[] };

        case EField(e, field):
            if (path == null) path = [field];
            else path.unshift(field);
            return makeTypePath(e, path);

        default:
            throw "nope";
    }
}
#end
Run Code Online (Sandbox Code Playgroud)