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()
.