简体   繁体   English

在使用haxe宏构建的函数中使用局部变量

[英]Using local variables in function built with haxe macro

I have a LangBuilder macro class; 我有一个LangBuilder宏类; it's used to build a langObjects:Map<String, Dynamic> of texts in various languages at compile time, and inject this structure in classes via @:build macro. 它用于构建langObjects:Map<String, Dynamic>在编译时以各种语言langObjects:Map<String, Dynamic>文本langObjects:Map<String, Dynamic> ,并通过@:build宏在类中注入此结构。 Every item of the Map has a field for every language supported. 地图的每个项目都包含支持的每种语言的字段。 So the result is: 结果是:

@:build(LangBuilder.build())
class Lang{}

trace(Lang.langObjects["hello-world"].en); //outputs "Hello World!"
trace(Lang.langObjects["hello-world"].it); //outputs "Ciao Mondo!"

This works perfectly, but I thought I could make a cleaner job hiding the langObjects structure using a function getLangText with arguments the id of the text (eg "hello-world" ) and the language code (eg "it" ). 这非常有效,但我想我可以使用函数getLangText隐藏langObjects结构,其中包含文本id(例如"hello-world" )和语言代码(例如"it" )的参数。

So I'm trying to add this function to classes: 所以我试图将这个函数添加到类中:

public static function getLangText(id:String, lang:String)

Its non-macro version could be expressed as: 其非宏版本可表示为:

public static function getLangText(id:String, lang:String){
    var _langObj_id = langObjects[id];
    switch(lang){
        case "it":
            return _langObj_id.it;
        case "en":
            return _langObj_id.en;
    }
    return "Missing Translation";

If i translate this function as a macro with this code: 如果我使用此代码将此函数转换为宏:

var code = macro {
  var _langObj_id = langObjects[$i{"id"}];
  switch($i{"lang"}){
    case "it":
      return _langObj_id.it;
    case "en":
      return _langObj_id.en;
  }
  return "Missing translation";
};

var myFunc:Function = {
  args: [{
    name: "id",
    type: TPath({name: "String", pack: []}),
    meta: null
  },
  {
    name: "lang",
    type: TPath({name: "String", pack: []}),
    meta: null
  }],
  ret: (macro:String),
  expr: macro $code
};

fields.push({
  pos: Context.currentPos(),
  name: "getLangText",
  meta: null,
  kind: FieldType.FFun(myFunc),
  doc: null,
  access: [Access.APublic, Access.AStatic]
});

... it works without problems. ......它没有问题。 However I would like to know how it could be written without the switch , to make it more flexible and to learn something about haxe macros. 但是我想知道如何在没有switch情况下编写它,使其更灵活,并学习一些关于haxe宏的东西。 I have seen some examples where fields could be accessed in macros with $p{} or with object.$fieldName . 我已经看到了一些示例,其中可以使用$p{}或使用object.$fieldName在宏中访问字段。 However the haxe manual warns that the second form could be used only for simple identifiers; 然而,haxe手册警告第二种形式只能用于简单的标识符; for example object.${fieldName} would not work. 例如object.${fieldName}不起作用。

So I try this code: 所以我试试这段代码:

var code = macro {
  var l:String = $i{"lang"};
  var _langObj_id = langObjects[$i{"id"}];
  return _langObj_id.$l;
};

The compiler gives an error 编译器出错

Unknown identifier : l 未知标识符:l

on the line containing return _langObj_id.$l; 在包含return _langObj_id.$l; .

Then i tried to use the $p{} reification: 然后我尝试使用$p{}具体化:

var code = macro {
  var _langObj_id = langObjects[$i{"id"}];
  return macro $p{["_langObj_id", $i{"lang"}]};
};

But the error is similar: 但错误类似:

Unknown identifier : lang 未知标识符:lang

I can surely change the langObjects structure to Map<String, Map<String, String>> and then change the code to: 我肯定可以将langObjects结构更改为Map<String, Map<String, String>> ,然后将代码更改为:

var code = macro {
  return macro langObjects[$i{"id"}][$i{"lang"}];
};

I think this would work, but now i'm trying to understand why both _langObj_id.$lang and $p{["_langObj_id", $i{"lang"}]} wouldn't work, and what would be the correct way to access a field in a situation like that. 我认为这会有效,但现在我试图理解为什么_langObj_id.$lang$p{["_langObj_id", $i{"lang"}]}都不起作用,以及正确的方法是什么在这种情况下访问一个字段。

The value of the lang parameter is not known at compile- / macro-time, so I don't see how you could generate a field access expression like langObjects["mytext"].en . 在compile- / macro-time时不知道lang参数的值,所以我看不出如何生成像langObjects["mytext"].en这样的字段访问表达式。 At runtime when getLangText() is actually called, lang could be "en" , or anything else. 在运行时,实际调用getLangText()时, lang可以是"en" ,或其他任何东西。 So that would still require a switch-case, if-else-chain or reflection to handle all the possible values. 因此,仍然需要一个switch-case,if-else-chain或反射来处理所有可能的值。

If instead of using being created by a build macro, getLangText() was an expression macro / a macro function , the function call would be evaluated at compile-time, and be replaced with the expression it returns. 如果不使用由构建宏创建的getLangText()是表达式宏/ macro function ,则函数调用将在编译时进行求值,并替换为它返回的表达式。 That would allow you to generate the appropriate field access expression based on the parameters. 这将允许您根据参数生成适当的字段访问表达式。 It could look something like this: 它可能看起来像这样:

class Macro {
    public static var langObjects = ["mytext" => {en: "hello", de: "hallo"}];

    public static macro function getLangText(id:String, lang:String) {
        return macro {
            var langObject = Macro.langObjects[$v{id}];
            langObject.$lang;
        }
    }
}
class Main {
    static function main() {
        trace(Macro.getLangText("mytext", "en"));
        trace(Macro.getLangText("mytext", "de"));
    }
}

Compiles to the following on the JS target: 在JS目标上编译如下:

Main.main = function() {
    var langObject = Macro.langObjects.get("mytext");
    console.log("source/Main.hx:3:",langObject.en);
    var langObject1 = Macro.langObjects.get("mytext");
    console.log("source/Main.hx:4:",langObject1.de);
};

Perhaps that's what you're looking for? 也许这就是你要找的东西? Hard to say without knowing what problem you're trying to solve. 很难说不知道你想要解决什么问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM