股票
板块
用户
资讯
首页
天眼
自选
发现
开户
首页
天眼
自选
发现
我的

ArkTS 极简开发| Taihe:跨语言开发的极简之道

同样定义一个跨语言接口,传统方案需要 200+行手写代码,Taihe 只需要 4 行 IDL。代码量减少 95%,类型安全有保障。

一、开发者的现实困境


在 HarmonyOS 生态开发中,一个常见场景是:ArkTS 应用需要调用 C++ 实现的核心能力。传统做法要求开发者同时掌握 NAPI、类型转换、内存管理等多个领域的知识。

典型的跨语言绑定代码如下:

// C++ 实现class Calculator {public:    int add(int a, int b) { return a + b; }};
// NAPI 绑定(简化版,实际代码更长)static napi_value Add(napi_env env, napi_callback_info info) {    size_t argc = 2;    napi_value args[2];    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);        int32_t a, b;    napi_get_value_int32(env, args[0], &a);    napi_get_value_int32(env, args[1], &b);        Calculator* calc = new Calculator();    int result = calc->add(a, b);        napi_value napiResult;    napi_create_int32(env, result, &napiResult);    return napiResult;}
// ArkTs 侧还需要对应的类型声明export declare class Calculator {    add(a: number, b: number): number;}

*可左右滑动查看

200+行代码,涉及 C++、ArkTs 两种语言,一处错误就可能导致崩溃或内存泄漏。

更糟糕的是,当接口变更时,需要同步修改 C++ 实现、NAPI 绑定、ArkTs 声明三处代码,漏改一处就会出问题。

二、Taihe 是什么?


Taihe 是面向 HarmonyOS 生态系统的跨语言接口定义与代码生成工具。它的核心使命是:通过统一接口定义语言(IDL),为 HarmonyOS 生态提供可靠、高效、易于维护的跨语言解决方案。

Taihe 工具链介绍

Taihe 提供了核心编译器命令行工具 taihec,用于解析 Taihe IDL 文件并生成目标语言代码。它支持选择多种代码生成后端,生成适用于不同场景的代码。

基本用法:

taihec [taihe_files ...][options ...]

*可左右滑动查看

taihe_files 可以是一个或多个 Taihe IDL 文件,也可以使用通配符(例如 path/to/idl/*.ohidl)来指定多个文件。

options 用于指定代码生成的各种选项,详见下表。

命令行选项

代码生成后端

代码生成后端决定了 taihec 会生成哪些代码文件。后端之间存在依赖关系,工具会自动根据配置的后端来递归地启用所需的其它后端,生成完整的代码。

通用后端

C/C++ 后端

ArkTS 后端

使用示例:

# 生成 NAPI 桥接代码和 C++ 实现模板taihec path/to/idl/*.ohidl -O path/to/generated -Gnapi-bridge -Gcpp-author

*可左右滑动查看

三、Taihe 的核心理念:接口即契约


Taihe 的出发点非常明确:接口定义是跨语言开发的核心,语言只是实现细节。

通过统一的接口定义语言(IDL,Interface Definition Language),开发者只需关注"接口长什么样",Taihe 负责生成多种语言的绑定代码。

简洁的接口定义

// my.package.ohidlinterface ICalculator {    add(a: i32, b: i32): i32;    hello(name: String): String;}

*可左右滑动查看

4 行代码,完成一个完整的跨语言接口定义。

自动生成的多语言绑定

运行 taihec 命令处理 my.package.ohidl 这个文件后,Taihe 自动生成:

path/to/generated/├── include/│   ├── my.package.abi.h                   // ABI 层:聚合头文件│   ├── my.package.proj.hpp                // C++ 层:聚合通用头文件│   ├── my.package.user.hpp                // C++ 层:供接口【消费方】使用的头文件│   ├── my.package.impl.hpp                // C++ 层:供接口【发布方】使用的头文件│   └── my.package.napi.h                  // NAPI 层:桥接代码头文件├── src/│   ├── my.package.napi.cpp                // NAPI 层:桥接代码实现文件│   └── my.package.abi.c                   // ABI 层:接口 IID 等符号的定义└── temp/│   ├── my.package.napi_register.cpp       // NAPI 层:桥接代码注册文件│   └── my.package.impl.cpp                // C++ 层:为发布方提供的实现模板└── my.package.d.ts                        // ArkTs 声明文件

*可左右滑动查看

每种语言都有自己的生成代码,开发者可以直接使用,无需关心实现细节。以实际生产环境中的 napi_string/idl/string_test.ohidl IDL 定义为例,生成的文件目录如下:

四、系统架构:编译器、ABI 与语言投影


Taihe 的能力源于其精巧的系统架构,主要包含三个关键部分:

Taihe 编译器

Taihe 编译器(taihec)遵循经典的三阶段设计,将 IDL 合约转化为生产就绪代码:

1.前端 (Frontend):解析 IDL 源代码,将其转换成一种名为"中间表示"(IR)的内部数据结构。

2.语义分析 (Semantics):对 IR 进行类型检查和语义验证,确保接口定义的正确性。此阶段还会处理和验证注解(Attributes),用于精细化控制代码生成。

3.代码生成 (Code Generation):通过可插拔的语言后端(Language Backends),将验证过的 IR 转换成不同目标语言的代码。

稳定的 ABI (应用二进制接口)

编译器会根据 IDL 定义,生成一套基于 C 语言标准的 ABI(Application Binary Interface)。你可以把它理解为一种多种编程语言都能理解的"通用二进制语言",它精确定义了数据类型在内存中如何排列、函数该如何调用。这套 ABI 是实现跨语言互操作的基石。

语言投影 (Language Projection)

直接使用底层的 C ABI 进行开发繁琐且易错。因此,Taihe 重要的一步是自动生成语言投影——即为每种目标语言(如 C++、ArkTS)生成一套高级、易用且符合其语言习惯的 API 封装。

这些投影代码将底层的 ABI 复杂性有效隐藏,为开发者提供了调用原生接口般的顺畅体验。

对于 C++ 开发者,他们会得到易于使用的 C++ 类型、通过引用计数自动管理内存的接口对象,用于导出接口实现的各种方便易用的宏和模板,以及丰富的 STL 风格容器。

对于 ArkTS 开发者,他们会得到相应的 ArkTS 类和函数,与底层 C++ 的通信细节被自动处理。

五、核心技术优势


多目标语言:不只是"支持多种语言",而是"一次定义,到处运行"

Taihe 同时支持 ArkTS,C++ 等目标语言:

同一份接口定义,多套绑定方案。开发者可根据实际需求灵活选择技术栈,无需修改接口描述。

传统方案通常只能绑定到一种运行时,而 Taihe 在编译期即可同时生成多个目标语言的完整实现。以一次接口定义为例:

// 只需编写一次接口定义interface ICalculator {    add(a: i32, b: i32): i32;}

*可左右滑动查看

编译器会自动生成以下绑定:

C ABI 层——底层基础

C++ 投影层——为 C++ 开发者提供舒适的接口

NAPI 绑定——面向 ArkTS

编译产物中包含完整、独立的各语言实现,彼此解耦,运行时开销很低。Taihe 的本质并非"提供一个兼容层",而是"自动生成多种语言的绑定代码"。各语言直接使用自己的那份实现,无需额外的运行时适配。

二进制隔离

Taihe 清晰地划分了 API 提供者和消费者的角色。

作为 API 提供者 (Provider)

1.定义接口:在.ohidl 文件中用 IDL 定义数据结构和接口方法。

2.生成代码:运行 taihec 工具,并启用面向提供方的目标语言,生成用于导出 API 实现的宏和模板。

3.实现功能:在自己的原生代码中,包含生成的头文件,并实现 IDL 中定义的接口逻辑。

作为 API 消费者 (Consumer)

1.获取头文件/模块:在项目中包含或导入由 Taihe 生成的消费方语言投影文件(如 C++ 文件或 ArkTS 文件)。

2.编写代码:像使用普通的第三方库一样,直接创建和调用 IDL 中定义的类和函数。与 ABI 的交互由生成的代码自动完成。

Taihe 通过 C ABI 作为中间层,实现接口提供者和消费者的解耦,确保跨编译器、跨平台的二进制兼容性。

六、核心特性一览


强大的面向对象能力:完整支持接口的单继承与多重继承。通过精密的虚表(v-table)和运行时类型信息(RTTI)布局,实现了高效的静态转换和安全的动态转换。

自动内存管理:对于接口对象和大部分容器类型,采用引用计数进行生命周期管理,提供强、弱引用机制,简化内存管理。

丰富的容器与数据类型:提供值语义的 Array<T> 和 Optional<T>,以及引用计数的 String、Vector<T>、Map<K,V> 和 Set<T>。还支持函数闭包,满足多样化的数据处理需求。

灵活的注解系统:允许使用注解(如@readonly, @get, @promise)来精细地控制代码生成过程,例如将方法映射为属性的 getter/setter,或生成异步函数。

七、实际开发流程


让我们通过一个完整的计算器示例来看看使用 Taihe 的实际开发流程:

步骤 1:编写 IDL 接口定义

创建 calculator.ohidl 文件:

interface ICalculator {    add(a: i32, b: i32): i32;    subtract(a: i32, b: i32): i32;    multiply(a: i32, b: i32): i32;    divide(a: i32, b: i32): i32;    hello(name: String): String;}
function create(): ICalculator;

*可左右滑动查看

步骤 2:使用 taihec 编译 IDL 生成代码

taihec calculator.ohidl  -O  generated -Gnapi-bridge -Gcpp-author

*可左右滑动查看

生成代码目录:

步骤 3:实现 C++ 接口

在实现模板的基础上,补充具体的业务逻辑:

// calculator.impl.cppclass CalculatorImpl {public:    int32_t add(int32_t a, int32_t b) {        return a + b;    }        int32_t subtract(int32_t a, int32_t b) {        return a - b;    }        int32_t multiply(int32_t a, int32_t b) {        return a * b;    }        int32_t divide(int32_t a, int32_t b) {        if (b == 0) return 0;        return a / b;    }        ::taihe::string hello(::taihe::string_view name) {        return "Hello, " + name + "!";    }};
ICalculator create() {    return ::taihe::make_holder<CalculatorImpl, ICalculator>();}
// 导出接口TH_EXPORT_CPP_API_create(create);

*可左右滑动查看

步骤 4:在 ArkTS 中调用

// Index.etsimport testNapi from 'libentry.so';
@Entry@Componentstruct Index {  @State message: string = 'Hello World';    build() {    Row() {      Column() {        Text(this.message)          .fontSize($r('app.float.page_text_font_size'))          .fontWeight(FontWeight.Bold)          .onClick(() => {            hilog.info(DOMAIN, 'testTag', '---------------------start test-----------------------');            let calculator: testNapi.ICalculator = testNapi.create();            hilog.info(DOMAIN, 'testTag', 'Test calculator add 2 + 3 = %{public}d', calculator.add(2, 3));            hilog.info(DOMAIN, 'testTag', 'Test calculator subtract 2 - 3 = %{public}d', calculator.subtract(2, 3));            hilog.info(DOMAIN, 'testTag', 'Test calculator multiply 2 * 3 = %{public}d', calculator.multiply(2, 3));            hilog.info(DOMAIN, 'testTag', 'Test calculator divide 2 / 3 = %{public}d', calculator.divide(2, 3));            hilog.info(DOMAIN, 'testTag', 'Test calculator hello %{public}s', calculator.hello("Alice"));            hilog.info(DOMAIN, 'testTag', '---------------------end test-----------------------');          })      }      .width('100%')    }    .height('100%')  }}

*可左右滑动查看

应用运行结果:

八、权衡与考量


学习曲线:需要掌握 Taihe 的 IDL 语法及各目标语言特性。虽比手写绑定简单,但仍有一定的学习成本。

类型映射限制:并非所有 C++ 类型都能直接映射。复杂模板、指针、引用等类型需要额外处理。

错误处理开销:类型安全并非无代价。每一层绑定都伴随着类型转换与错误检查的运行时开销。

构建复杂度:集成到现有工程时,需额外配置 Taihe 编译器、运行时及构建模板。

但这些并非 Taihe 的问题,而是跨语言开发固有的复杂性。Taihe 已大幅降低了复杂性。

总结


Taihe 通过以下方式解决跨语言开发的核心痛点:

开发者只需关注业务逻辑,Taihe 负责处理跨语言细节。

在 HarmonyOS 生态日益完善的今天,Taihe 为开发者提供了一个可靠的跨语言解决方案。它不是简单的代码生成工具,而是面向未来的开发范式转变——从"如何实现"到"要实现什么"。

*广告往期回顾 | HarmonyOS

让 ArkTS 代码“可理解、可导航、可诊断”的 LSP 语言服务ArkTS 为啥比 TS 更好写ArkTS 极简开发| 被 ArkTS 注解惊艳到了:这才是该有的注解语法关注公众号,获取更多 HarmonyOS 技术内容                       

【扫码加入 HarmonyOS ArkTS技术交流群】

原文链接
多空观点 观点仅供交流,不构成投资建议
点赞
踩一踩
收藏
分享

评论 (0)

0/200
声明:社区内容仅代表用户个人观点,不构成任何投资建议。投资有风险,入市需谨慎。