Haxe自定义元数据转换为宏调用

4
假设我创建了一个可用的构建宏,其使用方式如下:
@:build(macros.SampleMacro.build("arg"))
class Main {}

有没有可能将其转换为自定义简写元数据?
@:samplemacro("arg")
class Main {}

这个有文档吗?
2个回答

4

经过许多尝试,我发现这是可能的。

解决方案的一部分是使用

--macro addGlobalMetadata('$path', '@:build(Build.build())')

这使您可以为$path中的所有类分配一个@:build函数。这可以用作编译器选项或haxeflag。
但仅此还不足以采用具有动态参数的元数据标记。但是,因为我们现在有了Build.build(),它可以执行所有类的操作,该Build.build()函数可以处理检查哪些类具有我们的自定义元数据标记,以及构建我们可能需要的任何这些类的内容。
为了检查我们的自定义元数据标记,我们设置了以下检查宏:
class Build {

    static var META_STR:String = ":samplemacro";

    macro static public function build():Array<Field> {

        // We check only those Contexts for where a class type exists
        var localClass:Null<Ref<ClassType>> = Context.getLocalClass();
        if(localClass == null) return null; // no class type

        // We check if the metadata for the class contains our 
        // custom metadata string at all
        if(!localClass.get().meta.has(META_STR)) return null;

        // This class does have our custom metadata!
        // Because there may be more than one of the same type
        // of metadata, we extract a list of all the custom metadata tags

        var customTags = localClass.get().meta.extract(META_STR);

        // For each tag we can get at the arguments passed in 
        // by accessing the params field
        for(customTag in customTags){
            var params = customTag.params;

            // Here we can handle each of our @:samplemacro(params) tags,
            // save the params for use later, or 
            // pass the arguments over to another class to deal with
        }

        var fields = Context.getBuildFields();

        // Modify the class fields the way you want

        // Optionally destroy the metadata tags afterwards
        // with localClass.get().meta.remove(META_STR);

        return fields;
    }
}

有关addGlobalMetadata的详细信息,请访问: https://dev59.com/1pjga4cB1Zd3GeqPKG9b#38075492


1
你可能想要检查这对编译性能的影响。在大型项目中运行构建宏可能会很昂贵(基本上这就是我没有建议这种方法的原因)。 - Gama11
1
我还没有遇到性能问题,而且我似乎找不到其他解决方法。幸运的是,检查标签是否存在的测试实际上只涉及检查元数据标签的映射。 - Danny Yaroslavski
2
有一个库可以完成相同的任务,并具有一些附加功能:https://github.com/haxetink/tink_syntaxhub - KevinResoL
我想一旦在我的代码库中注册了几个自定义宏,我会转而使用 tink_syntaxhub。 - Danny Yaroslavski

3

我不确定这是可能的,但您可以利用@:autoBuild()元数据在接口上工作的事实。这常常被用于像这样的“标记接口”:

class Main implements ISampleMacro {}

@:autoBuild(macros.SampleMacro.build("arg"))
interface ISampleMacro {}

然而,你可能希望每次使用时都有不同的"arg",而不是硬编码它。你可以通过使用@:const类型参数来实现这一点:

class Main implements ISampleMacro<"foo"> {}

@:autoBuild(macros.SampleMacro.build())
interface ISampleMacro<@:const T> {}

在您的构建宏中提取类型参数的值需要比简单传递参数多费一些功夫:

switch (Context.getLocalClass().get().interfaces[0].params[0]) {
    case TInst(_.get() => t, params):
        switch (t.kind) {
            case KExpr({expr: EConst(CString(arg)), pos: _}):
                trace(arg); // "foo"
            case _:
        }
    case _:
}

感谢您的建议-这是另一种做事情的方式,但不是我想要的。 - Danny Yaroslavski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接