如何创建一个宏来生成一组在 Haxe 中动态已知的构造函数?
How to create a macro that generates an array of constructors known dynamically by name in Haxe?
我的目标是 javascript。
我在 Context.onGenerate() 上有一个 运行 的宏,它将完全限定类型名称的子集保存到文件中。然后,另一个构建宏(在下一个 buid 上会 运行)从文件中读取类型名称列表,以便创建一个静态字段到 class 中,它应该包含这些类型(构造函数)在数组中。
我想从第二个宏生成的字段是这样的:
public static _entities:Array<Class<entities.Entity>> = [
entities.Foo,
entities.Bar,
...
];
这将生成以下内容 javascript
MyClass._entities = [ entities_Foo, entities_Bar, ... ];
现在我已经尝试手动编写该字段以确保一切都正确生成——确实如此。但是我想不出编写宏的正确方法,我坚持添加一个标识符常量作为数组表达式的值,它总是以 "Unknown identifier"[=31 结束=]错误:
var id = { expr: EConst( CIdent( "entities.Foo" ) ),
pos: Context.currentPos() };
var ex = EArrayDecl([ id ]);
fields.push( {
name : "_entities",
access : [Access.APublic, Access.AStatic ],
pos : Context.currentPos(),
kind : FVar(
macro:Array<Class<entities.Entity>>,
// I've tried writing it without reification: (see above vars)
{ expr: ex, pos:Context.currentPos() }
// Or w/ reification:
macro $a{[ $i{ "entities.Foo" } ]}
)
});
我想用宏来实现吗?如果可以,可以指导我完成此操作的步骤吗?
谢谢。
在对 API 参考资料进行更多挖掘后,我想出了如何去做。事实证明我需要 TypedExpr 而不仅仅是标识符常量。
具有 TClassDecl 的 ModuleType 的 TTypeExpr 将产生正确的结果。
所以我上面的示例代码变成:
static function getTypeRef( name:String ):Ref<ClassType>
{
var type = Context.getType( name );
switch( type )
{
default: return Context.error( "Expected a ClassType", Context.currentPos() );
case TInst( cr, _ ):
return cr;
}
}
static function getTypes()
{
// Obtain ClassType by identifier
var fooCls = getTypeRef( "entities.Foo" );
// Get a TypedExpr for the ClassType
var typedExpr:TypedExpr = {
expr : TTypeExpr( TClassDecl( fooCls ) ),
t : TInst( fooCls, [] ),
pos : Context.currentPos()
};
// Convert to Expr
var expr:Expr = Context.getTypedExpr( typedExpr );
var fields = Context.getBuildFields();
fields.push( {
name : "_entities",
access : [Access.APublic, Access.AStatic ],
pos : Context.currentPos(),
kind : FVar(
macro:Array<Class<entities.Entity>>,
macro $a{[ ${expr} ]} // <- Now it works here
)
});
return fields;
}
问题是您试图将它输出为单个标识符,而它实际上是一个点路径,应该表示为 EField
到第一个 [=11= 的链].幸运的是,Haxe 有一个方便的 "path" reification 适合这个:试试 $p{path.split(".")}
(其中 path
是你的 "entities.Foo"
字符串)。
我的目标是 javascript。
我在 Context.onGenerate() 上有一个 运行 的宏,它将完全限定类型名称的子集保存到文件中。然后,另一个构建宏(在下一个 buid 上会 运行)从文件中读取类型名称列表,以便创建一个静态字段到 class 中,它应该包含这些类型(构造函数)在数组中。
我想从第二个宏生成的字段是这样的:
public static _entities:Array<Class<entities.Entity>> = [
entities.Foo,
entities.Bar,
...
];
这将生成以下内容 javascript
MyClass._entities = [ entities_Foo, entities_Bar, ... ];
现在我已经尝试手动编写该字段以确保一切都正确生成——确实如此。但是我想不出编写宏的正确方法,我坚持添加一个标识符常量作为数组表达式的值,它总是以 "Unknown identifier"[=31 结束=]错误:
var id = { expr: EConst( CIdent( "entities.Foo" ) ),
pos: Context.currentPos() };
var ex = EArrayDecl([ id ]);
fields.push( {
name : "_entities",
access : [Access.APublic, Access.AStatic ],
pos : Context.currentPos(),
kind : FVar(
macro:Array<Class<entities.Entity>>,
// I've tried writing it without reification: (see above vars)
{ expr: ex, pos:Context.currentPos() }
// Or w/ reification:
macro $a{[ $i{ "entities.Foo" } ]}
)
});
我想用宏来实现吗?如果可以,可以指导我完成此操作的步骤吗?
谢谢。
在对 API 参考资料进行更多挖掘后,我想出了如何去做。事实证明我需要 TypedExpr 而不仅仅是标识符常量。
具有 TClassDecl 的 ModuleType 的 TTypeExpr 将产生正确的结果。 所以我上面的示例代码变成:
static function getTypeRef( name:String ):Ref<ClassType>
{
var type = Context.getType( name );
switch( type )
{
default: return Context.error( "Expected a ClassType", Context.currentPos() );
case TInst( cr, _ ):
return cr;
}
}
static function getTypes()
{
// Obtain ClassType by identifier
var fooCls = getTypeRef( "entities.Foo" );
// Get a TypedExpr for the ClassType
var typedExpr:TypedExpr = {
expr : TTypeExpr( TClassDecl( fooCls ) ),
t : TInst( fooCls, [] ),
pos : Context.currentPos()
};
// Convert to Expr
var expr:Expr = Context.getTypedExpr( typedExpr );
var fields = Context.getBuildFields();
fields.push( {
name : "_entities",
access : [Access.APublic, Access.AStatic ],
pos : Context.currentPos(),
kind : FVar(
macro:Array<Class<entities.Entity>>,
macro $a{[ ${expr} ]} // <- Now it works here
)
});
return fields;
}
问题是您试图将它输出为单个标识符,而它实际上是一个点路径,应该表示为 EField
到第一个 [=11= 的链].幸运的是,Haxe 有一个方便的 "path" reification 适合这个:试试 $p{path.split(".")}
(其中 path
是你的 "entities.Foo"
字符串)。