Emscripten绑定C++类时部分重载方法在JS中无法调用
问题:Emscripten绑定C++重载方法时,Parser参数的重载无法正确调用
场景重现
我通过Emscripten将C++的RuleContext类绑定到JavaScript/TypeScript,该类包含三个toStringTree重载方法:
class RuleContext : public tree::ParseTree { public: virtual std::string toStringTree(Parser *recog, bool pretty = false) override; virtual std::string toStringTree(std::vector<std::string> &ruleNames, bool pretty = false); virtual std::string toStringTree(bool pretty = false) override; };
对应的Emscripten绑定代码:
class_<RuleContext, base<tree::ParseTree>>("RuleContext") .function("toStringTree", select_overload<std::string(Parser *, bool)> (&RuleContext::toStringTree), allow_raw_pointers()) .function("toStringTree", select_overload<std::string(std::vector<std::string> &, bool)> (&RuleContext::toStringTree)) .function("toStringTree", select_overload<std::string(bool)> (&RuleContext::toStringTree)) .allow_subclass<RuleContextWrapper>("RuleContextWrapper");
TypeScript声明:
export declare class RuleContext extends Extendable { public toStringTree(recognizer: Parser, pretty: boolean): string; public toStringTree(ruleNames: Vector<string>, pretty: boolean): string; public toStringTree(pretty: boolean): string; }
目前仅两个重载可正常调用:
console.log(tree.toStringTree(names, true)); console.log(tree.toStringTree(true));
但调用传入Parser实例的重载时:
console.log(tree.toStringTree(parser, true));
出现以下错误:
BindingError: Expected null or instance of StringVector, got an instance of Recognizer at throwBindingError (file:///Volumes/Extern/Work/projects/antlr4wasm/wasm/antlr4-runtime-wasm.js:3910:8) at upcastPointer (file:///Volumes/Extern/Work/projects/antlr4wasm/wasm/antlr4-runtime-wasm.js:4652:4) at RegisteredPointer.nonConstNoSmartPtrRawPointerToWireType [as toWireType] (file:///Volumes/Extern/Work/projects/antlr4wasm/wasm/antlr4-runtime-wasm.js:4760:12) at Test1Context.RuleContext$toStringTree (eval at newFunc (file:///Volumes/Extern/Work/projects/antlr4wasm/wasm/antlr4-runtime-wasm.js:4957:22), <anonymous>:9:26) at proto.<computed> [as toStringTree] (file:///Volumes/Extern/Work/projects/antlr4wasm/wasm/antlr4-runtime-wasm.js:4612:61) at file:///Volumes/Extern/Work/projects/antlr4wasm/tests/test.ts:24:22
补充:StringVector是std::vector<std::string>的绑定类型,Recognizer是Parser的基类。
问题原因
Emscripten的函数重载匹配逻辑依赖参数数量和类型识别,但对于两个参数数量相同的重载(Parser* + bool 和 std::vector<std::string>& + bool),JavaScript层面无法精确区分自定义C++类型的实例,导致绑定系统误将Parser实例匹配到期望StringVector的重载,从而抛出类型转换错误。
解决方案
方案1:为重载方法指定不同的JS函数名
直接避免重载冲突,给两个双参数的重载分配不同的名称,修改Emscripten绑定代码:
class_<RuleContext, base<tree::ParseTree>>("RuleContext") // 给Parser参数的重载单独命名 .function("toStringTreeWithParser", select_overload<std::string(Parser *, bool)> (&RuleContext::toStringTree), allow_raw_pointers()) // 给Vector参数的重载单独命名 .function("toStringTreeWithRuleNames", select_overload<std::string(std::vector<std::string> &, bool)> (&RuleContext::toStringTree)) // 保留单参数重载的原名 .function("toStringTree", select_overload<std::string(bool)> (&RuleContext::toStringTree)) .allow_subclass<RuleContextWrapper>("RuleContextWrapper");
同步更新TypeScript声明:
export declare class RuleContext extends Extendable { public toStringTreeWithParser(recognizer: Parser, pretty: boolean): string; public toStringTreeWithRuleNames(ruleNames: Vector<string>, pretty: boolean): string; public toStringTree(pretty: boolean): string; }
调用时使用对应名称即可:
console.log(tree.toStringTreeWithParser(parser, true));
方案2:调整重载绑定顺序并确保类型注册正确
如果希望保留原函数名,可尝试调整重载的绑定顺序,同时确保Parser类已正确完成Emscripten绑定并注册继承关系:
// 先绑定基类Recognizer class_<Recognizer>("Recognizer"); // 再绑定Parser,指定基类 class_<Parser, base<Recognizer>>("Parser"); // 调整RuleContext的重载绑定顺序,将Parser参数的重载放在最后 class_<RuleContext, base<tree::ParseTree>>("RuleContext") .function("toStringTree", select_overload<std::string(bool)> (&RuleContext::toStringTree)) .function("toStringTree", select_overload<std::string(std::vector<std::string> &, bool)> (&RuleContext::toStringTree)) .function("toStringTree", select_overload<std::string(Parser *, bool)> (&RuleContext::toStringTree), allow_raw_pointers()) .allow_subclass<RuleContextWrapper>("RuleContextWrapper");
此方案依赖Emscripten的匹配顺序,稳定性不如方案1,若仍有问题建议优先使用方案1。
内容的提问来源于stack exchange,提问作者Mike Lischke
相关产品推荐
相关产品推荐

