You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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;
}

我的核心需求:

  1. 让Module.getNumber()直接返回JS的number类型;同时向接收my_number_value的C函数传入JSnumber时,自动在C栈上创建实例
  2. 自定义字符串类型实现像std::string那样与JSstring自动互转
  3. 利用已有的枚举-字符串互转函数,实现枚举类型与字符串的双向自动转换

解决方案

一、自定义数值类型的自动双向转换

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 07:00:57