如何通过Embind实现自定义类型与JS原始类型绑定及枚举字符串转换?
问题描述
我正在学习Emscripten和Embind,已经为自定义类型编写了绑定,但在JavaScript端接收到的都是需要调用delete()销毁的复杂对象。针对两类自定义类型,我希望实现自动转换以避免手动销毁的繁琐:
- 基于
std::array<char>实现的固定最大长度自定义字符串类型 - 仅封装一个整数的自定义数值包装类型
my_number_value(用于防止无关数字转换)
当前绑定逻辑下,JS调用Module.getNumber()需要手动提取值并销毁对象:
function getNumber() { const num = Module.getNumber(); const val = num.x; num.delete(); return val; }
我的核心需求:
- 让
Module.getNumber()直接返回JS的number类型;同时向接收my_number_value的C函数传入JSnumber时,自动在C栈上创建实例 - 自定义字符串类型实现像
std::string那样与JSstring自动互转 - 利用已有的枚举-字符串互转函数,实现枚举类型与字符串的双向自动转换
解决方案
一、自定义数值类型的自动双向转换
Embind的**自定义转换器(emscripten::converter)**可以实现值类型与JS原生类型的自动映射,无需将自定义类型绑定为类。以下是my_number_value的实现:
#pragma once #ifdef __EMSCRIPTEN__ #include "my_number_value.h" #include <emscripten/emscripten.h> #include <emscripten/bind.h> // 注册双向转换器:C++ my_number_value <-> JS number namespace emscripten { template<> struct converter<my_number_value> { // JS number转C++ my_number_value static my_number_value from_js(val v) { return my_number_value(v.as<int>()); } // C++ my_number_value转JS number static val to_js(const my_number_value& v) { return val(v.x); } }; } // 测试函数:返回my_number_value EMSCRIPTEN_KEEPALIVE my_number_value getNumber() { return my_number_value{42}; } // 测试函数:接收my_number_value EMSCRIPTEN_KEEPALIVE int takeNumber(my_number_value num) { return num.x * 2; } EMSCRIPTEN_BINDINGS(mymodule) { // 无需绑定my_number_value为class_,转换器自动处理类型转换 function("getNumber", &getNumber); function("takeNumber", &takeNumber); } #endif
JS端直接调用即可,无需手动销毁对象:
const num = Module.getNumber(); // 直接得到42(number类型) const doubled = Module.takeNumber(100); // 传入数字自动转为my_number_value,返回200
二、自定义字符串类型的自动双向转换
假设自定义字符串类型为my_fixed_string(基于std::array<char, N>),同样通过转换器实现与JS字符串的互转:
1. 自定义字符串类型定义
// my_fixed_string.h #include <array> #include <string> template<size_t N> struct my_fixed_string { std::array<char, N> data{}; // 从std::string构造 explicit my_fixed_string(const std::string& s) { std::copy(s.begin(), s.end(), data.begin()); if (s.size() < N) { data[s.size()] = '\0'; } } // 转换为std::string operator std::string() const { return std::string(data.data()); } };
2. 注册转换器与绑定
#ifdef __EMSCRIPTEN__ #include "my_fixed_string.h" #include <emscripten/bind.h> namespace emscripten { template<size_t N> struct converter<my_fixed_string<N>> { // JS string转C++ my_fixed_string static my_fixed_string<N> from_js(val v) { std::string s = v.as<std::string>(); return my_fixed_string<N>(s); } // C++ my_fixed_string转JS string static val to_js(const my_fixed_string<N>& s) { return val(static_cast<std::string>(s)); } }; } // 测试函数:返回自定义字符串 EMSCRIPTEN_KEEPALIVE my_fixed_string<32> getFixedString() { return my_fixed_string<32>("Hello from C++"); } // 测试函数:接收自定义字符串 EMSCRIPTEN_KEEPALIVE std::string takeFixedString(my_fixed_string<32> s) { return static_cast<std::string>(s) + " (processed)"; } EMSCRIPTEN_BINDINGS(mymodule_string) { function("getFixedString", &getFixedString); function("takeFixedString", &takeFixedString); } #endif
JS端调用示例:
const str = Module.getFixedString(); // 直接得到"Hello from C++"(string类型) const processed = Module.takeFixedString("Hi from JS"); // 传入字符串自动转换,返回处理后的结果
三、枚举与字符串的双向自动转换
针对已有枚举-字符串互转函数的场景,同样可以通过自定义转换器实现自动映射,或直接在enum_绑定中指定固定映射。
方法1:直接绑定枚举与字符串映射
如果枚举值与字符串的对应关系固定,可直接在绑定中声明:
// my_enum.h enum class MyEnum { OptionA, OptionB, OptionC }; EMSCRIPTEN_BINDINGS(mymodule_enum) { enum_<MyEnum>("MyEnum") .value("OptionA", MyEnum::OptionA) .value("OptionB", MyEnum::OptionB) .value("OptionC", MyEnum::OptionC); }
JS端可通过字符串或枚举值访问:
Module.MyEnum.OptionA; // 获取枚举数值 Module.MyEnum["OptionB"]; // 同样获取枚举数值
方法2:自定义转换器实现灵活互转
如果已有现成的myEnumToString和stringToMyEnum函数,用转换器实现自动双向转换:
// my_enum.h enum class MyEnum { OptionA, OptionB, OptionC }; // 已有转换函数 const char* myEnumToString(MyEnum e); MyEnum stringToMyEnum(const std::string& s);
#ifdef __EMSCRIPTEN__ #include "my_enum.h" #include <emscripten/bind.h> namespace emscripten { template<> struct converter<MyEnum> { // JS string/number转C++ MyEnum static MyEnum from_js(val v) { if (v.isString()) { return stringToMyEnum(v.as<std::string>()); } else { return static_cast<MyEnum>(v.as<int>()); } } // C++ MyEnum转JS string static val to_js(MyEnum e) { return val(myEnumToString(e)); } }; } // 测试函数:返回枚举 EMSCRIPTEN_KEEPALIVE MyEnum getEnum() { return MyEnum::OptionB; } // 测试函数:接收枚举 EMSCRIPTEN_KEEPALIVE std::string takeEnum(MyEnum e) { return "Received: " + std::string(myEnumToString(e)); } EMSCRIPTEN_BINDINGS(mymodule_enum_converter) { function("getEnum", &getEnum); function("takeEnum", &takeEnum); } #endif
JS端调用示例:
const enumStr = Module.getEnum(); // 直接得到"OptionB"(string类型) const result = Module.takeEnum("OptionA"); // 传入字符串自动转为MyEnum,返回"Received: OptionA"
内容的提问来源于stack exchange,提问作者Tomáš Zato
相关产品推荐
相关产品推荐

