【问题标题】:Using local variables in function built with haxe macro在用 haxe 宏构建的函数中使用局部变量
【发布时间】:2018-11-25 21:55:44
【问题描述】:

我有一个LangBuilder 宏类;它用于在编译时构建各种语言的文本langObjects:Map<String, Dynamic>,并通过@:build 宏将该结构注入到类中。地图的每个项目都有一个支持每种语言的字段。所以结果是:

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

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

这很好用,但我认为我可以使用带有文本 id(例如 "hello-world")和语言代码(例如 "it" )。

所以我正在尝试将此函数添加到类中:

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

它的非宏版本可以表示为:

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";

如果我用这段代码将此函数翻译为宏:

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]
});

...它可以正常工作。但是我想知道如何在没有switch 的情况下编写它,以使其更加灵活并了解有关 haxe 宏的知识。我已经看到一些示例,其中可以使用$p{} 或object.$fieldName 在宏中访问字段。然而,haxe 手册警告说,第二种形式只能用于简单的标识符;例如object.${fieldName} 将不起作用。

所以我试试这个代码:

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

编译器报错

未知标识符:l

在包含return _langObj_id.$l;的行上。

然后我尝试使用$p{} reification:

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

但错误类似:

未知标识符:语言

我当然可以将langObjects结构更改为Map<String, Map<String, String>>,然后将代码更改为:

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

我认为这会起作用,但现在我试图理解为什么 _langObj_id.$lang 和 $p{["_langObj_id", $i{"lang"}]} 都不起作用,以及在这种情况下访问字段的正确方法是什么。

【问题讨论】:

    标签: macros haxe


    【解决方案1】:

    lang 参数的值在编译/宏时是未知的,所以我看不出如何生成像langObjects["mytext"].en 这样的字段访问表达式。在运行时实际调用getLangText() 时,lang 可能是"en",或其他任何值。所以这仍然需要一个 switch-case、if-else-chain 或反射来处理所有可能的值。

    如果 getLangText() 不是由构建宏创建,而是一个表达式宏/macro function,则函数调用将在编译时进行评估,并被它返回的表达式替换。这将允许您根据参数生成适当的字段访问表达式。它可能看起来像这样:

    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"));
        }
    }
    

    在 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);
    };
    

    也许这就是您要找的东西?很难说不知道您要解决什么问题。

    【讨论】:

    • 感谢您的回答。我更新了问题以更好地解释我正在尝试做的事情。
    • 使用嵌套地图绝对是这里更务实的方法......你明白我回答的第一部分吗?当您只知道在运行时该字段的名称将是什么时,您无法在编译时生成字段访问。您尝试的两种具体化语法都要求lang 是宏的code 部分之外的变量。由于不存在,因此您最终会得到“未知标识符”。
    • 我知道lang 在构建时是未知的......所以除了通过switch/if-then-else/reflection 列出各种条件之外,没有办法动态访问字段?
    • 嗯,有一种在运行时动态访问字段的常用方法:反射。要么是那个,要么是 switch(或 if-else-chain),其中包含您所说的所有可能值。
    • 在编译时用实际字符串替换函数的替代宏也很简洁。唉,我不能使用它,因为我需要在运行时更改语言。也许我可以将它与嵌套的地图结构合并......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-14
    • 2014-04-29
    • 2023-03-12
    • 1970-01-01
    • 2014-01-31
    • 1970-01-01
    相关资源
    最近更新 更多