仓颉FFI实战:C/C++互操作与性能优化

目录

摘要

一、FFI原理与架构

[1.1 什么是FFI](#1.1 什么是FFI)

[1.2 仓颉FFI架构设计](#1.2 仓颉FFI架构设计)

[1.3 安全边界设计](#1.3 安全边界设计)

二、类型映射与转换

[2.1 基本类型映射表](#2.1 基本类型映射表)

[2.2 基本类型转换示例](#2.2 基本类型转换示例)

[2.3 字符串类型转换](#2.3 字符串类型转换)

[2.4 结构体类型映射](#2.4 结构体类型映射)

[2.5 数组和指针转换](#2.5 数组和指针转换)

三、C库集成实战

[3.1 集成SQLite数据库](#3.1 集成SQLite数据库)

[3.2 集成JSON解析库(cJSON)](#3.2 集成JSON解析库(cJSON))

四、性能基准测试

[4.1 基准测试框架](#4.1 基准测试框架)

[4.2 FFI调用开销分析](#4.2 FFI调用开销分析)

五、内存管理最佳实践

[5.1 RAII模式实现](#5.1 RAII模式实现)

[5.2 智能指针实现](#5.2 智能指针实现)

[5.3 内存泄漏检测](#5.3 内存泄漏检测)

六、高级FFI技巧

[6.1 回调函数处理](#6.1 回调函数处理)

[6.2 可变参数函数](#6.2 可变参数函数)

[6.3 函数指针表](#6.3 函数指针表)

七、实战案例:图像处理库集成

[7.1 集成libpng](#7.1 集成libpng)

八、总结与最佳实践

[8.1 FFI使用原则](#8.1 FFI使用原则)

[8.2 性能优化清单](#8.2 性能优化清单)

[8.3 常见陷阱](#8.3 常见陷阱)

九、总结

核心知识点回顾

讨论问题

学习建议

参考资源


摘要

Foreign Function Interface (FFI) 是连接不同编程语言的桥梁,使开发者能够复用现有的C/C++库生态。仓颉语言提供了强大而安全的FFI机制,支持与C/C++代码的无缝互操作。本文深入探讨仓颉FFI的实现原理、类型映射规则、内存管理策略以及性能优化技巧,通过丰富的实战案例帮助开发者构建高性能的混合语言应用,充分利用HarmonyOS原生能力和第三方库生态。


一、FFI原理与架构

1.1 什么是FFI

FFI的核心作用:

功能 说明 优势
代码复用 调用现有C/C++库 避免重复开发
性能优化 关键路径使用原生代码 突破语言性能瓶颈
系统集成 访问操作系统API 实现底层功能
生态互通 利用成熟库生态 加速开发进度

1.2 仓颉FFI架构设计

复制代码
// ========== FFI模块组织结构 ==========
/*
project/
├── src/
│   └── main.cj              # 仓颉主代码
├── native/
│   ├── include/
│   │   └── mylib.h          # C头文件
│   └── src/
│       └── mylib.c          # C实现
├── ffi/
│   └── bindings.cj          # FFI绑定
└── build.cj                 # 构建配置
*/

FFI调用流程:

1.3 安全边界设计

复制代码
// ========== FFI安全检查机制 ==========
import std.ffi.*

// 不安全的FFI调用需要显式标记
@unsafe
extern func c_malloc(size: UInt64): RawPointer

@unsafe
extern func c_free(ptr: RawPointer)

// 安全的包装函数
func safeAllocate(size: UInt64): Result<ManagedPointer, Error> {
    if size == 0 {
        return Err(Error("Invalid allocation size"))
    }
    
    if size > MAX_ALLOCATION_SIZE {
        return Err(Error("Allocation size too large"))
    }
    
    // 在unsafe块中调用
    let rawPtr = unsafe {
        c_malloc(size)
    }
    
    if rawPtr.isNull() {
        return Err(Error("Allocation failed"))
    }
    
    // 包装为托管指针
    return Ok(ManagedPointer(rawPtr, size))
}

安全边界层级:


二、类型映射与转换

2.1 基本类型映射表

复制代码
// ========== 仓颉 ⟷ C 类型对应关系 ==========

// 整数类型
/*
┌─────────────┬─────────────┬──────────┐
│ 仓颉类型    │ C类型       │ 大小     │
├─────────────┼─────────────┼──────────┤
│ Int8        │ int8_t      │ 1字节    │
│ UInt8       │ uint8_t     │ 1字节    │
│ Int16       │ int16_t     │ 2字节    │
│ UInt16      │ uint16_t    │ 2字节    │
│ Int32       │ int32_t     │ 4字节    │
│ UInt32      │ uint32_t    │ 4字节    │
│ Int64       │ int64_t     │ 8字节    │
│ UInt64      │ uint64_t    │ 8字节    │
└─────────────┴─────────────┴──────────┘
*/

// 浮点类型
/*
┌─────────────┬─────────────┬──────────┐
│ Float32     │ float       │ 4字节    │
│ Float64     │ double      │ 8字节    │
└─────────────┴─────────────┴──────────┘
*/

// 布尔类型
/*
┌─────────────┬─────────────┬──────────┐
│ Bool        │ bool/_Bool  │ 1字节    │
└─────────────┴─────────────┴──────────┘
*/

// 指针类型
/*
┌─────────────┬─────────────┬──────────┐
│ RawPointer  │ void*       │ 8字节    │
│ &T          │ T*          │ 8字节    │
│ &mut T      │ T*          │ 8字节    │
└─────────────┴─────────────┴──────────┘
*/

2.2 基本类型转换示例

复制代码
import std.ffi.*

// ========== C库函数声明 ==========
// C代码: int add(int a, int b);
@extern("C")
extern func c_add(a: Int32, b: Int32): Int32

// C代码: double sqrt(double x);
@extern("C", name: "sqrt")
extern func c_sqrt(x: Float64): Float64

// C代码: void* malloc(size_t size);
@extern("C")
@unsafe
extern func c_malloc(size: UInt64): RawPointer

// ========== 仓颉包装函数 ==========
func add(a: Int32, b: Int32): Int32 {
    return c_add(a, b)
}

func squareRoot(x: Float64): Result<Float64, Error> {
    if x < 0.0 {
        return Err(Error("Cannot compute square root of negative number"))
    }
    return Ok(c_sqrt(x))
}

func testBasicTypes() {
    // 整数运算
    let sum = add(10, 20)
    println("10 + 20 = ${sum}")  // 30
    
    // 浮点运算
    match squareRoot(16.0) {
        case Ok(result) => println("sqrt(16) = ${result}")  // 4.0
        case Err(e) => println("Error: ${e.message}")
    }
}

2.3 字符串类型转换

复制代码
import std.ffi.*

// ========== C字符串函数 ==========
// C代码: size_t strlen(const char* str);
@extern("C")
extern func c_strlen(str: CString): UInt64

// C代码: char* strcpy(char* dest, const char* src);
@extern("C")
@unsafe
extern func c_strcpy(dest: CString, src: CString): CString

// C代码: int strcmp(const char* s1, const char* s2);
@extern("C")
extern func c_strcmp(s1: CString, s2: CString): Int32

// ========== 仓颉 ⟷ C字符串转换 ==========
func stringLength(str: String): UInt64 {
    // 将仓颉String转换为C字符串
    let cStr = str.toCString()
    let length = c_strlen(cStr)
    
    // C字符串自动释放
    return length
}

func copyString(source: String): String {
    let srcCStr = source.toCString()
    
    unsafe {
        // 分配目标缓冲区
        let destPtr = c_malloc((source.length() + 1) as UInt64)
        let destCStr = CString.fromRawPointer(destPtr)
        
        // 复制字符串
        c_strcpy(destCStr, srcCStr)
        
        // 转换回仓颉String
        let result = destCStr.toString()
        
        // 释放内存
        c_free(destPtr)
        
        return result
    }
}

func compareStrings(s1: String, s2: String): Int32 {
    let cStr1 = s1.toCString()
    let cStr2 = s2.toCString()
    return c_strcmp(cStr1, cStr2)
}

func testStrings() {
    let text = "Hello, FFI!"
    println("Length: ${stringLength(text)}")  // 11
    
    let copied = copyString(text)
    println("Copied: ${copied}")
    
    let cmp = compareStrings("abc", "def")
    println("Compare result: ${cmp}")  // 负数
}

字符串转换流程:

2.4 结构体类型映射

复制代码
import std.ffi.*

// ========== C结构体定义 ==========
/*
// C代码
typedef struct {
    int x;
    int y;
} Point;

typedef struct {
    char name[64];
    int age;
    double salary;
} Person;
*/

// ========== 仓颉结构体映射 ==========
@repr(C)  // 使用C内存布局
struct Point {
    x: Int32
    y: Int32
}

@repr(C)
struct Person {
    name: Array<UInt8, 64>  // 固定大小数组
    age: Int32
    salary: Float64
}

// ========== C函数声明 ==========
@extern("C")
extern func distance(p1: &Point, p2: &Point): Float64

@extern("C")
extern func print_person(person: &Person)

// ========== 使用示例 ==========
func testStructs() {
    let p1 = Point(x: 0, y: 0)
    let p2 = Point(x: 3, y: 4)
    
    let dist = distance(&p1, &p2)
    println("Distance: ${dist}")  // 5.0
    
    // 创建Person结构
    var person = Person(
        name: Array::zero(),  // 初始化为0
        age: 30,
        salary: 50000.0
    )
    
    // 填充name字段
    let nameStr = "Alice"
    for i in 0..<nameStr.length() {
        person.name[i] = nameStr[i] as UInt8
    }
    
    print_person(&person)
}

结构体内存布局 :

2.5 数组和指针转换

复制代码
import std.ffi.*

// ========== C数组函数 ==========
// C代码: int sum_array(int* arr, size_t len);
@extern("C")
extern func c_sum_array(arr: RawPointer, len: UInt64): Int32

// C代码: void fill_array(int* arr, size_t len, int value);
@extern("C")
@unsafe
extern func c_fill_array(arr: RawPointer, len: UInt64, value: Int32)

// ========== 仓颉包装函数 ==========
func sumArray(arr: &Array<Int32>): Int32 {
    // 获取数组的原始指针
    let ptr = arr.asRawPointer()
    let len = arr.length() as UInt64
    
    return c_sum_array(ptr, len)
}

func fillArray(arr: &mut Array<Int32>, value: Int32) {
    unsafe {
        let ptr = arr.asRawPointer()
        let len = arr.length() as UInt64
        c_fill_array(ptr, len, value)
    }
}

func testArrays() {
    let numbers = Array<Int32>([1, 2, 3, 4, 5])
    let total = sumArray(&numbers)
    println("Sum: ${total}")  // 15
    
    var buffer = Array<Int32>::withCapacity(10)
    fillArray(&mut buffer, 42)
    println("Buffer: ${buffer}")  // [42, 42, ..., 42]
}

// ========== 动态分配数组 ==========
func allocateIntArray(size: UInt64): Result<ManagedArray<Int32>, Error> {
    if size == 0 {
        return Err(Error("Invalid size"))
    }
    
    unsafe {
        let byteSize = size * sizeof<Int32>()
        let ptr = c_malloc(byteSize)
        
        if ptr.isNull() {
            return Err(Error("Allocation failed"))
        }
        
        // 包装为托管数组
        let array = ManagedArray<Int32>::fromRawPointer(ptr, size)
        return Ok(array)
    }
}

// ========== 托管数组包装 ==========
class ManagedArray<T> {
    private ptr: RawPointer
    private length: UInt64
    
    private init(ptr: RawPointer, length: UInt64) {
        this.ptr = ptr
        this.length = length
    }
    
    public static func fromRawPointer(ptr: RawPointer, length: UInt64): ManagedArray<T> {
        return ManagedArray<T>(ptr, length)
    }
    
    public func get(index: UInt64): T {
        if index >= this.length {
            throw IndexOutOfBoundsError()
        }
        
        unsafe {
            let elementPtr = this.ptr.offset(index * sizeof<T>())
            return elementPtr.read<T>()
        }
    }
    
    public func set(index: UInt64, value: T) {
        if index >= this.length {
            throw IndexOutOfBoundsError()
        }
        
        unsafe {
            let elementPtr = this.ptr.offset(index * sizeof<T>())
            elementPtr.write<T>(value)
        }
    }
    
    // 析构函数:自动释放内存
    public func finalize() {
        unsafe {
            c_free(this.ptr)
        }
    }
}

三、C库集成实战

3.1 集成SQLite数据库

复制代码
import std.ffi.*

// ========== SQLite类型定义 ==========
type SqlitePtr = RawPointer
type SqliteStmtPtr = RawPointer

// ========== SQLite函数绑定 ==========
@extern("C", library: "sqlite3")
extern func sqlite3_open(
    filename: CString,
    ppDb: &SqlitePtr
): Int32

@extern("C", library: "sqlite3")
extern func sqlite3_close(db: SqlitePtr): Int32

@extern("C", library: "sqlite3")
extern func sqlite3_prepare_v2(
    db: SqlitePtr,
    sql: CString,
    nByte: Int32,
    ppStmt: &SqliteStmtPtr,
    pzTail: &CString
): Int32

@extern("C", library: "sqlite3")
extern func sqlite3_step(stmt: SqliteStmtPtr): Int32

@extern("C", library: "sqlite3")
extern func sqlite3_finalize(stmt: SqliteStmtPtr): Int32

@extern("C", library: "sqlite3")
extern func sqlite3_column_int(stmt: SqliteStmtPtr, iCol: Int32): Int32

@extern("C", library: "sqlite3")
extern func sqlite3_column_text(stmt: SqliteStmtPtr, iCol: Int32): CString

@extern("C", library: "sqlite3")
extern func sqlite3_errmsg(db: SqlitePtr): CString

// ========== 常量定义 ==========
let SQLITE_OK = 0
let SQLITE_ROW = 100
let SQLITE_DONE = 101

// ========== SQLite包装类 ==========
class SqliteDatabase {
    private db: SqlitePtr
    
    private init(db: SqlitePtr) {
        this.db = db
    }
    
    public static func open(path: String): Result<SqliteDatabase, Error> {
        var dbPtr: SqlitePtr = RawPointer.null()
        let cPath = path.toCString()
        
        let result = sqlite3_open(cPath, &dbPtr)
        
        if result != SQLITE_OK {
            return Err(Error("Failed to open database: ${path}"))
        }
        
        return Ok(SqliteDatabase(dbPtr))
    }
    
    public func execute(sql: String): Result<Unit, Error> {
        var stmtPtr: SqliteStmtPtr = RawPointer.null()
        var tail: CString = CString.null()
        let cSql = sql.toCString()
        
        let prepareResult = sqlite3_prepare_v2(
            this.db,
            cSql,
            -1,
            &stmtPtr,
            &tail
        )
        
        if prepareResult != SQLITE_OK {
            let errMsg = sqlite3_errmsg(this.db).toString()
            return Err(Error("Prepare failed: ${errMsg}"))
        }
        
        let stepResult = sqlite3_step(stmtPtr)
        sqlite3_finalize(stmtPtr)
        
        if stepResult != SQLITE_DONE && stepResult != SQLITE_ROW {
            let errMsg = sqlite3_errmsg(this.db).toString()
            return Err(Error("Execute failed: ${errMsg}"))
        }
        
        return Ok(())
    }
    
    public func query(sql: String): Result<ArrayList<Row>, Error> {
        var stmtPtr: SqliteStmtPtr = RawPointer.null()
        var tail: CString = CString.null()
        let cSql = sql.toCString()
        
        let prepareResult = sqlite3_prepare_v2(
            this.db,
            cSql,
            -1,
            &stmtPtr,
            &tail
        )
        
        if prepareResult != SQLITE_OK {
            let errMsg = sqlite3_errmsg(this.db).toString()
            return Err(Error("Prepare failed: ${errMsg}"))
        }
        
        let rows = ArrayList<Row>()
        
        while true {
            let stepResult = sqlite3_step(stmtPtr)
            
            if stepResult == SQLITE_ROW {
                let row = Row()
                // 简化:假设两列 (id, name)
                row.id = sqlite3_column_int(stmtPtr, 0)
                row.name = sqlite3_column_text(stmtPtr, 1).toString()
                rows.add(row)
            } else if stepResult == SQLITE_DONE {
                break
            } else {
                sqlite3_finalize(stmtPtr)
                let errMsg = sqlite3_errmsg(this.db).toString()
                return Err(Error("Query failed: ${errMsg}"))
            }
        }
        
        sqlite3_finalize(stmtPtr)
        return Ok(rows)
    }
    
    public func close() {
        if !this.db.isNull() {
            sqlite3_close(this.db)
            this.db = RawPointer.null()
        }
    }
    
    public func finalize() {
        this.close()
    }
}

struct Row {
    id: Int32
    name: String
}

// ========== 使用示例 ==========
func testSqlite() {
    match SqliteDatabase.open("test.db") {
        case Ok(db) => {
            // 创建表
            _ = db.execute("""
                CREATE TABLE IF NOT EXISTS users (
                    id INTEGER PRIMARY KEY,
                    name TEXT NOT NULL
                )
            """)
            
            // 插入数据
            _ = db.execute("INSERT INTO users (name) VALUES ('Alice')")
            _ = db.execute("INSERT INTO users (name) VALUES ('Bob')")
            
            // 查询数据
            match db.query("SELECT id, name FROM users") {
                case Ok(rows) => {
                    for row in rows {
                        println("User ${row.id}: ${row.name}")
                    }
                }
                case Err(e) => println("Query error: ${e.message}")
            }
            
            db.close()
        }
        case Err(e) => println("Open error: ${e.message}")
    }
}

3.2 集成JSON解析库(cJSON)

复制代码
import std.ffi.*

// ========== cJSON类型定义 ==========
type CJsonPtr = RawPointer

// ========== cJSON函数绑定 ==========
@extern("C", library: "cjson")
extern func cJSON_Parse(value: CString): CJsonPtr

@extern("C", library: "cjson")
extern func cJSON_Delete(item: CJsonPtr)

@extern("C", library: "cjson")
extern func cJSON_Print(item: CJsonPtr): CString

@extern("C", library: "cjson")
extern func cJSON_GetObjectItem(object: CJsonPtr, string: CString): CJsonPtr

@extern("C", library: "cjson")
extern func cJSON_IsString(item: CJsonPtr): Bool

@extern("C", library: "cjson")
extern func cJSON_IsNumber(item: CJsonPtr): Bool

@extern("C", library: "cjson")
extern func cJSON_GetStringValue(item: CJsonPtr): CString

@extern("C", library: "cjson")
extern func cJSON_GetNumberValue(item: CJsonPtr): Float64

// ========== JSON包装类 ==========
class JsonValue {
    private ptr: CJsonPtr
    private owned: Bool
    
    private init(ptr: CJsonPtr, owned: Bool) {
        this.ptr = ptr
        this.owned = owned
    }
    
    public static func parse(json: String): Result<JsonValue, Error> {
        let cStr = json.toCString()
        let ptr = cJSON_Parse(cStr)
        
        if ptr.isNull() {
            return Err(Error("Invalid JSON"))
        }
        
        return Ok(JsonValue(ptr, owned: true))
    }
    
    public func getString(key: String): Option<String> {
        let cKey = key.toCString()
        let item = cJSON_GetObjectItem(this.ptr, cKey)
        
        if item.isNull() || !cJSON_IsString(item) {
            return None
        }
        
        let value = cJSON_GetStringValue(item).toString()
        return Some(value)
    }
    
    public func getNumber(key: String): Option<Float64> {
        let cKey = key.toCString()
        let item = cJSON_GetObjectItem(this.ptr, cKey)
        
        if item.isNull() || !cJSON_IsNumber(item) {
            return None
        }
        
        return Some(cJSON_GetNumberValue(item))
    }
    
    public func toString(): String {
        let cStr = cJSON_Print(this.ptr)
        let result = cStr.toString()
        unsafe { c_free(cStr.asRawPointer()) }
        return result
    }
    
    public func finalize() {
        if this.owned && !this.ptr.isNull() {
            cJSON_Delete(this.ptr)
            this.ptr = RawPointer.null()
        }
    }
}

// ========== 使用示例 ==========
func testJson() {
    let jsonStr = """
    {
        "name": "Alice",
        "age": 30,
        "city": "Beijing"
    }
    """
    
    match JsonValue.parse(jsonStr) {
        case Ok(json) => {
            match json.getString("name") {
                case Some(name) => println("Name: ${name}")
                case None => println("Name not found")
            }
            
            match json.getNumber("age") {
                case Some(age) => println("Age: ${age}")
                case None => println("Age not found")
            }
            
            println("JSON: ${json.toString()}")
        }
        case Err(e) => println("Parse error: ${e.message}")
    }
}

四、性能基准测试

4.1 基准测试框架

复制代码
import std.time.*
import std.ffi.*

// ========== 基准测试工具 ==========
class Benchmark {
    name: String
    iterations: Int32
    
    public init(name: String, iterations: Int32) {
        this.name = name
        this.iterations = iterations
    }
    
    public func run(block: () -> Unit) {
        // 预热
        for _ in 0..<10 {
            block()
        }
        
        // 正式测试
        let start = DateTime.now()
        
        for _ in 0..<this.iterations {
            block()
        }
        
        let end = DateTime.now()
        let duration = end - start
        let avgTime = duration.totalMilliseconds() / this.iterations as Float64
        
        println("""
        Benchmark: ${this.name}
        Iterations: ${this.iterations}
        Total time: ${duration.totalMilliseconds()}ms
        Average: ${avgTime}ms
        """)
    }
}

// ========== 内存分配性能测试 ==========
func benchmarkMemoryAllocation() {
    println("=== Memory Allocation Benchmark ===\n")
    
    // 仓颉原生分配
    Benchmark("Cangjie native allocation", 100000).run {
        let arr = Array<Int32>::withCapacity(1000)
        // 自动释放
    }
    
    // C malloc/free
    Benchmark("C malloc/free", 100000).run {
        unsafe {
            let ptr = c_malloc(1000 * sizeof<Int32>())
            c_free(ptr)
        }
    }
}

// ========== 数组操作性能测试 ==========
func benchmarkArrayOperations() {
    println("=== Array Operations Benchmark ===\n")
    
    let size = 10000
    let arr = Array<Int32>::withCapacity(size)
    for i in 0..<size {
        arr.add(i)
    }
    
    // 仓颉数组求和
    Benchmark("Cangjie array sum", 10000).run {
        var sum = 0
        for i in 0..<arr.length() {
            sum += arr[i]
        }
    }

    // C数组求和
    Benchmark("C array sum", 10000).run {
        let sum = sumArray(&arr)
    }
}

// ========== 字符串处理性能测试 ==========
func benchmarkStringOperations() {
    println("=== String Operations Benchmark ===\n")
    
    let text = "Hello, World! This is a test string."
    
    // 仓颉字符串长度
    Benchmark("Cangjie string length", 1000000).run {
        let len = text.length()
    }
    
    // C strlen
    Benchmark("C strlen", 1000000).run {
        let len = stringLength(text)
    }
    
    // 字符串拼接
    Benchmark("String concatenation", 10000).run {
        var result = ""
        for i in 0..<100 {
            result += "item${i}"
        }
    }
}

性能测试结果对比:

4.2 FFI调用开销分析

复制代码
// ========== FFI调用开销测试 ==========
@extern("C")
extern func c_noop()

@extern("C")
extern func c_add_simple(a: Int32, b: Int32): Int32

func cangjie_add_simple(a: Int32, b: Int32): Int32 {
    return a + b
}

func benchmarkFfiOverhead() {
    println("=== FFI Call Overhead ===\n")
    
    // 纯仓颉函数调用
    Benchmark("Cangjie function call", 10000000).run {
        let _ = cangjie_add_simple(10, 20)
    }
    
    // FFI函数调用
    Benchmark("FFI function call", 10000000).run {
        let _ = c_add_simple(10, 20)
    }
    
    // 空函数调用(测量调用开销)
    Benchmark("Empty FFI call", 10000000).run {
        c_noop()
    }
}

FFI调用开销分解:


五、内存管理最佳实践

5.1 RAII模式实现

复制代码
import std.ffi.*

// ========== RAII资源包装器 ==========
class CFile {
    private handle: RawPointer
    
    private init(handle: RawPointer) {
        this.handle = handle
    }
    
    public static func open(path: String, mode: String): Result<CFile, Error> {
        let cPath = path.toCString()
        let cMode = mode.toCString()
        
        let handle = unsafe {
            c_fopen(cPath, cMode)
        }
        
        if handle.isNull() {
            return Err(Error("Failed to open file: ${path}"))
        }
        
        return Ok(CFile(handle))
    }
    
    public func write(data: String): Result<Unit, Error> {
        let cData = data.toCString()
        let len = data.length() as UInt64
        
        let written = unsafe {
            c_fwrite(cData.asRawPointer(), 1, len, this.handle)
        }
        
        if written != len {
            return Err(Error("Write failed"))
        }
        
        return Ok(())
    }
    
    public func read(size: UInt64): Result<String, Error> {
        let buffer = unsafe {
            c_malloc(size + 1)
        }
        
        if buffer.isNull() {
            return Err(Error("Allocation failed"))
        }
        
        let readBytes = unsafe {
            c_fread(buffer, 1, size, this.handle)
        }
        
        unsafe {
            // 添加null终止符
            let nullPtr = buffer.offset(readBytes)
            nullPtr.write<UInt8>(0)
        }
        
        let result = CString.fromRawPointer(buffer).toString()
        
        unsafe {
            c_free(buffer)
        }
        
        return Ok(result)
    }
    
    // 析构函数:自动关闭文件
    public func finalize() {
        if !this.handle.isNull() {
            unsafe {
                c_fclose(this.handle)
            }
            this.handle = RawPointer.null()
        }
    }
}

// ========== C文件函数绑定 ==========
@extern("C")
@unsafe
extern func c_fopen(filename: CString, mode: CString): RawPointer

@extern("C")
@unsafe
extern func c_fclose(stream: RawPointer): Int32

@extern("C")
@unsafe
extern func c_fwrite(
    ptr: RawPointer,
    size: UInt64,
    count: UInt64,
    stream: RawPointer
): UInt64

@extern("C")
@unsafe
extern func c_fread(
    ptr: RawPointer,
    size: UInt64,
    count: UInt64,
    stream: RawPointer
): UInt64

// ========== 使用示例 ==========
func testCFile() {
    // 文件自动关闭,无需手动管理
    match CFile.open("test.txt", "w") {
        case Ok(file) => {
            _ = file.write("Hello, FFI!")
            // file 离开作用域时自动关闭
        }
        case Err(e) => println("Error: ${e.message}")
    }
    
    // 读取文件
    match CFile.open("test.txt", "r") {
        case Ok(file) => {
            match file.read(1024) {
                case Ok(content) => println("Content: ${content}")
                case Err(e) => println("Read error: ${e.message}")
            }
        }
        case Err(e) => println("Open error: ${e.message}")
    }
}

5.2 智能指针实现

复制代码
import std.ffi.*
import std.concurrent.Atomic

// ========== 引用计数智能指针 ==========
class SharedPtr<T> {
    private ptr: RawPointer
    private refCount: &AtomicInt32
    private deleter: (RawPointer) -> Unit
    
    private init(
        ptr: RawPointer,
        refCount: &AtomicInt32,
        deleter: (RawPointer) -> Unit
    ) {
        this.ptr = ptr
        this.refCount = refCount
        this.deleter = deleter
    }
    
    public static func make(value: T): SharedPtr<T> {
        let ptr = unsafe {
            c_malloc(sizeof<T>())
        }
        
        unsafe {
            ptr.write<T>(value)
        }
        
        let refCount = AtomicInt32(1)
        let deleter = |p: RawPointer| {
            unsafe { c_free(p) }
        }
        
        return SharedPtr<T>(ptr, &refCount, deleter)
    }
    
    public static func fromCPointer(
        ptr: RawPointer,
        deleter: (RawPointer) -> Unit
    ): SharedPtr<T> {
        let refCount = AtomicInt32(1)
        return SharedPtr<T>(ptr, &refCount, deleter)
    }
    
    // 拷贝构造
    public func clone(): SharedPtr<T> {
        this.refCount.fetchAdd(1)
        return SharedPtr<T>(this.ptr, this.refCount, this.deleter)
    }
    
    public func get(): &T {
        unsafe {
            return this.ptr.readRef<T>()
        }
    }
    
    public func getMut(): &mut T {
        unsafe {
            return this.ptr.readMutRef<T>()
        }
    }
    
    public func refCount(): Int32 {
        return this.refCount.load()
    }
    
    // 析构函数
    public func finalize() {
        let oldCount = this.refCount.fetchSub(1)
        
        if oldCount == 1 {
            // 最后一个引用,释放内存
            this.deleter(this.ptr)
        }
    }
}

// ========== 使用示例 ==========
struct LargeObject {
    data: Array<Int32, 1000>
    id: Int32
}

func testSharedPtr() {
    let obj = LargeObject(
        data: Array::zero(),
        id: 42
    )
    
    let ptr1 = SharedPtr<LargeObject>.make(obj)
    println("Ref count: ${ptr1.refCount()}")  // 1
    
    let ptr2 = ptr1.clone()
    println("Ref count: ${ptr1.refCount()}")  // 2
    
    {
        let ptr3 = ptr1.clone()
        println("Ref count: ${ptr1.refCount()}")  // 3
    }  // ptr3 销毁
    
    println("Ref count: ${ptr1.refCount()}")  // 2
    
    // ptr1 和 ptr2 离开作用域时,对象自动释放
}

5.3 内存泄漏检测

复制代码
import std.ffi.*
import std.concurrent.Mutex
import std.collections.HashMap

// ========== 内存分配跟踪器 ==========
class MemoryTracker {
    private allocations: HashMap<RawPointer, AllocationInfo>
    private mutex: Mutex
    private totalAllocated: Int64
    private totalFreed: Int64
    
    public init() {
        this.allocations = HashMap<RawPointer, AllocationInfo>()
        this.mutex = Mutex()
        this.totalAllocated = 0
        this.totalFreed = 0
    }
    
    public func trackAlloc(ptr: RawPointer, size: UInt64, location: String) {
        let guard = this.mutex.lock()
        
        let info = AllocationInfo(
            size: size,
            location: location,
            timestamp: DateTime.now()
        )
        
        this.allocations.put(ptr, info)
        this.totalAllocated += size as Int64
    }
    
    public func trackFree(ptr: RawPointer) {
        let guard = this.mutex.lock()
        
        match this.allocations.get(ptr) {
            case Some(info) => {
                this.totalFreed += info.size as Int64
                this.allocations.remove(ptr)
            }
            case None => {
                println("WARNING: Attempt to free untracked pointer")
            }
        }
    }
    
    public func printLeaks() {
        let guard = this.mutex.lock()
        
        println("=== Memory Leak Report ===")
        println("Total allocated: ${this.totalAllocated} bytes")
        println("Total freed: ${this.totalFreed} bytes")
        println("Leaked: ${this.totalAllocated - this.totalFreed} bytes")
        println("Leak count: ${this.allocations.size()}\n")
        
        if this.allocations.size() > 0 {
            println("Leaked allocations:")
            for (ptr, info) in this.allocations {
                println("  ${info.size} bytes at ${info.location}")
            }
        } else {
            println("No leaks detected!")
        }
    }
}

struct AllocationInfo {
    size: UInt64
    location: String
    timestamp: DateTime
}

// ========== 全局跟踪器 ==========
let memoryTracker = MemoryTracker()

// ========== 包装的分配函数 ==========
func trackedMalloc(size: UInt64, location: String): RawPointer {
    let ptr = unsafe {
        c_malloc(size)
    }
    
    if !ptr.isNull() {
        memoryTracker.trackAlloc(ptr, size, location)
    }
    
    return ptr
}

func trackedFree(ptr: RawPointer) {
    if !ptr.isNull() {
        memoryTracker.trackFree(ptr)
        unsafe {
            c_free(ptr)
        }
    }
}

// ========== 使用示例 ==========
func testMemoryTracking() {
    // 正常分配和释放
    let ptr1 = trackedMalloc(1024, "testMemoryTracking:line1")
    trackedFree(ptr1)
    
    // 内存泄漏
    let ptr2 = trackedMalloc(2048, "testMemoryTracking:line2")
    // 忘记释放 ptr2
    
    // 打印泄漏报告
    memoryTracker.printLeaks()
}

六、高级FFI技巧

6.1 回调函数处理

复制代码
import std.ffi.*

// ========== C回调函数类型 ==========
type CCallback = extern "C" func(data: RawPointer) -> Int32

// ========== C库函数:注册回调 ==========
@extern("C")
extern func register_callback(callback: CCallback, userData: RawPointer)

@extern("C")
extern func trigger_callback()

// ========== 仓颉回调包装 ==========
class CallbackWrapper {
    private handler: (String) -> Int32
    
    public init(handler: (String) -> Int32) {
        this.handler = handler
    }
    
    // C兼容的回调函数
    @extern("C")
    public static func callbackTrampoline(data: RawPointer): Int32 {
        unsafe {
            // 从用户数据恢复仓颉闭包
            let wrapper = data.read<&CallbackWrapper>()
            let message = "Callback triggered"
            return wrapper.handler(message)
        }
    }
    
    public func register() {
        let userData = unsafe {
            let ptr = c_malloc(sizeof<&CallbackWrapper>())
            ptr.write<&CallbackWrapper>(&this)
            ptr
        }
        
        register_callback(CallbackWrapper.callbackTrampoline, userData)
    }
}

// ========== 使用示例 ==========
func testCallback() {
    let wrapper = CallbackWrapper { message =>
        println("Received: ${message}")
        return 0
    }
    
    wrapper.register()
    trigger_callback()
}

6.2 可变参数函数

复制代码
import std.ffi.*

// ========== C可变参数函数 ==========
// C代码: int printf(const char* format, ...);
@extern("C")
extern func c_printf(format: CString, ...): Int32

// ========== 仓颉包装(类型安全)==========
func printf(format: String, args: Array<FormatArg>) {
    let cFormat = format.toCString()
    
    // 根据参数类型调用
    match args.length() {
        0 => c_printf(cFormat)
        1 => match args[0] {
            case IntArg(value) => c_printf(cFormat, value)
            case StringArg(value) => c_printf(cFormat, value.toCString())
            case DoubleArg(value) => c_printf(cFormat, value)
        }
        // 更多参数情况...
    }
}

enum FormatArg {
    | IntArg(Int32)
    | StringArg(String)
    | DoubleArg(Float64)
}

func testPrintf() {
    printf("Hello, %s!\n", [StringArg("World")])
    printf("Number: %d\n", [IntArg(42)])
    printf("Float: %.2f\n", [DoubleArg(3.14159)])
}

6.3 函数指针表

复制代码
import std.ffi.*

// ========== C函数指针表结构 ==========
/*
// C代码
typedef struct {
    int (*add)(int, int);
    int (*multiply)(int, int);
    void (*print)(const char*);
} MathOps;
*/

@repr(C)
struct MathOps {
    add: extern "C" func(Int32, Int32) -> Int32
    multiply: extern "C" func(Int32, Int32) -> Int32
    print: extern "C" func(CString) -> Unit
}

// ========== 实现C函数 ==========
@extern("C")
func cj_add(a: Int32, b: Int32): Int32 {
    return a + b
}

@extern("C")
func cj_multiply(a: Int32, b: Int32): Int32 {
    return a * b
}

@extern("C")
func cj_print(msg: CString) {
    println(msg.toString())
}

// ========== 创建函数表 ==========
func createMathOps(): MathOps {
    return MathOps(
        add: cj_add,
        multiply: cj_multiply,
        print: cj_print
    )
}

// ========== C库函数:使用函数表 ==========
@extern("C")
extern func use_math_ops(ops: &MathOps)

func testFunctionPointers() {
    let ops = createMathOps()
    use_math_ops(&ops)
}

七、实战案例:图像处理库集成

7.1 集成libpng

复制代码
import std.ffi.*

// ========== libpng类型定义 ==========
type PngStructPtr = RawPointer
type PngInfoPtr = RawPointer

// ========== libpng函数绑定(简化版)==========
@extern("C", library: "png")
extern func png_create_read_struct(
    user_png_ver: CString,
    error_ptr: RawPointer,
    error_fn: RawPointer,
    warn_fn: RawPointer
): PngStructPtr

@extern("C", library: "png")
extern func png_create_info_struct(png_ptr: PngStructPtr): PngInfoPtr

@extern("C", library: "png")
extern func png_destroy_read_struct(
    png_ptr_ptr: &PngStructPtr,
    info_ptr_ptr: &PngInfoPtr,
    end_info_ptr_ptr: &PngInfoPtr
)

@extern("C", library: "png")
extern func png_init_io(png_ptr: PngStructPtr, fp: RawPointer)

@extern("C", library: "png")
extern func png_read_info(png_ptr: PngStructPtr, info_ptr: PngInfoPtr)

@extern("C", library: "png")
extern func png_get_image_width(png_ptr: PngStructPtr, info_ptr: PngInfoPtr): UInt32

@extern("C", library: "png")
extern func png_get_image_height(png_ptr: PngStructPtr, info_ptr: PngInfoPtr): UInt32

@extern("C", library: "png")
extern func png_read_image(png_ptr: PngStructPtr, row_pointers: RawPointer)

// ========== PNG图像类 ==========
class PngImage {
    width: UInt32
    height: UInt32
    data: Array<UInt8>
    
    private init(width: UInt32, height: UInt32, data: Array<UInt8>) {
        this.width = width
        this.height = height
        this.data = data
    }
    
    public static func load(path: String): Result<PngImage, Error> {
        // 打开文件
        let file = match CFile.open(path, "rb") {
            case Ok(f) => f
            case Err(e) => return Err(e)
        }
        
        // 创建PNG读取结构
        let pngPtr = png_create_read_struct(
            "1.6.37".toCString(),
            RawPointer.null(),
            RawPointer.null(),
            RawPointer.null()
        )
        
        if pngPtr.isNull() {
            return Err(Error("Failed to create PNG read struct"))
        }
        
        let infoPtr = png_create_info_struct(pngPtr)
        if infoPtr.isNull() {
            png_destroy_read_struct(&pngPtr, &RawPointer.null(), &RawPointer.null())
            return Err(Error("Failed to create PNG info struct"))
        }
        
        // 设置文件IO
        png_init_io(pngPtr, file.handle)
        
        // 读取图像信息
        png_read_info(pngPtr, infoPtr)
        
        let width = png_get_image_width(pngPtr, infoPtr)
        let height = png_get_image_height(pngPtr, infoPtr)
        
        // 分配图像数据
        let dataSize = (width * height * 4) as UInt64  // RGBA
        let data = Array<UInt8>::withCapacity(dataSize as Int64)
        
        // 读取图像数据
        // ... 实现细节省略
        
        // 清理
        png_destroy_read_struct(&pngPtr, &infoPtr, &RawPointer.null())
        
        return Ok(PngImage(width, height, data))
    }
    
    public func getPixel(x: UInt32, y: UInt32): Rgba {
        let index = ((y * this.width + x) * 4) as Int64
        return Rgba(
            r: this.data[index],
            g: this.data[index + 1],
            b: this.data[index + 2],
            a: this.data[index + 3]
        )
    }
    
    public func setPixel(x: UInt32, y: UInt32, color: Rgba) {
        let index = ((y * this.width + x) * 4) as Int64
        this.data[index] = color.r
        this.data[index + 1] = color.g
        this.data[index + 2] = color.b
        this.data[index + 3] = color.a
    }
}

struct Rgba {
    r: UInt8
    g: UInt8
    b: UInt8
    a: UInt8
}

// ========== 使用示例 ==========
func testPngImage() {
    match PngImage.load("image.png") {
        case Ok(image) => {
            println("Image size: ${image.width}x${image.height}")
            
            // 获取像素
            let pixel = image.getPixel(10, 10)
            println("Pixel at (10,10): RGB(${pixel.r}, ${pixel.g}, ${pixel.b})")
            
            // 修改像素
            image.setPixel(10, 10, Rgba(r: 255, g: 0, b: 0, a: 255))
        }
        case Err(e) => println("Error: ${e.message}")
    }
}

八、总结与最佳实践

8.1 FFI使用原则

8.2 性能优化清单

减少FFI调用次数

  • 批量传递数据而非单个元素
  • 缓存频繁访问的C函数结果
  • 在C层实现计算密集型循环

优化内存传递

  • 使用引用而非值传递大对象
  • 避免不必要的内存复制
  • 考虑使用共享内存

正确使用内存管理

  • 优先使用RAII模式
  • 实现智能指针封装C指针
  • 及时释放不再使用的资源

类型转换优化

  • 最小化字符串转换
  • 使用零拷贝技术
  • 预分配缓冲区

8.3 常见陷阱

陷阱1:内存泄漏

复制代码
// 错误:忘记释放
func bad() {
    let ptr = unsafe { c_malloc(1024) }
    // 忘记调用 c_free(ptr)
}

// 正确:使用RAII
func good() {
    let managed = ManagedPointer.alloc(1024)
    // 自动释放
}

陷阱2:悬空指针

复制代码
// 错误:返回局部变量的指针
func bad(): RawPointer {
    let local = 42
    return &local  // ❌ 悬空指针!
}

// 正确:使用堆分配
func good(): RawPointer {
    let ptr = unsafe { c_malloc(sizeof<Int32>()) }
    unsafe { ptr.write<Int32>(42) }
    return ptr
}

陷阱3:ABI不兼容

复制代码
// 错误:忘记@repr(C)
struct Bad {  // ❌ 内存布局不确定
    a: Int32
    b: Float64
}

// 正确:显式指定C布局
@repr(C)
struct Good {  // ✅ C兼容
    a: Int32
    b: Float64
}

九、总结

核心知识点回顾

  1. FFI原理:理解仓颉与C/C++互操作的底层机制
  2. 类型映射:掌握各种数据类型的转换规则
  3. 内存管理:使用RAII和智能指针确保安全
  4. 性能优化:减少跨边界调用,优化数据传递
  5. 实战应用:集成SQLite、JSON、PNG等库

讨论问题

  1. 设计权衡:什么时候应该使用FFI?什么时候应该纯仓颉实现?
  2. 安全性:如何在性能和安全性之间找到平衡?
  3. 可维护性:如何设计易于维护的FFI绑定层?

学习建议

  • 🔹 深入理解ABI:学习不同平台的调用约定
  • 🔹 实践项目:集成实际的C/C++库
  • 🔹 性能分析:使用工具诊断FFI性能问题
  • 🔹 安全审计:定期检查内存安全和资源泄漏

参考资源

相关推荐
西哥写代码8 小时前
基于dcmtk的dicom工具 第十三章 dicom文件导出bmp、jpg、png、tiff、mp4
c++·mfc·dicom·dcmtk·tiffopen·dipngplugin·dijpegplugin
十五学长8 小时前
程序设计C语言
c语言·开发语言·笔记·学习·考研
永远有缘9 小时前
Java、Python、C# 和 C++ 在函数定义语法上的主要区别
java·c++·python·c#
绛洞花主敏明10 小时前
Go切片的赋值
c++·算法·golang
GilgameshJSS12 小时前
STM32H743-ARM例程33-TOUCH
c语言·arm开发·stm32·单片机·嵌入式硬件
纵有疾風起14 小时前
C++—string(1):string类的学习与使用
开发语言·c++·经验分享·学习·开源·1024程序员节
luoqice14 小时前
如何编译QT可执行release程序
c语言
冴羽17 小时前
10 个被严重低估的 JS 特性,直接少写 500 行代码
前端·javascript·性能优化
郭源潮118 小时前
《Muduo网络库:实现TcpServer类终章》
服务器·网络·c++·网络库