Building Types with Macros

Thanks to Macros, it is also possible to manipulate class fields, adding, modifying or removing them, at the time the class is defined.

In order to do that, you need to specify a build function using @:build metadata before the class declaration. This function will be passed as parameter a Expr that will contain the user-declared class fields, which can be modified in order to return a similar Expr that will list the fields that will get compiled.

Here's an example :

// C.hx
@:build(MyMacro.build()) class C {
}

// MyMacro.hx
import haxe.macro.Expr;
class MyMacro {
    @:macro public static function build() : Array<Field> {        
        var pos = haxe.macro.Context.currentPos();
        var fields = haxe.macro.Context.getBuildFields();
        // add a new public class field named "hello" with type "Int"
        var tint = TPath({ pack : [], name : "Int", params : [], sub : null });
        fields.push({ name : "hello", doc : null, meta : [], access : [APublic], kind : FVar(tint,null), pos : pos });
        return fields;
    }
}

Make sure to have both classes in separate files or else the compiler will complain since @:build cannot be used in code inside macros.

Please note also that you should always explicitly type the build method return type as Array<Field>, since it will be inferred as haxe.macro.Expr by the macro system either.

Otherwise, you can still use the following example to make sure that your class get only built outside macros :

#if !macro @:build(MyMacro.build()) #end class C {
}

You can of course modify the user fields list that is passed as input in order to modify the fields that have been declared in the body of the class built.

Note: if your macro does not make any changes to the fields, it can return null. This will be faster than encoding/decoding the fields between the compiler and the macro subsystem.

Building Enums

You can also use macros to build enums constructors, in a similar way as classes, as the following example show :

// E.hx
@:build(MyMacro.build()) enum E {
}

// MyMacro.hx
import haxe.macro.Expr;
class MyMacro {
    @:macro public static function build() : Array<Field> {
        var pos = haxe.macro.Context.currentPos();
        var cl = new Array();
        for( c in ["A","B","C"] )
            cl.push({ name : c, doc : null, meta : [], access : [], kind : FVar(null,null), pos : pos });
        return cl;
    }
}

You can also add constructors which take parameters :

// add an enum with a single parameter
var tint = TPath({ pack : [], name : "Int", params : [], sub : null });
var f = { args : [{ name : "value", type : tint, opt : false, value : null  }], params : null, ret : null, expr : null };
cl.push({ name : "D", doc : null, meta : [], access : [], kind : FFun(f), pos : pos });

MacroType

Since a typedef is only a type alias, there is no @:build for typedefs. However since you might still want to generate anonymous structures with a macro, you can use haxe.macro.MacroType at each place a type is required, for instance :

typedef Fields = haxe.macro.MacroType<[my.Macro.getFields("file.txt")]>;

The macro method must return a haxe.macro.Type value. You can extract this type by using the haxe macro api or build you own by creating the corresponding expression and using haxe.macro.Context.typeof

AutoBuild

If you use @:autoBuild(buildMacro()) on a given class or interface, this will generate automatically a @:build metadata on all the classes which extends/implements it. You will be able to access the current class being built with haxe.macro.Context.getLocalClass().

This is an easy way to perform build operations in a transparent way, without the user having to declare anything macro-related.

Starting from Haxe 2.10 (Release Candidate), any class which implement an empty interface with "@:autoBuild" will not implement this interface in the final compiled output.

Defining a new Type

If you want to have your macro automatically create and declare a new type, you can do it by using haxe.macro.Context.defineType.

version #15413, modified 2012-08-24 22:40:14 by Confidant