Skip to content

Instantly share code, notes, and snippets.

@puffnfresh
Last active March 6, 2020 01:09
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save puffnfresh/5314836 to your computer and use it in GitHub Desktop.
Save puffnfresh/5314836 to your computer and use it in GitHub Desktop.
Macro to generate "value classes" in Haxe
import precog.macro.ValueClass;
class ABC implements ValueClass {
var a: Int;
var b: Bool;
var c: String;
}
/*
class ABC extends ValueClass {
public var a(default, null): Int;
public var b(default, null): Bool;
public var c(default, null): String;
public function new(a: Int, b: Bool, c: String) {
this.a = a;
this.b = b;
this.c = c;
}
}
*/
package precog.macro;
import haxe.macro.Expr;
import haxe.macro.Context;
@:remove @:autoBuild(ValueClassImpl.build())
extern interface ValueClass {}
class ValueClassImpl {
#if macro
public static function build() {
var fields = Context.getBuildFields();
var args = [];
var states = [];
for (f in fields) {
switch (f.kind) {
case FVar(t, _):
args.push({name: f.name, type: t, opt: false, value: null});
states.push(macro $p{["this", f.name]} = $i{f.name});
f.kind = FProp("default", "null", t);
f.access.push(APublic);
default:
}
}
fields.push({
name: "new",
access: [APublic],
pos: Context.currentPos(),
kind: FFun({
args: args,
expr: macro $b{states},
params: [],
ret: null
})
});
return fields;
}
#end
}
@deltaluca
Copy link

Using an interface with @:remove and extern so that it is 100% compile time only.

Reification to shortern code, and lacking use of @:publicFields, adding public access modifier manually to fields.

package;

import haxe.macro.Expr;
import haxe.macro.Context;

@:remove @:autoBuild(ValueClassImpl.build())
extern interface ValueClass {}

class ValueClassImpl {
#if macro
    public static function build() {
        var fields = Context.getBuildFields();
        var args = [];
        var states = [];
        for (f in fields) {
            switch (f.kind) {
            case FVar(t,_):
                args.push({name:f.name, type:t, opt:false, value:null});
                states.push(macro $p{["this", f.name]} = $i{f.name});
                f.access.push(APublic);
            default:
            }
        }
        fields.push({
            name: "new",
            access: [APublic],
            pos: Context.currentPos(),
            kind: FFun({
                args: args,
                expr: macro $b{states},
                params: [],
                ret: null
            })
        });
        return fields;
    }
#end
}

@Gamezpedia
Copy link

Hi, i am learning :

i just followed it and it is giving me :

image

@fizzy33
Copy link

fizzy33 commented Mar 5, 2018

I ran into the "Type not found : ValueClassImpl" my work around was to remove the @:remove and the extern and it worked. So that does have some light runtime overhead but worth it for me to get this feature working.

@theJenix
Copy link

theJenix commented Mar 6, 2020

I ran into the Type not found error with Haxe 4.0.5; I got it to work by specifying the fully qualified macro type name in the @:autoBuild metadata, e.g. "@:autoBuild(my.package.to.ValueClass.ValueClassImpl.build()). Once I did that, everything worked as originally spec'd above (including the extern and @:remove).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment