Haxe Custom Metadata to Macro Call

早过忘川 提交于 2019-12-07 11:45:10

问题


Let's say that I've created a build macro that can be used like so

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

Is it possible to convert it into a custom, shorthand metadata?

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

Any documentation on this?


回答1:


I'm not sure that's possible, but you can make use of the fact that @:autoBuild() metadata works on interfaces. This is often used for "marker interfaces" like this:

class Main implements ISampleMacro {}

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

However, presumably you want to have a different "arg" per usage, rather than hardcoding it. You can achieve this by using a @:const type parameter:

class Main implements ISampleMacro<"foo"> {}

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

Extracting the type parameter's value in your build macro requires a bit more effort than simply passing a parameter to it:

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:


After much stumbling, I've figured out that it is possible to do.

Part of the solution is to use

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

This lets you assign a @:build function to all of the classes in the $path. This can be used as either a compiler option or a haxeflag.

But that on its own is not enough to adopt metadata tags that take dynamic arguments. But because we now have a Build.build() that executes for all of our classes, that Build.build() function can both handle checking which classes have our custom metadata tag(s), as well as build anything for those classes we may need.

To check for our custom metadata tags, we set up our checking macro as follows:

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;
    }
}

More details on addGlobalMetadata can be found at: https://stackoverflow.com/a/38075492/1502818



来源:https://stackoverflow.com/questions/43872908/haxe-custom-metadata-to-macro-call

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!