用于填写自定义 @:enum 摘要的字段名称、值和 switch 语句的宏?
Macro to fill out field names, values, and switch statements of a custom @:enum abstract?
我的情况有点特殊。出于各种原因,主要是与可为空的 stringly typed 遗留系统进行互操作,以及目前我不会讨论的各种其他需求,我已经确定了一个自定义的 @:enum 摘要,如下所示:
@:enum abstract MyEnum(Null<Int>) {
public var A = 0;
public var B = 1;
public var C = 2;
public var D = 3;
public var E = 4;
public var F = 5;
public var G = 6;
@:from private static function fromString (value:String):MyEnum {
return switch (value) {
case "a": A;
case "b": B;
case "c": C;
case "d": D;
case "e": E;
case "f": F;
case "g": G;
default: null;
}
}
@:to private static function toString (value:Int):String {
return switch (value) {
case A: "a";
case B: "b";
case C: "c";
case D: "d";
case E: "e";
case F: "f";
case G: "g";
default: null;
}
}
}
但是,要键入的内容太多,令人恼火,添加和删除成员时很容易出现手动错误。显然,这遵循了一个超级可预测的模式,并且似乎是用宏构建的好东西,但我不擅长 haxe 宏。
有人可以解释我如何使用宏来构建这个枚举,这样我只需要提供一个字段名称列表吗?
伪代码:
@:enum abstract MyEnum = doTheMacroMagic(["A","B","C","D","E","F","G"]);
逻辑步骤是:
- 从字段名称(大写)声明 public 变量
- 从字段名称(小写)声明 fromString/toString 值
- 将 public 变量设置为从 0 开始的整数,并遵循与提供字段名称相同的顺序
我认为像这样一个简单的实际例子可能最终让 haxe 宏对我来说 "click" 如果我能看到它的实际效果。
Flixel 在 类 中处理非常相似的用例,例如 FlxKey
和 FlxMacroUtil.buildMap()
。这个表达式宏查找它在摘要中找到的所有大写内联变量,并从中生成一个 Map<String, EnumType>
,键是字段名,值是字段值(或者如果 invert
是 true
).
@:enum
abstract FlxKey(Int) from Int to Int
{
public static var fromStringMap(default, null):Map<String, FlxKey>
= FlxMacroUtil.buildMap("flixel.input.keyboard.FlxKey");
public static var toStringMap(default, null):Map<FlxKey, String>
= FlxMacroUtil.buildMap("flixel.input.keyboard.FlxKey", true);
var A = 65;
var B = 66;
// more keys...
@:from
public static inline function fromString(s:String)
{
s = s.toUpperCase();
return fromStringMap.exists(s) ? fromStringMap.get(s) : NONE;
}
@:to
public inline function toString():String
{
return toStringMap.get(this);
}
}
我认为这是一个很好的起点。如果要生成整个 abstract
,您将需要一个 @:build
宏。
回答后续问题,如何生成字段:这实际上使用构建宏非常简单:
@:enum
@:build(Macro.createVariables(["A", "B", "C", "D", "E"]))
abstract Generated(Int)
{
}
Macro.hx
(明智的做法是在自己的文件中避免必须处理 #if macro
条件):
package;
import haxe.macro.Context;
import haxe.macro.Expr;
class Macro
{
public static macro function createVariables(varNames:Array<String>):Array<Field>
{
// get the current fields of the calling type (empty array in this case)
var fields = Context.getBuildFields();
for (i in 0...varNames.length)
// create a custom variable and add it to the fields
fields.push(createVariable(varNames[i], i));
return fields;
}
private static function createVariable(name:String, value:Int):Field
{
return {
name: name,
doc: null,
meta: [],
access: [Access.APublic, Access.AStatic, Access.AInline],
kind: FieldType.FVar(macro:Int, macro $v{value}),
pos: Context.currentPos()
}
}
}
您会注意到 Generated.
的字段显示在自动完成中。您还可以在执行 AST 转储时通过查看 Generated_Impl.dump
查看生成的内容。
我的情况有点特殊。出于各种原因,主要是与可为空的 stringly typed 遗留系统进行互操作,以及目前我不会讨论的各种其他需求,我已经确定了一个自定义的 @:enum 摘要,如下所示:
@:enum abstract MyEnum(Null<Int>) {
public var A = 0;
public var B = 1;
public var C = 2;
public var D = 3;
public var E = 4;
public var F = 5;
public var G = 6;
@:from private static function fromString (value:String):MyEnum {
return switch (value) {
case "a": A;
case "b": B;
case "c": C;
case "d": D;
case "e": E;
case "f": F;
case "g": G;
default: null;
}
}
@:to private static function toString (value:Int):String {
return switch (value) {
case A: "a";
case B: "b";
case C: "c";
case D: "d";
case E: "e";
case F: "f";
case G: "g";
default: null;
}
}
}
但是,要键入的内容太多,令人恼火,添加和删除成员时很容易出现手动错误。显然,这遵循了一个超级可预测的模式,并且似乎是用宏构建的好东西,但我不擅长 haxe 宏。
有人可以解释我如何使用宏来构建这个枚举,这样我只需要提供一个字段名称列表吗?
伪代码:
@:enum abstract MyEnum = doTheMacroMagic(["A","B","C","D","E","F","G"]);
逻辑步骤是:
- 从字段名称(大写)声明 public 变量
- 从字段名称(小写)声明 fromString/toString 值
- 将 public 变量设置为从 0 开始的整数,并遵循与提供字段名称相同的顺序
我认为像这样一个简单的实际例子可能最终让 haxe 宏对我来说 "click" 如果我能看到它的实际效果。
Flixel 在 类 中处理非常相似的用例,例如 FlxKey
和 FlxMacroUtil.buildMap()
。这个表达式宏查找它在摘要中找到的所有大写内联变量,并从中生成一个 Map<String, EnumType>
,键是字段名,值是字段值(或者如果 invert
是 true
).
@:enum
abstract FlxKey(Int) from Int to Int
{
public static var fromStringMap(default, null):Map<String, FlxKey>
= FlxMacroUtil.buildMap("flixel.input.keyboard.FlxKey");
public static var toStringMap(default, null):Map<FlxKey, String>
= FlxMacroUtil.buildMap("flixel.input.keyboard.FlxKey", true);
var A = 65;
var B = 66;
// more keys...
@:from
public static inline function fromString(s:String)
{
s = s.toUpperCase();
return fromStringMap.exists(s) ? fromStringMap.get(s) : NONE;
}
@:to
public inline function toString():String
{
return toStringMap.get(this);
}
}
我认为这是一个很好的起点。如果要生成整个 abstract
,您将需要一个 @:build
宏。
回答后续问题,如何生成字段:这实际上使用构建宏非常简单:
@:enum
@:build(Macro.createVariables(["A", "B", "C", "D", "E"]))
abstract Generated(Int)
{
}
Macro.hx
(明智的做法是在自己的文件中避免必须处理 #if macro
条件):
package;
import haxe.macro.Context;
import haxe.macro.Expr;
class Macro
{
public static macro function createVariables(varNames:Array<String>):Array<Field>
{
// get the current fields of the calling type (empty array in this case)
var fields = Context.getBuildFields();
for (i in 0...varNames.length)
// create a custom variable and add it to the fields
fields.push(createVariable(varNames[i], i));
return fields;
}
private static function createVariable(name:String, value:Int):Field
{
return {
name: name,
doc: null,
meta: [],
access: [Access.APublic, Access.AStatic, Access.AInline],
kind: FieldType.FVar(macro:Int, macro $v{value}),
pos: Context.currentPos()
}
}
}
您会注意到 Generated.
的字段显示在自动完成中。您还可以在执行 AST 转储时通过查看 Generated_Impl.dump
查看生成的内容。