第三方 C/C++ 库集成
Kotlin/Native 可集成任意 C/C++ 库。本文深入讲解常见第三方库的集成方法和最佳实践。
集成流程
完整流程图
1. 准备 C/C++ 库
├─ 获取头文件 (.h)
├─ 获取库文件 (.so/.a/.dll)
└─ 了解 API 文档
2. 创建定义文件 (.def)
├─ 指定头文件
├─ 设置编译选项
└─ 设置链接选项
3. 配置 Gradle 构建
├─ 添加 cinterop 配置
└─ 设置平台特定参数
4. 生成 Kotlin 绑定
└─ 运行编译,生成 klib
5. 在 Kotlin 中使用
└─ 导入并调用 API案例1:OpenSSL集成
定义文件
properties
# openssl.def
headers = openssl/ssl.h openssl/crypto.h openssl/sha.h
headerFilter = openssl/*.h
package = openssl
# macOS 配置
compilerOpts.osx = -I/usr/local/opt/openssl/include
linkerOpts.osx = -L/usr/local/opt/openssl/lib -lssl -lcrypto
# Linux 配置
compilerOpts.linux = -I/usr/include/openssl
linkerOpts.linux = -lssl -lcrypto -ldl -lpthread
# Windows 配置
compilerOpts.mingw = -IC:/OpenSSL/include
linkerOpts.mingw = -LC:/OpenSSL/lib -lssl -lcrypto -lws2_32Gradle 配置
kotlin
// build.gradle.kts
kotlin {
linuxX64 {
compilations.getByName("main") {
cinterops {
val openssl by creating {
defFile(project.file("src/nativeInterop/cinterop/openssl.def"))
packageName("openssl")
// 额外的头文件路径
includeDirs.apply {
allHeaders("/usr/include", "/usr/local/include")
}
}
}
}
}
}Kotlin 使用示例
kotlin
@OptIn(ExperimentalForeignApi::class)
import kotlinx.cinterop.*
import openssl.*
object CryptoUtils {
/**
* SHA256 哈希
*/
fun sha256(data: ByteArray): ByteArray {
memScoped {
val hash = allocArray<UByteVar>(SHA256_DIGEST_LENGTH)
data.usePinned { pinned ->
SHA256(
pinned.addressOf(0).reinterpret(),
data.size.toULong(),
hash
)
}
return ByteArray(SHA256_DIGEST_LENGTH) { i ->
hash[i].toByte()
}
}
}
/**
* AES 加密
*/
fun aesEncrypt(data: ByteArray, key: ByteArray, iv: ByteArray): ByteArray {
memScoped {
val ctx = alloc<EVP_CIPHER_CTX>()
EVP_CIPHER_CTX_init(ctx.ptr)
val output = ByteArray(data.size + 16) // 考虑padding
val outLen = alloc<IntVar>()
try {
// 初始化加密
EVP_EncryptInit_ex(
ctx.ptr,
EVP_aes_256_cbc(),
null,
key.toCValues(),
iv.toCValues()
)
// 加密数据
output.usePinned { pinnedOut ->
data.usePinned { pinnedIn ->
EVP_EncryptUpdate(
ctx.ptr,
pinnedOut.addressOf(0).reinterpret(),
outLen.ptr,
pinnedIn.addressOf(0).reinterpret(),
data.size
)
}
}
val written = outLen.value
// Finalize
EVP_EncryptFinal_ex(
ctx.ptr,
output.toCValues().ptr.plus(written)!!.reinterpret(),
outLen.ptr
)
return output.copyOf(written + outLen.value)
} finally {
EVP_CIPHER_CTX_cleanup(ctx.ptr)
}
}
}
}
// 使用
fun main() {
val data = "Hello, World!".encodeToByteArray()
val hash = CryptoUtils.sha256(data)
println("SHA256: ${hash.joinToString("") { "%02x".format(it) }}")
}案例2:SQLite 集成
定义文件
properties
# sqlite.def
headers = sqlite3.h
headerFilter = sqlite3.h
package = sqlite
linkerOpts.osx = -lsqlite3
linkerOpts.linux = -lsqlite3 -ldl -lpthread
linkerOpts.mingw = -lsqlite3数据库操作封装
kotlin
@OptIn(ExperimentalForeignApi::class)
import sqlite.*
class SQLiteDatabase(private val path: String) : AutoCloseable {
private var db: CPointer<sqlite3>? = null
init {
memScoped {
val dbPtr = allocPointerTo<sqlite3>()
val result = sqlite3_open(path.cstr.ptr, dbPtr.ptr)
if (result != SQLITE_OK) {
throw Exception("Failed to open database: $result")
}
db = dbPtr.value
}
}
fun execute(sql: String) {
val result = sqlite3_exec(
db,
sql.cstr.ptr,
null,
null,
null
)
if (result != SQLITE_OK) {
val error = sqlite3_errmsg(db)?.toKString()
throw Exception("SQL error: $error")
}
}
fun query(sql: String): List<Map<String, String>> {
val results = mutableListOf<Map<String, String>>()
memScoped {
val stmtPtr = allocPointerTo<sqlite3_stmt>()
sqlite3_prepare_v2(
db,
sql.cstr.ptr,
-1,
stmtPtr.ptr,
null
)
val stmt = stmtPtr.value
try {
val columnCount = sqlite3_column_count(stmt)
while (sqlite3_step(stmt) == SQLITE_ROW) {
val row = mutableMapOf<String, String>()
for (i in 0 until columnCount) {
val name = sqlite3_column_name(stmt, i)?.toKString() ?: "col$i"
val value = sqlite3_column_text(stmt, i)?.toKString() ?: ""
row[name] = value
}
results.add(row)
}
} finally {
sqlite3_finalize(stmt)
}
}
return results
}
override fun close() {
db?.let { sqlite3_close(it) }
db = null
}
}
// 使用
SQLiteDatabase("test.db").use { db ->
db.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)")
db.execute("INSERT INTO users (name) VALUES ('Alice')")
val users = db.query("SELECT * FROM users")
users.forEach { user ->
println("User: ${user["name"]}")
}
}案例3:libcurl 网络库
定义文件
properties
# curl.def
headers = curl/curl.h
headerFilter = curl/*.h
package = curl
compilerOpts.osx = -I/usr/local/opt/curl/include
linkerOpts.osx = -L/usr/local/opt/curl/lib -lcurl
compilerOpts.linux = -I/usr/include/curl
linkerOpts.linux = -lcurl
compilerOpts.mingw = -IC:/curl/include
linkerOpts.mingw = -LC:/curl/lib -lcurl -lws2_32HTTP 请求封装
kotlin
@OptIn(ExperimentalForeignApi::class)
import curl.*
object HttpClient {
init {
curl_global_init(CURL_GLOBAL_ALL)
}
private val writeCallback = staticCFunction<
CPointer<ByteVar>?,
size_t,
size_t,
COpaquePointer?,
size_t
> { ptr, size, nmemb, userdata ->
if (ptr != null && userdata != null) {
val totalSize = (size * nmemb).toInt()
val buffer = userdata.asStableRef<StringBuilder>().get()
val data = ByteArray(totalSize) { i ->
ptr[i]
}
buffer.append(data.decodeToString())
}
size * nmemb
}
fun get(url: String): String {
val curl = curl_easy_init() ?: throw Exception("Failed to init curl")
val response = StringBuilder()
val stableRef = StableRef.create(response)
try {
curl_easy_setopt(curl, CURLOPT_URL, url.cstr.ptr)
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback)
curl_easy_setopt(curl, CURLOPT_WRITEDATA, stableRef.asCPointer())
val res = curl_easy_perform(curl)
if (res != CURLE_OK) {
val error = curl_easy_strerror(res)?.toKString()
throw Exception("Curl error: $error")
}
return response.toString()
} finally {
curl_easy_cleanup(curl)
stableRef.dispose()
}
}
}
// 使用
val html = HttpClient.get("https://example.com")
println(html)常见问题与解决方案
问题1:找不到头文件
properties
# ❌ 错误配置
headers = mylib.h
# ✅ 正确配置
headers = mylib.h
compilerOpts = -I/usr/local/include -I/opt/mylib/include问题2:链接失败
bash
# 检查库文件是否存在
ls /usr/local/lib/libmylib.so
#检查符号是否存在
nm -D /usr/local/lib/libmylib.so | grep my_functionproperties
# 修复链接配置
linkerOpts = -L/usr/local/lib -lmylib -lpthread问题3:符号冲突
properties
# 使用 headerFilter 只导出需要的符号
headers = lib1.h lib2.h system.h
headerFilter = lib1.h # 只导出 lib1 的符号最佳实践
实践1:版本管理
properties
# 在 def 文件中记录版本
# Library: OpenSSL
# Version: 3.0.0
# Updated: 2024-01-01
headers = openssl/ssl.h实践2:错误处理
kotlin
@OptIn(ExperimentalForeignApi::class)
inline fun <T> withCLibrary(
errorCheck: () -> Boolean,
errorMessage: () -> String,
block: () -> T
): T {
val result = block()
if (errorCheck()) {
throw Exception(errorMessage())
}
return result
}实践3:内存安全
kotlin
@OptIn(ExperimentalForeignApi::class)
class SafeCPointer<T : CVariable>(private val ptr: CPointer<T>) : AutoCloseable {
fun use(): CPointer<T> = ptr
override fun close() {
nativeHeap.free(ptr)
}
}通过 cinterop,几乎所有 C/C++ 库都可无缝集成到 Kotlin/Native 项目中。关键是正确配置定义文件和处理好内存管理。