[英]How to declare instantiation in Haxe macro function
我想創建一個為我生成此代碼的宏:
if (myEntity.get(Attack) == null) myEntity.add(new Attack());
if (myEntity.get(Confused) == null) myEntity.add(new Confused());
if (myEntity.get(Defend) == null) myEntity.add(new Defend());
if (myEntity.get(Offense) == null) myEntity.add(new Offense());
在代碼中我想聲明/使用它像這樣:
EntityMacroUtils.addComponents(myEntity, Attack, Confused, Defend, Offense);
當前的宏函數如下所示:
macro public static function addComponents(entity:ExprOf<Entity>, components:Array<ExprOf<Class<Component>>>):Expr
{
var exprs:Array<Expr> = [];
for (componentClass in components)
{
var instance = macro $e { new $componentClass() }; // problem is here
var expr = macro if ($entity.get($componentClass) == null) $entity.add(instance);
exprs.push(expr);
}
return macro $b{ exprs };
}
這個宏函數不正確,我收到錯誤:
EntityMacroUtils.hx:17:字符22-43:未找到類型:$ componentClass
問題是我不知道如何定義new $componentClass()
。 我該如何解決這個問題?
我還想避免在輸出代碼中使用Type.createInstance
。
以編程方式生成實例化代碼的一種方法是使用“舊學校”枚舉AST構建(兼容Haxe 3.0.1+):
// new pack.age.TheClass()
return {
expr:ENew({name:"TheClass", pack:["pack", "age"], params:[]}, []),
pos:Context.currentPos()
};
使用具體化的改進語法是可能的:
// new pack.age.TheClass()
var typePath = { name:"TheClass", pack:["pack", "age"], params:[] };
return macro new $typePath();
現在,為了方便的“實例化助手”函數語法,我們需要做一些扭曲來從我們在宏函數中接收的表達式中提取類型路徑:
// new Foo(), new pack.Bar(), new pack.age.Baz()
instantiate(Foo, pack.Bar, pack.age.Baz);
macro static function instantiate(list:Array<Expr>)
{
var news = [for (what in list) {
var tp = makeTypePath(what);
macro new $tp();
}];
return macro $b{news};
}
#if macro
static function makeTypePath(of:Expr, ?path:Array<String>):TypePath
{
switch (of.expr)
{
case EConst(CIdent(name)):
if (path != null) {
path.unshift(name);
name = path.pop();
}
else path = [];
return { name:name, pack:path, params:[] };
case EField(e, field):
if (path == null) path = [field];
else path.unshift(field);
return makeTypePath(e, path);
default:
throw "nope";
}
}
#end
如果有人需要答案,我得到了這個感謝ousado在Haxe IRC聊天:
如果你只在宏中執行它,你可以這樣做:
var ct = macro : pack.age.SomeTypename;
var tp = switch ct { case TPath(tp):tp; case _: throw "nope"; }
var expr = macro new $tp();
..或者,如果你明確地構造tp
:
var tp = {sub:'SomeTypeName',params:[],pack:['pack','age'],name:"SomeModuleName"}
如您所見,此處明確給出了復雜類型路徑。
不幸的是,Haxe對於表達式位置中的類型並沒有真正簡潔的語法。 您可以傳遞( _ : TypeName )
以提供包含ComplexType的表達式。
但是如果你想傳遞一個類型作為參數,你可以這樣做:
import haxe.macro.Expr;
using haxe.macro.Tools;
class Thing {
public function new(){}
}
class OtherThing {
public function new(){}
}
class TMacroNew {
macro static function instances( arr:Array<Expr> ) {
var news = [for (e in arr) {
var ct = switch e.expr { case EParenthesis({expr:ECheckType(_,ct)}):ct; case _: throw "nope"; };
var tp = switch ct { case TPath(tp):tp; case _: throw "nope"; };
macro new $tp();
}];
trace( (macro $b{news}).toString());
return macro $b{news};
}
static function main(){
instances( (_:Thing), (_:Thing), (_:OtherThing) );
}
}
..如果你想要一個類型列表,你可能想要一個參數列表,如( _ : L< One,Two,Three> )
。
接受的答案是有問題的,因為當涉及類型參數時,或者當應包括對非標稱類型的支持時,它會中斷。
我使用兩個替代方法更新了示例,以便為類型列表提供更簡潔的表示法,同時仍允許實際類型的語法。
import haxe.macro.Expr;
using haxe.macro.Tools;
class Thing {
public function new(){}
}
class OtherThing {
public function new(){}
}
class TPThing<T>{
public function new(){}
}
class TMacroNew {
macro static function instances( e:Expr ) {
var tps = switch e.expr {
case EParenthesis({expr:ECheckType(_,TPath({params:tps}))}):tps;
case ENew({params:tps},_):tps;
case _: throw "not supported";
}
var type_paths = [ for (tp in tps) switch tp {
case TPType(TPath(tp)):tp;
case _: throw "not supported";
}];
var news = [for (tp in type_paths) macro new $tp()];
trace( (macro $b{news}).toString());
return macro $b{news};
}
static function main(){
instances( (_:L<Thing,Thing,OtherThing,TPThing<Int>> ) );
instances( new L<Thing,Thing,OtherThing,TPThing<Int>>() );
}
}
編輯: L< ... >
中的L< ... >
可以是任何有效的類型名稱。 它的唯一目的是允許以有效語法編寫以逗號分隔的類型列表。 由於宏函數將表達式作為參數,我們必須使用允許/需要類型的表達式,如: ( _ :T ), new T(), var v:T, function(_:T):T {}
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.