C++ 互操作实战
Kotlin/Native 对 C++ 的支持需要通过 C 兼容层实现。本文详解如何与 C++ 代码互操作,包括类、模板、STL 等。
##C++ 互操作基础
核心限制重要
cinterop 工具不直接支持 C++ 特性。需要创建 C 兼容的包装层:
cpp
// mylib.hpp - C++ 代码
namespace MyNamespace {
class Calculator {
private:
int value;
public:
Calculator(int initial) : value(initial) {}
int add(int x) { return value += x; }
int getValue() const { return value; }
};
}cpp
// mylib_wrapper.h - C 包装层
#ifdef __cplusplus
extern "C" {
#endif
typedef void* CalculatorHandle;
CalculatorHandle Calculator_create(int initial);
int Calculator_add(CalculatorHandle handle, int x);
int Calculator_getValue(CalculatorHandle handle);
void Calculator_destroy(CalculatorHandle handle);
#ifdef __cplusplus
}
#endifcpp
// mylib_wrapper.cpp - 包装实现
#include "mylib.hpp"
#include "mylib_wrapper.h"
extern "C" {
CalculatorHandle Calculator_create(int initial) {
return new MyNamespace::Calculator(initial);
}
int Calculator_add(CalculatorHandle handle, int x) {
auto* calc = static_cast<MyNamespace::Calculator*>(handle);
return calc->add(x);
}
int Calculator_getValue(CalculatorHandle handle) {
auto* calc = static_cast<MyNamespace::Calculator*>(handle);
return calc->getValue();
}
void Calculator_destroy(CalculatorHandle handle) {
delete static_cast<MyNamespace::Calculator*>(handle);
}
}定义文件配置
properties
# mylib.def
headers=mylib_wrapper.h
headerFilter=mylib_wrapper.h
package=mylib
# C++ 编译器选项
compilerOpts.linux=-I/usr/include -std=c++17
compilerOpts.osx=-I/usr/local/include -std=c++17
# 链接 C++ 标准库
linkerOpts.linux=-L. -lmylib -lstdc++
linkerOpts.osx=-L. -lmylib -lc++Kotlin 使用
kotlin
@OptIn(ExperimentalForeignApi::class)
import kotlinx . cinterop . *
import mylib . *
class Calculator(initial: Int) : AutoCloseable {
private val handle: CalculatorHandle? = Calculator_create(initial)
fun add(x: Int): Int {
return Calculator_add(handle, x)
}
fun getValue(): Int {
return Calculator_getValue(handle)
}
override fun close() {
Calculator_destroy(handle)
}
}
// 使用
Calculator(10).use { calc ->
calc.add(5)
calc.add(3)
println(calc.getValue()) // 18
}STL 容器包装
std::vector 包装
cpp
// vector_wrapper.h
#ifdef __cplusplus
extern "C" {
#endif
typedef void* VectorHandle;
VectorHandle Vector_create();
void Vector_push_back(VectorHandle handle, int value);
int Vector_get(VectorHandle handle, int index);
int Vector_size(VectorHandle handle);
void Vector_destroy(VectorHandle handle);
#ifdef __cplusplus
}
#endif
// vector_wrapper.cpp
#include <vector>
#include "vector_wrapper.h"
extern "C" {
VectorHandle Vector_create() {
return new std::vector<int>();
}
void Vector_push_back(VectorHandle handle, int value) {
auto* vec = static_cast<std::vector<int>*>(handle);
vec->push_back(value);
}
int Vector_get(VectorHandle handle, int index) {
auto* vec = static_cast<std::vector<int>*>(handle);
return (*vec)[index];
}
int Vector_size(VectorHandle handle) {
auto* vec = static_cast<std::vector<int>*>(handle);
return static_cast<int>(vec->size());
}
void Vector_destroy(VectorHandle handle) {
delete static_cast<std::vector<int>*>(handle);
}
}kotlin
@OptIn(ExperimentalForeignApi::class)
class IntVector : AutoCloseable {
private val handle: VectorHandle? = Vector_create()
fun add(value: Int) {
Vector_push_back(handle, value)
}
operator fun get(index: Int): Int {
return Vector_get(handle, index)
}
val size: Int
get() = Vector_size(handle)
override fun close() {
Vector_destroy(handle)
}
}
// 使用
IntVector().use { vec ->
vec.add(10)
vec.add(20)
vec.add(30)
for (i in 0 until vec.size) {
println(vec[i])
}
}std::string 包装
cpp
// string_wrapper.h
typedef void* StringHandle;
StringHandle String_create(const char* cstr);
const char* String_c_str(StringHandle handle);
int String_length(StringHandle handle);
void String_append(StringHandle handle, const char* str);
void String_destroy(StringHandle handle);
// string_wrapper.cpp
#include <string>
#include "string_wrapper.h"
extern "C" {
StringHandle String_create(const char* cstr) {
return new std::string(cstr ? cstr : "");
}
const char* String_c_str(StringHandle handle) {
return static_cast<std::string*>(handle)->c_str();
}
int String_length(StringHandle handle) {
return static_cast<int>(static_cast<std::string*>(handle)->length());
}
void String_append(StringHandle handle, const char* str) {
*static_cast<std::string*>(handle) += str;
}
void String_destroy(StringHandle handle) {
delete static_cast<std::string*>(handle);
}
}kotlin
@OptIn(ExperimentalForeignApi::class)
class CppString(initial: String = "") : AutoCloseable {
private val handle: StringHandle? = String_create(initial.cstr.ptr)
fun append(str: String) {
String_append(handle, str.cstr.ptr)
}
override fun toString(): String {
return String_c_str(handle)?.toKString() ?: ""
}
val length: Int
get() = String_length(handle)
override fun close() {
String_destroy(handle)
}
}模板特化
泛型容器包装
cpp
// 为特定类型实例化模板
template<typename T> class Container { /*...*/ };
// 显式实例化并导出
extern "C" {
// Int 版本
typedef void* IntContainerHandle;
IntContainerHandle IntContainer_create() {
return new Container<int>();
}
// String 版本
typedef void* StringContainerHandle;
StringContainerHandle StringContainer_create() {
return new Container<std::string>();
}
}异常处理
C++ 异常到 Kotlin
cpp
// error_handling.h
typedef struct {
int error_code;
char message[256];
} ErrorInfo;
int safe_operation(int value, ErrorInfo* error);
// error_handling.cpp
extern "C" {
int safe_operation(int value, ErrorInfo* error) {
try {
if (value < 0) {
throw std::invalid_argument("Value must be non-negative");
}
// 执行操作
return value * 2;
} catch (const std::exception& e) {
if (error) {
error->error_code = -1;
strncpy(error->message, e.what(), 255);
error->message[255] = '\0';
}
return -1;
}
}
}kotlin
@OptIn(ExperimentalForeignApi::class)
fun safeOperation(value: Int): Int {
memScoped {
val error = alloc<ErrorInfo>()
val result = safe_operation(value, error.ptr)
if (error.error_code != 0) {
throw Exception(error.message.toKString())
}
return result
}
}命名空间处理
命名空间扁平化
cpp
// C++ 命名空间
namespace Math {
namespace Advanced {
double calculate(double x);
}
}
// C 包装:将命名空间编码到函数名
extern "C" {
double Math_Advanced_calculate(double x) {
return Math::Advanced::calculate(x);
}
}构建配置
CMakeLists.txt
cmake
cmake_minimum_required(VERSION 3.18)
project(MyCppLib CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# C++ 源文件
add_library(mycpplib SHARED
mylib.cpp
mylib_wrapper.cpp
vector_wrapper.cpp
string_wrapper.cpp
)
# 导出符号
set_target_properties(mycpplib PROPERTIES
CXX_VISIBILITY_PRESET default
VISIBILITY_INLINES_HIDDEN OFF
)
# 链接 C++ 标准库
target_link_libraries(mycpplib
stdc++
)Gradle 集成
kotlin
// build.gradle.kts
kotlin {
linuxX64 {
compilations.getByName("main") {
cinterops {
val mycpplib by creating {
defFile("src/nativeInterop/cinterop/mycpplib.def")
packageName("mycpplib")
// 指定预编译的 C++ 库
extraOpts("-libraryPath", "$projectDir/libs")
}
}
}
binaries.all {
linkerOpts("-L$projectDir/libs", "-lmycpplib")
}
}
}最佳实践
实践:使用智能指针
cpp
// 使用 unique_ptr 管理生命周期
extern "C" {
CalculatorHandle Calculator_create(int initial) {
return new std::unique_ptr<Calculator>(
std::make_unique<Calculator>(initial)
);
}
void Calculator_destroy(CalculatorHandle handle) {
delete static_cast<std::unique_ptr<Calculator>*>(handle);
}
}实践:类型安全包装
kotlin
@OptIn(ExperimentalForeignApi::class)
@JvmInline
value class calculation(val handle: CalculatorHandle?) {
// 类型安全的句柄
}
fun createCalculator(initial: Int): CalculatorHandle {
return CalculatorHandle(Calculator_create(initial))
}通过 C 包装层,Kotlin/Native 可以与复杂的 C++ 代码库互操作。关键是设计清晰的 C API 边界,保持资源管理的一致性。