Skip to content

C++ 互操作实战

源:Interoperability with 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
}
#endif
cpp
// 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 边界,保持资源管理的一致性。