使用napi实现ts枚举类型

在napi模块中定义枚举类型

枚举类型不是js的原生类型,它是ts中独有的语法,但是napi方法仅支持操作js对象。

下文将提供一个demo用来实现一个js的enum类。

分析ts枚举类的实现方式

定义一个ts枚举类

TypeScript 代码如下:

enum EnumClass {
    VAL_A = 0,
    VAL_B,
    VAL_C,
}

使用tsc命令将ts文件转为js文件,生成的js文件内容如下:

var EnumClass;
(function (EnumClass) {
    EnumClass[EnumClass["VAL_A"] = 0] = "VAL_A";
    EnumClass[EnumClass["VAL_B"] = 1] = "VAL_B";
    EnumClass[EnumClass["VAL_C"] = 2] = "VAL_C";
})(EnumClass || (EnumClass = {}));

分析代码,tsc 转换后的代码逻辑如下:

  1. 定义一个全局变量 EnumClass
  2. 构造一个匿名函数,该函数接受一个入参,入参类型经推断应为Object。函数逻辑如下:
    1. 为Object EnumClass定义了三个 keyenum的keyvalueenum的value 的属性。
    2. 为Object EnumClass定义了三个 valueenum的keykeyenum的value 的属性。
  3. 将全局变量EnumClass传递到上述函数,若全局变量EnumClass为undefined或null等值(即或语句前条件为false)则使其为空Object。

根据以上步骤,不难发现,enum在js中实际上是一个key、value互相作为key/value的Object。

使用napi方法进行实现js枚举类

定义一个C/C++的枚举类,其将作为js枚举类在Native枚举类的映射

enum EnumClass { 
    VAL_A = 0,
    VAL_B,
    VAL_C,
};

创建一个js Object, 用以容纳枚举类的内容

napi_value enumObject = nullptr;
napi_create_object(env, &enumObject);

定义两个下文将会使用到的宏,减少模板代码

// 按照枚举值的 key 和 value 创建对应的 napi value
#define GEN_ENUM_KV(env, enum, key, jsKey, jsVal) do {                 \
    napi_create_string_utf8((env), #key, NAPI_AUTO_LENGTH, &(jsKey));  \
    napi_create_int32((env), enum::key, &(jsVal));                     \
} while (0)

// 用以减少定义 napi_property_descriptor 模板代码
#define SET_ENUM_KV(jsKey, jsVal)                                                  \
    { nullptr, (jsKey), nullptr, nullptr, nullptr, (jsVal), napi_default, nullptr }

创建并初始化所需的 napi_value

napi_value enumKeys[3] = {nullptr};  // 3: num of EnumClass
napi_value enumVals[3] = {nullptr};  // 3: num of EnumClass

GEN_ENUM_KV(env, EnumClass, VAL_A, enumKeys[0], enumVals[0]);
GEN_ENUM_KV(env, EnumClass, VAL_B, enumKeys[1], enumVals[1]);
GEN_ENUM_KV(env, EnumClass, VAL_C, enumKeys[2], enumVals[2]);  // 2: index array

枚举类的实现应用到js Object

// 创建一个 napi_property_descriptor 数组,用以容纳js枚举值

napi_property_descriptor desc[] = {
    NAPI_SET_VALUE(enumKeys[0], enumVals[0]),
    NAPI_SET_VALUE(enumKeys[1], enumVals[1]),
    NAPI_SET_VALUE(enumKeys[2], enumVals[2]),  // 2: index array
};

napi_define_properties(env, enumObject, sizeof(desc) / sizeof(desc[0]), desc);

napi_set_property(env, enumObject, enumVals[0], enumKeys[0]),
napi_set_property(env, enumObject, enumVals[1], enumKeys[1]),
napi_set_property(env, enumObject, enumVals[2], enumKeys[2]),  // 2: index array

完整示例代码

Native侧 C++ 代码。

enum EnumClass { 
    VAL_A = 0,
    VAL_B,
    VAL_C,
};

#define GEN_ENUM_KV(env, enum, key, jsKey, jsVal) do {                 \
    napi_create_string_utf8((env), #key, NAPI_AUTO_LENGTH, &(jsKey));  \
    napi_create_int32((env), enum::key, &(jsVal));                     \
} while (0)

#define NAPI_SET_VALUE(jsKey, jsVal)                                                  \
    {nullptr, (jsKey), nullptr, nullptr, nullptr, (jsVal), napi_default, nullptr}

static SomeFunction(napi_env env /* args... */)
{
    // ... ...
    napi_value enumObject = nullptr;
    napi_create_object(env, &enumObject);

    napi_value enumKeys[3] = {nullptr};  // 3: num of EnumClass
    napi_value enumVals[3] = {nullptr};  // 3: num of EnumClass

    GEN_ENUM_KV(env, EnumClass, VAL_A, enumKeys[0], enumVals[0]);
    GEN_ENUM_KV(env, EnumClass, VAL_B, enumKeys[1], enumVals[1]);
    GEN_ENUM_KV(env, EnumClass, VAL_C, enumKeys[2], enumVals[2]);  // 2: index array

    napi_property_descriptor desc[] = {
        NAPI_SET_VALUE(enumKeys[0], enumVals[0]),
        NAPI_SET_VALUE(enumKeys[1], enumVals[1]),
        NAPI_SET_VALUE(enumKeys[2], enumVals[2]),  // 2: index array
    };

    napi_set_property(env, enumObject, enumVals[0], enumKeys[0]),
    napi_set_property(env, enumObject, enumVals[1], enumKeys[1]),
    napi_set_property(env, enumObject, enumVals[2], enumKeys[2]),  // 2: index array

    napi_define_properties(env, enumObject, sizeof(desc) / sizeof(desc[0]), desc);
    // ... ...
}

对应的枚举类的 .d.ts 描述文件。

export enum EnumClass {
    VAL_A = 0,
    VAL_B,
    VAL_C,
}

与上述逻辑相匹配的js代码。

const EnumClass = {
    0: "VAL_A",
    1: "VAL_B",
    2: "VAL_C",

    VAL_A: 0,
    VAL_B: 1,
    VAL_C: 2,
}
posted @ 2024-07-05 17:41  milkpotatoes  阅读(245)  评论(0)    收藏  举报