【问题标题】:Haxe Custom Metadata to Macro CallHaxe 自定义元数据到宏调用
【发布时间】:2017-05-09 14:43:10
【问题描述】:

假设我创建了一个可以像这样使用的构建宏

@:build(macros.SampleMacro.build("arg"))
class Main {}

是否可以将其转换为自定义的速记元数据?

@:samplemacro("arg")
class Main {}

有这方面的文档吗?

【问题讨论】:

    标签: macros haxe


    【解决方案1】:

    我不确定这是否可行,但您可以利用 @: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 _:
    }
    

    【讨论】:

    • 感谢您的建议 - 这是另一种做事方式,但不是我想要的。
    【解决方案2】:

    经过一番磕磕绊绊,我发现这是可以做到的。

    部分解决方案是使用

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

    这使您可以将@:build 函数分配给$path 中的所有类。这可以用作编译器选项或 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://stackoverflow.com/a/38075492/1502818

    【讨论】:

    • 您可能想要检查这如何影响编译性能。在大型项目中,对所有内容运行构建宏可能会很昂贵(这基本上是我不建议使用这种方法的原因)。
    • 我还没有遇到性能问题,而且,我似乎找不到任何其他解决方法。幸运的是,检查标签是否存在的测试实际上只涉及检查元数据标签的映射。
    • 有一个库可以做到这一点,并具有一些附加功能:github.com/haxetink/tink_syntaxhub
    • 我想一旦我在我的代码库中注册了多个自定义宏,我会改用 tink_syntaxhub。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-11
    • 2018-07-22
    相关资源
    最近更新 更多