如何导出C++自定义类,导出后如何在ArkTS侧进行类方法调用?

如何导出C++自定义类,导出后如何在ArkTS侧进行类方法调用

阅读 4.7k
avatarAI BotBETA

要导出C++自定义类并在ArkTS(我假设你指的是TypeScript,因为ArkTS并不是一个我熟悉或广泛认知的编程语言)中进行类方法调用,你通常需要使用某种形式的C++/CLI(Common Language Infrastructure)桥接,或者通过C++到C的ABI(Application Binary Interface)导出你的类,然后使用TypeScript的外部模块调用这些C函数。

这里提供一个基本步骤指南,用于将C++类导出为C接口,然后在TypeScript中调用它们:

步骤 1: 在C++中定义类和导出函数

首先,在C++中定义你的类,并创建导出函数作为类的包装器。这些导出函数将充当C++和TypeScript之间的桥梁。

// MyClass.h
#ifdef __cplusplus
extern "C" {
#endif

// 导出的函数原型
void* MyClass_Create();
void MyClass_Destroy(void* instance);
void MyClass_DoSomething(void* instance);

#ifdef __cplusplus
}
#endif

class MyClass {
public:
    MyClass() {}
    ~MyClass() {}
    void DoSomething() {
        // 类方法实现
    }
};
// MyClass.cpp
#include "MyClass.h"

void* MyClass_Create() {
    return new MyClass();
}

void MyClass_Destroy(void* instance) {
    delete static_cast<MyClass*>(instance);
}

void MyClass_DoSomething(void* instance) {
    static_cast<MyClass*>(instance)->DoSomething();
}

步骤 2: 编译C++代码为动态库

编译你的C++代码为动态链接库(例如,.dll.so.dylib),并确保导出的函数是可见的。

步骤 3: 在TypeScript中声明外部模块

在TypeScript中,你需要声明一个外部模块来代表你的C++动态库,并提供相应的函数签名。

// myclass.d.ts
declare module "myclass" {
    function MyClass_Create(): any;
    function MyClass_Destroy(instance: any): void;
    function MyClass_DoSomething(instance: any): void;
}

步骤 4: 在TypeScript中加载和使用C++类

在TypeScript中,你可以使用import语句来加载你的C++动态库,并调用之前声明的函数。

// main.ts
import * as MyClassModule from 'myclass';

// 创建C++类的实例
const myInstance = MyClassModule.MyClass_Create();

// 调用C++类的方法
MyClassModule.MyClass_DoSomething(myInstance);

// 销毁C++类的实例
MyClassModule.MyClass_Destroy(myInstance);

请注意,这个示例非常简化,并假设你熟悉如何设置C++项目、如何编译为动态库以及如何在TypeScript中处理外部模块。具体的细节可能因你的开发环境、编译器、构建系统以及C++和TypeScript的版本而有所不同。

此外,对于复杂的C++特性(如模板、异常等),你可能需要更复杂的桥接技术,或者可能需要使用如SWIG、Boost.Python或其他专门的库来简化C++到C或TypeScript的桥接过程。

1 个回答

可以通过napi_define_calss建立ArkTS类与C++侧的映射关系,然后将对应的对象挂载到export上导出。然后在index.d.ts文件中定义对应的ArkTS侧类接口,实现对class的调用。

参考代码如下:
C++侧定义类。

// MyDemo.h 定义C++ 类 
class MyDemo { 
  public: 
    MyDemo(std::string m_name); 
    MyDemo(); 
    ~MyDemo();   
    std::string name; 
    int add(int a, int b); 
    int sub(int a, int b); 
};

hello.cpp中完成ArkTS类与C++侧的映射关系,并将其挂载到export上。

// ArkTS对象构造函数 
static napi_value JsConstructor(napi_env env, napi_callback_info info) { 
    // 创建napi对象 
    napi_value jDemo = nullptr; 
    size_t argc = 0; 
    napi_value args[1] = {0}; 
    // 获取构造函数入参 
    napi_get_cb_info(env, info, &argc, args, &jDemo, nullptr); 
    // args[0] js传入的参数 
    char name[50]; 
    size_t result = 0; 
    napi_get_value_string_utf8(env, args[0], name, sizeof(name) + 1, &result); 
    // 创建C++对象 
    MyDemo *cDemo = new MyDemo(name); 
    OH_LOG_INFO(LOG_APP, "%{public}s", (cDemo->name).c_str()); 
    // 设置js对象name属性 
    napi_set_named_property(env, jDemo, "name", args[0]); 
    // 绑定JS对象与C++对象 
    napi_wrap( 
        env, jDemo, cDemo, 
        // 定义js对象回收时回调函数,用来销毁C++对象,防止内存泄漏 
        [](napi_env env, void *finalize_data, void *finalize_hint) { 
            MyDemo *cDemo = (MyDemo *)finalize_data; 
            delete cDemo; 
            cDemo = nullptr; 
        }, 
        nullptr, nullptr); 
    return jDemo; 
} 
// ArkTS对象add函数 
static napi_value JsAdd(napi_env env, napi_callback_info info) { 
    size_t argc = 2; 
    napi_value args[2] = {nullptr}; 
    napi_value jDemo = nullptr; 
    napi_get_cb_info(env, info, &argc, args, &jDemo, nullptr); 
    MyDemo *cDemo = nullptr; 
    // 将ArkTS对象转为c对象 
    napi_unwrap(env, jDemo, (void **)&cDemo); 
    // 获取ArkTS传递的参数 
    int value0; 
    napi_get_value_int32(env, args[0], &value0); 
    int value1; 
    napi_get_value_int32(env, args[1], &value1); 
    int cResult = cDemo->add(value0, value1); 
    napi_value jResult; 
    napi_create_int32(env, cResult, &jResult); 
    return jResult; 
} 
// ArkTS对象sub函数 
static napi_value JsSub(napi_env env, napi_callback_info info) { 
    size_t argc = 2; 
    napi_value args[2] = {nullptr}; 
    napi_value jDemo = nullptr; 
    napi_get_cb_info(env, info, &argc, args, &jDemo, nullptr); 
    MyDemo *cDemo = nullptr; 
    // 将ArkTS对象转为c对象 
    napi_unwrap(env, jDemo, (void **)&cDemo); 
    // 获取ArkTS传递的参数 
    int value0; 
    napi_get_value_int32(env, args[0], &value0); 
    int value1; 
    napi_get_value_int32(env, args[1], &value1); 
    int cResult = cDemo->sub(value0, value1); 
    napi_value jResult; 
    napi_create_int32(env, cResult, &jResult); 
    return jResult; 
} 
static napi_value Add(napi_env env, napi_callback_info info) { 
    size_t requireArgc = 2; 
    size_t argc = 2; 
    napi_value args[2] = {nullptr}; 
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 
    napi_valuetype valuetype0; 
    napi_typeof(env, args[0], &valuetype0); 
    napi_valuetype valuetype1; 
    napi_typeof(env, args[1], &valuetype1); 
    int value0; 
    napi_get_value_int32(env, args[0], &value0); 
    int value1; 
    napi_get_value_int32(env, args[1], &value1); 
    MyDemo *demo = new MyDemo(); 
    // 调用so中函数进行运算 
    int result = demo->add(value0, value1); 
    napi_value sum; 
    napi_create_int32(env, result, &sum); 
    delete demo; 
    return sum; 
} 
static napi_value Sub(napi_env env, napi_callback_info info) { 
    size_t argc = 2; 
    napi_value args[2] = {nullptr}; 
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 
    napi_valuetype valuetype0; 
    napi_typeof(env, args[0], &valuetype0); 
    napi_valuetype valuetype1; 
    napi_typeof(env, args[1], &valuetype1); 
    int value0; 
    napi_get_value_int32(env, args[0], &value0); 
    int value1; 
    napi_get_value_int32(env, args[1], &value1); 
    MyDemo *demo = new MyDemo(); 
    // 调用so中函数进行运算 
    int result = demo->sub(value0, value1); 
    napi_value num; 
    napi_create_int32(env, result, &num); 
    delete demo; 
    return num; 
} 
EXTERN_C_START 
static napi_value Init(napi_env env, napi_value exports) 
{ 
    // 定义模块需要对外暴露的方法 
    napi_property_descriptor desc[] = { 
        {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr}, 
        {"sub", nullptr, Sub, nullptr, nullptr, nullptr, napi_default, nullptr}}; 
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 
    // 通过napi_define_class建立ArkTS类与C++侧的映射关系,然后将对应的对象挂载到export上 
    napi_property_descriptor classProp[] = {{"add", nullptr, JsAdd, nullptr, nullptr, nullptr, napi_default, nullptr}, 
                                            {"sub", nullptr, JsSub, nullptr, nullptr, nullptr, napi_default, nullptr}}; 
    napi_value jDemo = nullptr; 
    const char *jDemoName = "MyDemo"; 
    // 建立ArkTS构造函数与C++方法的关联,指定2个prop 
    napi_define_class(env, jDemoName, sizeof(jDemoName), JsConstructor, nullptr, 
                      sizeof(classProp) / sizeof(classProp[0]), classProp, &jDemo); 
    napi_set_named_property(env, exports, jDemoName, jDemo); 
    return exports; 
} 
EXTERN_C_END

index.d.ts文件中定义ArkTS类。

declare namespace testNapi { 
  const add: (a: number, b: number) => number; 
  const sub: (a: number, b: number) => number; 
  // 定义ArkTS接口 
  class MyDemo { 
    constructor(name:string) 
    name: string 
    add(a: number, b: number): number 
    sub(a: number, b: number): number 
  } 
} 
export default testNapi;

ArkTS侧实现调用。

import testNapi from 'libentry.so'; 
// ... 
    new testNapi.MyDemo('abc'); 
    hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3)); 
    hilog.info(0x0000, 'testTag', 'Test NAPI 2 - 3 = %{public}d', testNapi.sub(2, 3)); 
// ... 
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进