Skip to content

cinterop 工具详解Beta

源:Interoperability with C - Kotlin/Native

cinterop 是 Kotlin/Native 的核心工具,负责生成 C/Objective-C 库的 Kotlin 绑定。深入理解 cinterop 能够充分利用 Native 生态。

工作原理

处理流程

C 头文件 (.h)

定义文件 (.def)  ← 配置项

cinterop 工具

Kotlin 绑定 (.klib)

Kotlin 代码导入使用

生成的绑定

cinterop 为 C 声明生成对应的 Kotlin API:

C 声明Kotlin 绑定
函数顶层函数
结构体CPointed 子类
枚举Kotlin 枚举或常量
宏常量Kotlin 常量
typedef类型别名

def 文件配置

核心属性

properties
# mylib.def

# 要处理的头文件(空格分隔)
headers = mylib.h utils.h

# 过滤器:只导入匹配的头文件
headerFilter = mylib.h

# 生成的 Kotlin 包名
package = mylib

# 源语言(C 或 Objective-C)
language = C
properties
# 编译器选项
compilerOpts = -I/usr/include
compilerOpts.linux = -I/usr/include/x86_64-linux-gnu
compilerOpts.osx = -I/usr/local/include
compilerOpts.mingw = -IC:/msys64/mingw64/include

# 链接器选项
linkerOpts = -lm
linkerOpts.linux = -L/usr/lib -lmylib
linkerOpts.osx = -L/usr/local/lib -lmylib
linkerOpts.mingw = -LC:/msys64/mingw64/lib -lmylib

高级选项

properties
# mylib.def

# 排除特定函数
excludedFunctions = internal_helper deprecated_api

# 控制枚举生成
strictEnums = Color Mode
nonStrictEnums = ErrorCode

# 禁用字符串自动转换
noStringConversion = get_raw_buffer process_bytes

# 排除头文件
excludeFilter = internal/*.h test/*.h

# 静态库(实验性)
staticLibraries = libstatic.a
libraryPaths = /opt/libs

自定义声明

可以在 def 文件中直接添加 C 声明:

properties
# mylib.def
headers = mylib.h
package = mylib

---

// 自定义声明(在 --- 之后)
static inline int square(int x) {
    return x * x;
}

#define MY_CONSTANT 42

typedef struct {
    int x, y;
} CustomPoint;

Gradle 集成

基础配置

kotlin
// build.gradle.kts
kotlin {
    linuxX64 {
        compilations.getByName("main") {
            cinterops {
                val mylib by creating {
                    // 指定 def 文件
                    defFile(project.file("src/nativeInterop/cinterop/mylib.def"))
                    
                    // 包名
                    packageName("mylib")
                    
                    // 额外的头文件搜索路径
                    includeDirs.apply {
                        allHeaders("/usr/include", "/usr/local/include")
                    }
                }
            }
        }
    }
}
groovy
// build.gradle
kotlin {
    linuxX64 {
        compilations.main {
            cinterops {
                mylib {
                    defFile file('src/nativeInterop/cinterop/mylib.def')
                    packageName 'mylib'
                    includeDirs {
                        allHeaders '/usr/include', '/usr/local/include'
                    }
                }
            }
        }
    }
}

多平台配置

kotlin
// build.gradle.kts
kotlin {
    // 定义多个目标
    val nativeTargets = listOf(
        linuxX64(),
        linuxArm64(),
        macosX64(),
        macosArm64(),
        mingwX64()
    )
    
    nativeTargets.forEach { target ->
        target.compilations.getByName("main") {
            cinterops {
                val openssl by creating {
                    defFile("src/nativeInterop/cinterop/openssl.def")
                    packageName("openssl")
                    
                    // 平台特定的头文件路径
                    when (target.name) {
                        "linuxX64", "linuxArm64" -> {
                            includeDirs("/usr/include/openssl")
                        }
                        "macosX64", "macosArm64" -> {
                            includeDirs("/usr/local/opt/openssl/include")
                        }
                        "mingw X64" -> {
                            includeDirs("C:/OpenSSL/include")
                        }
                    }
                }
            }
        }
    }
}

类型映射细节

基本类型

cinterop 自动映射 C 基本类型:

kotlin
// C → Kotlin
// char → Byte
// unsigned char → UByte
// short → Short
// unsigned short → UShort
// int → Int
// unsigned int → UInt
// long → Long (平台相关)
// unsigned long → ULong
// long long → Long
// unsigned long long → ULong
// float → Float
// double → Double
// void* → COpaquePointer?

函数映射

c
// mylib.h
int add(int a, int b);
void process(const char* data, size_t len);
char* get_string();
kotlin
@OptIn(ExperimentalForeignApi::class)
import mylib.*

// int add(int, int)
fun useAdd() {
    val result = add(10, 20)  // 直接调用
    println(result)  // 30
}

// void process(const char*, size_t)
fun useProcess() {
    "Hello".cstr.use { ptr ->
        process(ptr, 5u)
    }
}

// char* get_string()
fun useGetString() {
    val str = get_string()?.toKString()
    println(str)
}

结构体映射

c
// C 定义
typedef struct {
    int width;
    int height;
    unsigned char* data;
} Image;

Image* create_image(int w, int h);
void destroy_image(Image* img);
kotlin
@OptIn(ExperimentalForeignApi::class)
import kotlinx.cinterop.*
import mylib.*

fun useImage() {
    val img = create_image(640, 480)
    
    if (img != null) {
        // 访问结构体字段
        println("Size: ${img.pointed.width}x${img.pointed.height}")
        
        // 修改字段
        img.pointed.data = nativeHeap.allocArray<UByteVar>(640 * 480).reinterpret()
        
        destroy_image(img)
    }
}

处理复杂场景

回调函数

c
// C 定义
typedef void (*Callback)(int value, void* userdata);
void register_callback(Callback cb, void* userdata);
kotlin
@OptIn(ExperimentalForeignApi::class)
import kotlin.native.ref.*

val callback = staticCFunction<Int, COpaquePointer?, Unit> { value, userdata ->
    if (userdata != null) {
        val handler = userdata.asStableRef<(Int) -> Unit>().get()
        handler(value)
    }
}

fun setupCallback(handler: (Int) -> Unit) {
    val ref = StableRef.create(handler)
    register_callback(callback, ref.asCPointer())
}

可变参数函数

c
// C 定义
int printf(const char* format, ...);
kotlin
// cinterop 不生成可变参数函数的绑定
// 需要手动包装固定参数版本

// 在 def 文件中添加:
// ---
// static inline int my_printf_int(const char* fmt, int value) {
//     return printf(fmt, value);
// }

宏处理

properties
# mylib.def
---

// 常量宏会自动转换
#define MAX_SIZE 1024

// 函数宏需要转换为 inline 函数
#define SQUARE(x) ((x) * (x))

static inline int square_wrapper(int x) {
    return SQUARE(x);
}

调试与优化

查看生成的绑定

bash
# 构建后查看生成的 klib
./gradlew linkDebugSharedLinuxX64

# klib 位置
build/classes/kotlin/linuxX64/main/klib/mylib.klib

# 解压查看
unzip mylib.klib -d mylib_contents

启用详细日志

kotlin
// build.gradle.kts
kotlin {
    targets.withType<KotlinNativeTarget> {
        compilations.getByName("main") {
            cinterops {
                val mylib由 creating {
                    defFile("mylib.def")
                    
                    // 启用详细输出
                    extraOpts("-verbose")
                }
            }
        }
    }
}

常见问题排查

properties
# ❌ 错误
headers = mylib.h
# 错误:mylib.h: No such file or directory

# ✅ 解决
headers = mylib.h
compilerOpts = -I/path/to/headers
properties
# ❌ 错误
headers = mylib.h
# 链接时:undefined reference to `my_function`

# ✅ 解决
headers = mylib.h
linkerOpts = -L/path/to/lib -lmylib
properties
# ❌ 错误:与系统头文件冲突

# ✅ 解决:使用 headerFilter
headers = mylib.h system.h
headerFilter = mylib.h  # 只导入 mylib.h 的符号

最佳实践

实践:分层定义文件

src/nativeInterop/cinterop/
├── base.def          # 基础库
├── graphics.def      # 图形相关
├── network.def       # 网络相关
└── platform_linux.def # 平台特定

实践:版本管理

properties
# mylib.def
# Library: MyLib
# Version: 2.1.0
# Last updated: 2024-01-01
# Source: https://github.com/example/mylib

headers = mylib.h
package = mylib

实践:封装 C API

kotlin
@OptIn(ExperimentalForeignApi::class)
// 不要直接暴露 C API
class ImageProcessor {
    private val nativeImage: CPointer<Image>?
    
    init {
        nativeImage = create_image(640, 480)
    }
    
    fun process() {
        // 封装 C调用
    }
    
    fun close() {
        nativeImage?.let { destroy_image(it) }
    }
}

cinterop 是连接 Kotlin 与 C 生态的桥梁。合理配置 def 文件、理解类型映射规则,能够高效利用海量 C/C++ 库。