Macros 检查生成宏是否已处理祖先节点

Macros 检查生成宏是否已处理祖先节点,macros,haxe,Macros,Haxe,假设您有类型building macro,接口调用@:autoBuild,使用前面提到的宏,类实现接口,类扩展接口。如果类不包含特定方法,则宏将失败 像这样: 宏.hx package; import haxe.macro.Context; import haxe.macro.Expr; import haxe.macro.Type; class Macro { macro public function build():Array<Field> {

假设您有类型building macro,接口调用
@:autoBuild
,使用前面提到的宏,类实现接口,类扩展接口。如果类不包含特定方法,则宏将失败

像这样:

宏.hx

package;

import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;

class Macro
{
    macro public function build():Array<Field>
    {
        var fields = Context.getBuildFields();

        for (field in fields) {
            if (field.name == "hello") {
                //Do some modifications

                return fields;       
            }
        }

        Context.error('${Context.getLocalClass().toString()} doesn\'t contain a method `hello`', Context.currentPos());

        return null;
    }
}
package;

@:autoBuild(Macro.build())
interface I {}
package;

class Foobar implements I
{
    public function new() {}

    public function hello(person:String)
    {
        return 'Hello $person!';
    }
}
package;

@:keep
class Foo extends Foobar {}
package;

import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;

class Macro
{
    macro public function build():Array<Field>
    {
        var c = Context.getLocalClass().get();
        if(isAncestorAlreadyProcessed(c)) {
            return null;
        }

        var fields = Context.getBuildFields();

        for (field in fields) {
            if (field.name == "hello") {
                //Do some modifications

                c.meta.add(":processed", [], c.pos);  

                return fields;       
            }
        }      

        Context.error('${Context.getLocalClass().toString()} doesn\'t contain a method `hello`', Context.currentPos());

        return null;
    }

    private static function isAncestorAlreadyProcessed(c:ClassType)
    {
        if (c.meta.has(":processed")) return true;
        if (c.superClass == null) return false;

        return isAncestorAlreadyProcessed(c.superClass.t.get());
    }
}
Foobar.hx

package;

import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;

class Macro
{
    macro public function build():Array<Field>
    {
        var fields = Context.getBuildFields();

        for (field in fields) {
            if (field.name == "hello") {
                //Do some modifications

                return fields;       
            }
        }

        Context.error('${Context.getLocalClass().toString()} doesn\'t contain a method `hello`', Context.currentPos());

        return null;
    }
}
package;

@:autoBuild(Macro.build())
interface I {}
package;

class Foobar implements I
{
    public function new() {}

    public function hello(person:String)
    {
        return 'Hello $person!';
    }
}
package;

@:keep
class Foo extends Foobar {}
package;

import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;

class Macro
{
    macro public function build():Array<Field>
    {
        var c = Context.getLocalClass().get();
        if(isAncestorAlreadyProcessed(c)) {
            return null;
        }

        var fields = Context.getBuildFields();

        for (field in fields) {
            if (field.name == "hello") {
                //Do some modifications

                c.meta.add(":processed", [], c.pos);  

                return fields;       
            }
        }      

        Context.error('${Context.getLocalClass().toString()} doesn\'t contain a method `hello`', Context.currentPos());

        return null;
    }

    private static function isAncestorAlreadyProcessed(c:ClassType)
    {
        if (c.meta.has(":processed")) return true;
        if (c.superClass == null) return false;

        return isAncestorAlreadyProcessed(c.superClass.t.get());
    }
}
Foo.hx

package;

import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;

class Macro
{
    macro public function build():Array<Field>
    {
        var fields = Context.getBuildFields();

        for (field in fields) {
            if (field.name == "hello") {
                //Do some modifications

                return fields;       
            }
        }

        Context.error('${Context.getLocalClass().toString()} doesn\'t contain a method `hello`', Context.currentPos());

        return null;
    }
}
package;

@:autoBuild(Macro.build())
interface I {}
package;

class Foobar implements I
{
    public function new() {}

    public function hello(person:String)
    {
        return 'Hello $person!';
    }
}
package;

@:keep
class Foo extends Foobar {}
package;

import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;

class Macro
{
    macro public function build():Array<Field>
    {
        var c = Context.getLocalClass().get();
        if(isAncestorAlreadyProcessed(c)) {
            return null;
        }

        var fields = Context.getBuildFields();

        for (field in fields) {
            if (field.name == "hello") {
                //Do some modifications

                c.meta.add(":processed", [], c.pos);  

                return fields;       
            }
        }      

        Context.error('${Context.getLocalClass().toString()} doesn\'t contain a method `hello`', Context.currentPos());

        return null;
    }

    private static function isAncestorAlreadyProcessed(c:ClassType)
    {
        if (c.meta.has(":processed")) return true;
        if (c.superClass == null) return false;

        return isAncestorAlreadyProcessed(c.superClass.t.get());
    }
}
如您所见,我们正在检查字段“hello”是否存在。但是,
Context.getBuildFields
只包含当前类的字段,并且对于
Foo
,生成将失败

这就是我的想法:为什么不检查是否有祖先已经被处理过?我们将更改Macro.hx以反映这一点:

宏.hx

package;

import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;

class Macro
{
    macro public function build():Array<Field>
    {
        var fields = Context.getBuildFields();

        for (field in fields) {
            if (field.name == "hello") {
                //Do some modifications

                return fields;       
            }
        }

        Context.error('${Context.getLocalClass().toString()} doesn\'t contain a method `hello`', Context.currentPos());

        return null;
    }
}
package;

@:autoBuild(Macro.build())
interface I {}
package;

class Foobar implements I
{
    public function new() {}

    public function hello(person:String)
    {
        return 'Hello $person!';
    }
}
package;

@:keep
class Foo extends Foobar {}
package;

import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;

class Macro
{
    macro public function build():Array<Field>
    {
        var c = Context.getLocalClass().get();
        if(isAncestorAlreadyProcessed(c)) {
            return null;
        }

        var fields = Context.getBuildFields();

        for (field in fields) {
            if (field.name == "hello") {
                //Do some modifications

                c.meta.add(":processed", [], c.pos);  

                return fields;       
            }
        }      

        Context.error('${Context.getLocalClass().toString()} doesn\'t contain a method `hello`', Context.currentPos());

        return null;
    }

    private static function isAncestorAlreadyProcessed(c:ClassType)
    {
        if (c.meta.has(":processed")) return true;
        if (c.superClass == null) return false;

        return isAncestorAlreadyProcessed(c.superClass.t.get());
    }
}
包;
导入haxe.macro.Context;
导入haxe.macro.Expr;
导入haxe.macro.Type;
类宏
{
宏公共函数build():数组
{
var c=Context.getLocalClass().get();
如果(isAncestorAlreadyProcessed(c)){
返回null;
}
var fields=Context.getBuildFields();
for(字段中的字段){
如果(field.name==“hello”){
//做一些修改
c、 元添加(“:processed”,[],c.pos);
返回字段;
}
}      
Context.error('${Context.getLocalClass().toString()}不包含方法'hello`',Context.currentPos());
返回null;
}
私有静态函数isAncestorAlreadyProcessed(c:ClassType)
{
如果(c.meta.has(“:processed”))返回true;
if(c.superClass==null)返回false;
返回isAncestorAlreadyProcessed(c.superClass.t.get());
}
}
关于主要问题:我是否误解了haxe宏类型建筑?有没有一种更可行的方法来实现这一点?我的代码在特定情况下会失败吗?此代码是否有任何有害的副作用


我正在尝试解决这个问题。

不,这是解决方法,使用元数据存储您处理的类的信息()

如果在运行时不需要这些信息,另一种方法是在专用类上使用静态数组,如。之后,您甚至可以将这些信息推送到编译后的代码中,请参阅


希望这有帮助。

只有在haxe社区,一个答案,但具体解决了我的问题。非常感谢。