Type-building macros are different from expression macros in several ways:
Array<haxe.macro.Expr.Field>.haxe.macro.Context.getBuildFields().@:build or @:autoBuild metadata on a class or enum declaration.The following example demonstrates type building. Note that it is split up into two files for a reason: If a module contains a macro function, it has to be typed into macro context as well. This is often a problem for type-building macros because the type to be built could only be loaded in its incomplete state, before the building macro has run. We recommend to always define type-building macros in their own module.
import haxe.macro.Context; import haxe.macro.Expr; class TypeBuildingMacro { macro static public function build(fieldName:String):Array<Field> { var fields = Context.getBuildFields(); var newField = { name: fieldName, doc: null, meta: [], access: [AStatic, APublic], kind: FVar(macro:String, macro "my default"), pos: Context.currentPos() }; fields.push(newField); return fields; } }
@:build(TypeBuildingMacro.build("myFunc")) class Main { static public function main() { trace(Main.myFunc); // my default } }
The build method of TypeBuildingMacro performs three steps:
Context.getBuildFields().haxe.macro.expr.Field field using the funcName macro argument as field name. This field is a String variable with a default value "my default" (from the kind field) and is public and static (from the access field).This macro is argument to the @:build metadata on the Main class. As soon as this type is required, the compiler does the following:
@:build metadata.This allows adding and modifying class fields at will in a type-building macro. In our example, the macro is called with a "myFunc" argument, making Main.myFunc valid field access.
If a type-building macro should not modify anything, the macro can return null. This indicates to the compiler that no changes are intended and is preferable to returning Context.getBuildFields().