Skip to content

Instantly share code, notes, and snippets.

@AustinEast
Last active December 19, 2020 02:18
Show Gist options
  • Save AustinEast/ece115ce4a5fe622b293be1b5c651101 to your computer and use it in GitHub Desktop.
Save AustinEast/ece115ce4a5fe622b293be1b5c651101 to your computer and use it in GitHub Desktop.
Haxe Macro for defining unique IDs per Class
#if macro
import haxe.macro.Type;
import haxe.macro.Context;
import haxe.macro.Expr;
#end
/**
* Implementing this interface on a Class will run `ClassID.build()`, then remove itself.
**/
@:remove
@:autoBuild(ClassID.build())
interface IClassID {}
class ClassID {
#if macro
static final types = new Map<ClassType,Int>();
static final field_name = 'class_id';
static final get_field_name = 'get_$field_name';
static var uid = 0;
/**
* Generates the static variable `class_id`, which holds the unique Int ID for the defined Class.
* The non-static method `get_class_id()` is also generated so that Class instances can easily retrieve the Class ID.
*/
public static function build():Array<Field> {
var fields = Context.getBuildFields();
var pos = Context.currentPos();
var ct = Context.getLocalClass().get();
// If the Class doesn't have a Class ID, add one to the Map
if (!types.exists(ct)) types.set(ct, uid++);
// Add the `class_id` variable
fields.push({
name: field_name,
kind: FVar(macro:Int, macro $v{types.get(ct)}),
access: [APublic, AStatic, AFinal],
pos: pos
});
// If this Class extends another Class, recursively check if we need to override the `get_class_id()` method
var sc = ct.superClass;
while (sc != null) {
var sct = sc.t.get();
for (field in sct.fields.get()) {
// Override the method if needed, then exit early
if (field.name == get_field_name) {
var concat = (macro class {
override function $get_field_name () return $p{ct.pack.concat([ct.name])}.$field_name;
}).fields;
return fields.concat(concat);
}
}
sc = sct.superClass;
}
// Otherwise add the `get_class_id()` method
var concat = (macro class {
public function $get_field_name () return $p{ct.pack.concat([ct.name])}.$field_name;
}).fields;
return fields.concat(concat);
}
#end
}
import ClassID;
class ClassA implements IClassID {
public function new() {}
}
class ClassB implements IClassID {
public function new() {}
}
// Test extending Classes
class ClassC extends ClassB {}
class ClassIDTest {
static function main() {
// Note - ID's will not be generated from Classes in any specific order, but they will still be unique.
// For example - although `ClassA` is defined first, it has an ID of 2 instead of 0.
trace(ClassA.class_id); // == 2
trace(ClassB.class_id); // == 0
trace(ClassC.class_id); // == 1
trace(new ClassC().get_class_id()); // == 1
trace(new ClassB().get_class_id()); // == 0
trace(new ClassA().get_class_id()); // == 2
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment