Cangjie-C Interoperability
Inspect one language lane at a time so line-level text and code deltas stay readable.
Diff Lane
English
0 modified sections0 code block delta0 anchor delta
Diff Lane
中文
8 modified sections0 code block delta0 anchor delta
modified仓颉调用 C 的函数textcode+3 lines, -1 line
v1.0.5
Section Text
1
在仓颉中要调用 C 的函数,需要在仓颉语言中用 `@C` 和 `foreign` 关键字声明这个函数,但 `@C` 在修饰 `foreign` 声明的时候,可以省略。2
3
举个例子,假设要调用 C 的 `rand` 和 `printf` 函数,它的函数签名如下:4
5
6
那么在仓颉中调用这两个函数的方式如下:7
8
9
需要注意的是:10
11
1. `foreign` 修饰函数声明,代表该函数为外部函数。被 `foreign` 修饰的函数只能有函数声明,不能有函数实现。12
2. `foreign` 声明的函数,参数和返回类型必须符合 C 和仓颉数据类型之间的映射关系,详情请参见[类型映射](./cangjie-c.md#类型映射)。13
3. 由于 C 侧函数很可能产生不安全操作,所以调用 `foreign` 修饰的函数需要被 `unsafe` 块包裹,否则会发生编译错误。14
4. `@C` 修饰的 `foreign` 关键字只能用来修饰函数声明,不可用来修饰其他声明,否则会发生编译错误。15
5. `@C` 只支持修饰 `foreign` 函数、顶层作用域中的非泛型函数和 `struct` 类型。16
6. `foreign` 函数不支持命名参数和参数默认值。`foreign` 函数允许变长参数,使用 `...` 表达,只能用于参数列表的最后。变长参数均需要满足 `CType` 约束,但不必是同一类型。17
7. 仓颉(CJNative 后端)虽然提供了栈扩容能力,但是由于 C 侧函数实际使用栈大小仓颉无法感知,所以 ffi 调用进入 C 函数后,仍然存在栈溢出的风险(可能导致程序运行时崩溃或者产生不可预期的行为),需要开发者根据实际情况,修改 `cjStackSize` 的配置。18
19
一些不合法的 `foreign` 声明的示例代码如下:Code 1 · c
1
// stdlib.h2
int rand();3
4
// stdio.h5
int printf (const char *fmt, ...);Code 2 · cangjie
1
// declare the function by `foreign` keyword, and omit `@C`2
foreign func rand(): Int323
foreign func printf(fmt: CString, ...): Int324
5
main() {6
// call this function by `unsafe` block7
let r = unsafe { rand() }8
println("random number ${r}")9
unsafe {10
var fmt = LibC.mallocCString("Hello, No.%d\n")11
printf(fmt, 1)12
LibC.free(fmt)13
}14
}Code 3 · cangjie
1
foreign func rand(): Int32 { // compiler error2
return 03
}4
@C5
foreign var a: Int32 = 0 // compiler error6
@C7
foreign class A{} // compiler error8
@C9
foreign interface B{} // compiler errorv1.1.0
Section Text
1
在仓颉中要调用 C 的函数,需要在仓颉语言中用 `@C` 和 `foreign` 关键字声明这个函数,但 `@C` 在修饰 `foreign` 声明的时候,可以省略。2
3
举个例子,假设要调用 C 的 `rand` 和 `printf` 函数,它的函数签名如下:4
5
6
那么在仓颉中调用这两个函数的方式如下:7
8
<!-- run -->9
10
11
需要注意的是:12
13
1. `foreign` 修饰函数声明,代表该函数为外部函数。被 `foreign` 修饰的函数只能有函数声明,不能有函数实现。14
2. `foreign` 声明的函数,参数和返回类型必须符合 C 和仓颉数据类型之间的映射关系,详情请参见[类型映射](./cangjie-c.md#类型映射)。15
3. 由于 C 侧函数很可能产生不安全操作,所以调用 `foreign` 修饰的函数需要被 `unsafe` 块包裹,否则会发生编译错误。16
4. `@C` 修饰的 `foreign` 关键字只能用来修饰函数声明,不可用来修饰其他声明,否则会发生编译错误。17
5. `@C` 只支持修饰 `foreign` 函数、顶层作用域中的非泛型函数和 `struct` 类型。18
6. `foreign` 函数不支持命名参数和参数默认值。`foreign` 函数允许变长参数,使用 `...` 表达,只能用于参数列表的最后。变长参数均需要满足 `CType` 约束,但不必是同一类型。19
7. 仓颉(CJNative 后端)虽然提供了栈扩容能力,但是由于 C 侧函数实际使用栈大小其无法感知,所以 ffi 调用进入 C 函数后,仍然存在栈溢出的风险(可能导致程序运行时崩溃或者产生不可预期的行为),需要开发者根据实际情况,修改 `cjStackSize` 的配置。20
21
一些不合法的 `foreign` 声明的示例代码如下:22
23
<!-- compile.error -->Code 1 · c
1
// stdlib.h2
int rand();3
4
// stdio.h5
int printf(const char *fmt, ...);Code 2 · cangjie
1
// declare the function by `foreign` keyword, and omit `@C`2
foreign func rand(): Int323
foreign func printf(fmt: CString, ...): Int324
5
main() {6
// call this function by `unsafe` block7
let r = unsafe { rand() }8
println("random number ${r}")9
unsafe {10
var fmt = LibC.mallocCString("Hello, No.%d\n")11
printf(fmt, 1)12
LibC.free(fmt)13
}14
}Code 3 · cangjie
1
foreign func rand(): Int32 { // compiler error2
return 03
}4
@C5
foreign var a: Int32 = 0 // compiler error6
@C7
foreign class A{} // compiler error8
@C9
foreign interface B{} // compiler errormodifiedinout 参数text+1 line
v1.0.5
Section Text
1
在仓颉中调用 `CFunc` 时,其实参可以使用 `inout` 关键字修饰,组成引用传值表达式,此时,该参数按引用传递。引用传值表达式的类型为 `CPointer<T>`,其中 `T` 为 `inout` 修饰的表达式的类型。2
3
引用传值表达式具有以下约束:4
5
- 仅可用于对 `CFunc` 的调用处。6
- 其修饰对象的类型必须满足 `CType` 约束,但不可以是 `CString`。7
- 其修饰对象不可以是用 `let` 定义的,不可以是字面量、入参、其他表达式的值等临时变量。8
- 通过仓颉侧引用传值表达式传递到 C 侧的指针,仅保证在函数调用期间有效,即此种场景下 C 侧不应该保存指针以留作后用。9
10
`inout` 修饰的变量,可以是定义在顶层作用域中的变量、局部变量、`struct` 中的成员变量,但不能直接或间接来源于 `class` 的实例成员变量。11
12
下面是一个例子:13
14
15
> **注意:**16
>17
> 使用宏扩展特性时,在宏的定义中,暂时不能使用 `inout` 参数特性。Code 1 · cangjie
1
foreign func foo1(ptr: CPointer<Int32>): Unit2
3
@C4
func foo2(ptr: CPointer<Int32>): Unit {5
let n = unsafe { ptr.read() }6
println("*ptr = ${n}")7
}8
9
let foo3: CFunc<(CPointer<Int32>) -> Unit> = { ptr =>10
let n = unsafe { ptr.read() }11
println("*ptr = ${n}")12
}13
14
struct Data {15
var n: Int32 = 016
}17
18
class A {19
var data = Data()20
}21
22
main() {23
var n: Int32 = 024
unsafe {25
foo1(inout n) // OK26
foo2(inout n) // OK27
foo3(inout n) // OK28
}29
var data = Data()30
var a = A()31
unsafe {32
foo1(inout data.n) // OK33
foo1(inout a.data.n) // Error, n is derived indirectly from instance member variables of class A34
}35
}v1.1.0
Section Text
1
在仓颉中调用 `CFunc` 时,其实参可以使用 `inout` 关键字修饰,组成引用传值表达式,此时,该参数按引用传递。引用传值表达式的类型为 `CPointer<T>`,其中 `T` 为 `inout` 修饰的表达式的类型。2
3
引用传值表达式具有以下约束:4
5
- 仅可用于对 `CFunc` 的调用处。6
- 其修饰对象的类型必须满足 `CType` 约束,但不可以是 `CString`。7
- 其修饰对象不可以是用 `let` 定义的,不可以是字面量、入参、其他表达式的值等临时变量。8
- 通过仓颉侧引用传值表达式传递到 C 侧的指针,仅保证在函数调用期间有效,即此种场景下 C 侧不应该保存指针以留作后用。9
10
`inout` 修饰的变量,可以是定义在顶层作用域中的变量、局部变量、`struct` 中的成员变量,但不能直接或间接来源于 `class` 的实例成员变量。11
12
下面是一个例子:13
14
<!-- compile.error -->15
16
17
> **注意:**18
>19
> 使用宏扩展特性时,在宏的定义中,暂时不能使用 `inout` 参数特性。Code 1 · cangjie
1
foreign func foo1(ptr: CPointer<Int32>): Unit2
3
@C4
func foo2(ptr: CPointer<Int32>): Unit {5
let n = unsafe { ptr.read() }6
println("*ptr = ${n}")7
}8
9
let foo3: CFunc<(CPointer<Int32>) -> Unit> = { ptr =>10
let n = unsafe { ptr.read() }11
println("*ptr = ${n}")12
}13
14
struct Data {15
var n: Int32 = 016
}17
18
class A {19
var data = Data()20
}21
22
main() {23
var n: Int32 = 024
unsafe {25
foo1(inout n) // OK26
foo2(inout n) // OK27
foo3(inout n) // OK28
}29
var data = Data()30
var a = A()31
unsafe {32
foo1(inout data.n) // OK33
foo1(inout a.data.n) // Error, n is derived indirectly from instance member variables of class A34
}35
}modified使用说明text+1 line
v1.0.5
Section Text
1
- 操作系统线程局部变量使用约束2
3
仓颉和 C 语言互操作时,使用操作系统线程的局部变量存在风险,说明如下:4
5
1. 线程局部变量包括 C 语言提供的 `thread_local` 定义的变量和使用 `pthread_key_create` 创建的变量。6
2. 仓颉具备仓颉线程调度能力,支持仓颉线程的切换和恢复,仓颉线程被调度到哪个操作系统线程是随机的,从而在仓颉线程上调用其他语言的线程局部变量是有风险的。7
8
如下示例中,仓颉调用 C 语言的线程局部变量存在风险:9
10
```c11
// C language logic using thread_local12
static thread_local int64_t count = 0;13
int64_t getCount() {14
count++;15
return count;16
}17
```18
19
```cangjie20
foreign func getCount(): Int6421
// Cangjie invokes the preceding C language logic22
spawn {23
let r1 = unsafe { getCount() } // r1 equals 124
sleep(Duration.second * 10)25
let r2 = unsafe { getCount() } // r2 may not be equal to 226
}27
```28
29
- 线程绑定使用约束30
31
仓颉调用 C 语言执行互操作逻辑时,仓颉线程调度到哪个操作系统线程是随机的,线程优先级和线程亲和性等与线程绑定的行为不建议使用。32
33
- 同步原语使用说明34
35
仓颉调用 C 语言执行互操作逻辑时,当前这个仓颉线程会等待互操作逻辑执行结束,不建议在其他语言中出现可能导致长时间等待的阻塞性行为。36
37
- 对进程 fork 场景的支持说明38
39
仓颉调用 C 语言执行互操作逻辑时,如果在 C 语言中以 `fork()` 方式创建子进程,子进程中不支持执行仓颉逻辑。同一进程中其他操作系统线程不受影响。40
41
- 进程退出时的说明42
43
仓颉调用 C 语言执行互操作逻辑时,如果在 C 语言中退出进程,进程内共享的资源已经释放,可能导致非法访问等错误。v1.1.0
Section Text
1
- 操作系统线程局部变量使用约束2
3
仓颉和 C 语言互操作时,使用操作系统线程的局部变量存在风险,说明如下:4
5
1. 线程局部变量包括 C 语言提供的 `thread_local` 定义的变量和使用 `pthread_key_create` 创建的变量。6
2. 仓颉具备仓颉线程调度能力,支持仓颉线程的切换和恢复,仓颉线程被调度到哪个操作系统线程是随机的,从而在仓颉线程上调用其他语言的线程局部变量是有风险的。7
8
如下示例中,仓颉调用 C 语言的线程局部变量存在风险:9
10
```c11
// C language logic using thread_local12
static thread_local int64_t count = 0;13
int64_t getCount() {14
count++;15
return count;16
}17
```18
<!-- code_check_manual -->19
20
```cangjie21
foreign func getCount(): Int6422
// Cangjie invokes the preceding C language logic23
spawn {24
let r1 = unsafe { getCount() } // r1 equals 125
sleep(Duration.second * 10)26
let r2 = unsafe { getCount() } // r2 may not be equal to 227
}28
```29
30
- 线程绑定使用约束31
32
仓颉调用 C 语言执行互操作逻辑时,仓颉线程调度到哪个操作系统线程是随机的,线程优先级和线程亲和性等与线程绑定的行为不建议使用。33
34
- 同步原语使用说明35
36
仓颉调用 C 语言执行互操作逻辑时,当前这个仓颉线程会等待互操作逻辑执行结束,不建议在其他语言中出现可能导致长时间等待的阻塞性行为。37
38
- 对进程 fork 场景的支持说明39
40
仓颉调用 C 语言执行互操作逻辑时,如果在 C 语言中以 `fork()` 方式创建子进程,子进程中不支持执行仓颉逻辑。同一进程中其他操作系统线程不受影响。41
42
- 进程退出时的说明43
44
仓颉调用 C 语言执行互操作逻辑时,如果在 C 语言中退出进程,进程内共享的资源已经释放,可能导致非法访问等错误。modified数组text+1 line
v1.0.5
Section Text
1
仓颉使用 `VArray` 类型与 C 的数组类型映射,`VArray` 可以作为函数参数和 `@C struct` 成员。当 `VArray<T, $N>` 中的元素类型 `T` 满足 `CType` 约束时, `VArray<T, $N>` 类型也满足 `CType` 约束。2
3
**作为函数参数类型:**4
5
当 `VArray` 作为 `CFunc` 的参数时, `CFunc` 的函数签名仅可以是 `CPointer<T>` 类型或 `VArray<T, $N>` 类型。当函数签名中的参数类型为 `VArray<T, $N>` 时,传递的参数仍以 `CPointer<T>` 形式传递。6
7
`VArray` 作为参数的使用示例如下:8
9
10
对应的 C 侧函数定义可以是:11
12
13
调用 `CFunc` 时,需要通过 `inout` 修饰 `VArray` 类型变量:14
15
16
`VArray` 不允许作为 `CFunc` 的返回值类型。17
18
**作为 @C struct 成员:**19
20
当 `VArray` 作为 `@C struct` 成员时,它的内存布局与 C 侧的结构体排布一致,需要保证仓颉侧声明长度与类型也与 C 完全一致:21
22
23
在仓颉中,可以声明为如下结构体与 C 代码对应:24
25
<!-- run -->26
27
28
> **注意:**29
>30
> C 语言中允许结构体的最后一个字段为未指明长度的数组类型,该数组被称为柔性数组(flexible array),仓颉不支持包含柔性数组的结构体的映射。Code 1 · cangjie
1
foreign func cfoo1(a: CPointer<Int32>): Unit2
foreign func cfoo2(a: VArray<Int32, $3>): UnitCode 2 · c
1
void cfoo1(int *a) { ... }2
void cfoo2(int a[3]) { ... }Code 3 · cangjie
1
var a: VArray<Int32, $3> = [1, 2, 3]2
unsafe {3
cfoo1(inout a)4
cfoo2(inout a)5
}Code 4 · c
1
struct S {2
int a[2];3
int b[0];4
}Code 5 · cangjie
1
@C2
struct S {3
var a = VArray<Int32, $2>(repeat: 0)4
var b = VArray<Int32, $0>(repeat: 0)5
}v1.1.0
Section Text
1
仓颉使用 `VArray` 类型与 C 的数组类型映射,`VArray` 可以作为函数参数和 `@C struct` 成员。当 `VArray<T, $N>` 中的元素类型 `T` 满足 `CType` 约束时, `VArray<T, $N>` 类型也满足 `CType` 约束。2
3
**作为函数参数类型:**4
5
当 `VArray` 作为 `CFunc` 的参数时, `CFunc` 的函数签名仅可以是 `CPointer<T>` 类型或 `VArray<T, $N>` 类型。当函数签名中的参数类型为 `VArray<T, $N>` 时,传递的参数仍以 `CPointer<T>` 形式传递。6
7
`VArray` 作为参数的使用示例如下:8
9
<!-- code_check_manual -->10
11
12
对应的 C 侧函数定义可以是:13
14
15
调用 `CFunc` 时,需要通过 `inout` 修饰 `VArray` 类型变量:16
17
<!-- code_check_manual -->18
19
20
`VArray` 不允许作为 `CFunc` 的返回值类型。21
22
**作为 @C struct 成员:**23
24
当 `VArray` 作为 `@C struct` 成员时,它的内存布局与 C 侧的结构体排布一致,需要保证仓颉侧声明长度与类型也与 C 完全一致:25
26
27
在仓颉中,可以声明为如下结构体与 C 代码对应:28
29
<!-- run -->30
31
32
> **注意:**33
>34
> C 语言中允许结构体的最后一个字段为未指明长度的数组类型,该数组被称为柔性数组(flexible array),仓颉不支持包含柔性数组的结构体的映射。Code 1 · cangjie
1
foreign func cfoo1(a: CPointer<Int32>): Unit2
foreign func cfoo2(a: VArray<Int32, $3>): UnitCode 2 · c
1
void cfoo1(int *a) { ... }2
void cfoo2(int a[3]) { ... }Code 3 · cangjie
1
var a: VArray<Int32, $3> = [1, 2, 3]2
unsafe {3
cfoo1(inout a)4
cfoo2(inout a)5
}Code 4 · c
1
struct S {2
int a[2];3
int b[0];4
}Code 5 · cangjie
1
@C2
struct S {3
var a = VArray<Int32, $2>(repeat: 0)4
var b = VArray<Int32, $0>(repeat: 0)5
}modifiedsizeOf/alignOftext+2 lines, -1 line
v1.0.5
Section Text
1
仓颉还提供了 `sizeOf` 和 `alignOf` 两个函数,用于获取上述 C 互操作类型的内存占用和内存对齐数值(单位:字节),函数声明如下:2
3
4
使用示例:5
6
<!-- run -->7
8
9
在 64 位机器上运行,将输出:Code 1 · cangjie
1
public func sizeOf<T>(): UIntNative where T <: CType2
public func alignOf<T>(): UIntNative where T <: CTypeCode 2 · cangjie
1
@C2
struct Data {3
var a: Int64 = 04
var b: Float32 = 0.05
}6
7
main() {8
println(sizeOf<Data>())9
println(alignOf<Data>())10
}Code 3 · text
1
162
8v1.1.0
Section Text
1
仓颉还提供了 `sizeOf` 和 `alignOf` 两个函数,用于获取上述 C 互操作类型的内存占用和内存对齐数值(单位:字节),函数声明如下:2
3
<!-- code_no_check -->4
5
6
使用示例:7
8
<!-- verify -->9
10
11
在 64 位机器上运行,将输出:Code 1 · cangjie
1
public func sizeOf<T>(): UIntNative where T <: CType2
public func alignOf<T>(): UIntNative where T <: CTypeCode 2 · cangjie
1
@C2
struct Data {3
var a: Int64 = 04
var b: Float32 = 0.05
}6
7
main() {8
println(sizeOf<Data>())9
println(alignOf<Data>())10
}Code 3 · text
1
162
8modifiedC 调用仓颉的函数text+1 line
v1.0.5
Section Text
1
仓颉提供 `CFunc` 类型来对应 C 侧的函数指针类型。C 侧的函数指针可以传递到仓颉,仓颉也可以构造出对应 C 的函数指针的变量传递到 C 侧。2
3
假设一个 C 的库 API 如下:4
5
6
对应的,在仓颉里面这个函数可以声明为:7
8
9
CFunc 类型的变量可以从 C 侧传递过来,也可以在仓颉侧构造出来。在仓颉侧构造 CFunc 类型有两种办法,一个是用 `@C` 修饰的函数,另外一个是标记为 CFunc 类型的闭包。10
11
`@C` 修饰的函数,表明它的函数签名是满足 C 的调用规则的,定义还是写在仓颉这边。`foreign` 修饰的函数定义是在 C 侧的。12
13
> **注意:**14
>15
> `foreign` 修饰的函数与 `@C` 修饰的函数,这两种 `CFunc` 的命名不建议使用 `CJ_`(不区分大小写)作为前缀,否则可能与标准库及运行时等编译器内部符号出现冲突,导致未定义行为。16
17
示例如下:18
19
20
假设 C 函数编译出来的库是 "libmyfunc.so",那么需要使用 `cjc -L. -lmyfunc test.cj -o test.out` 编译命令,使仓颉编译器去链接这个库。最终就能生成想要的可执行程序。21
22
另外,在编译 C 代码时,请打开 `-fstack-protector-all/-fstack-protector-strong` 栈保护选项,仓颉侧代码默认拥有溢出检查与栈保护功能。在引入 C 代码后,需要同步保证 unsafe 块中的溢出的安全性。Code 1 · c
1
typedef void (*callback)(int);2
void set_callback(callback cb);Code 2 · cangjie
1
foreign func set_callback(cb: CFunc<(Int32) -> Unit>): UnitCode 3 · cangjie
1
@C2
func myCallback(s: Int32): Unit {3
println("handle ${s} in callback")4
}5
6
main() {7
// the argument is a function qualified by `@C`8
unsafe { set_callback(myCallback) }9
10
// the argument is a lambda with `CFunc` type11
let f: CFunc<(Int32) -> Unit> = { i => println("handle ${i} in callback") }12
unsafe { set_callback(f) }13
}v1.1.0
Section Text
1
仓颉提供 `CFunc` 类型来对应 C 侧的函数指针类型。C 侧的函数指针可以传递到仓颉,仓颉也可以构造出对应 C 的函数指针的变量传递到 C 侧。2
3
假设一个 C 的库 API 如下:4
5
6
对应的,在仓颉里面这个函数可以声明为:7
8
<!-- code_check_manual -->9
10
11
CFunc 类型的变量可以从 C 侧传递过来,也可以在仓颉侧构造出来。在仓颉侧构造 CFunc 类型有两种办法,一个是用 `@C` 修饰的函数,另外一个是标记为 CFunc 类型的闭包。12
13
`@C` 修饰的函数,表明它的函数签名是满足 C 的调用规则的,定义还是写在仓颉这边。`foreign` 修饰的函数定义是在 C 侧的。14
15
> **注意:**16
>17
> `foreign` 修饰的函数与 `@C` 修饰的函数,这两种 `CFunc` 的命名不建议使用 `CJ_`(不区分大小写)作为前缀,否则可能与标准库及运行时等编译器内部符号出现冲突,导致未定义行为。18
19
示例如下:20
21
<!-- code_check_manual -->22
23
24
假设 C 函数编译出来的库是 "libmyfunc.so",那么需要使用 `cjc -L. -lmyfunc test.cj -o test.out` 编译命令,使仓颉编译器去链接这个库。最终就能生成想要的可执行程序。25
26
另外,在编译 C 代码时,请打开 `-fstack-protector-all/-fstack-protector-strong` 栈保护选项,仓颉侧代码默认拥有溢出检查与栈保护功能。在引入 C 代码后,需要同步保证 unsafe 块中的溢出的安全性。Code 1 · c
1
typedef void (*callback)(int);2
void set_callback(callback cb);Code 2 · cangjie
1
foreign func set_callback(cb: CFunc<(Int32) -> Unit>): UnitCode 3 · cangjie
1
@C2
func myCallback(s: Int32): Unit {3
println("handle ${s} in callback")4
}5
6
main() {7
// the argument is a function qualified by `@C`8
unsafe { set_callback(myCallback) }9
10
// the argument is a lambda with `CFunc` type11
let f: CFunc<(Int32) -> Unit> = { i => println("handle ${i} in callback") }12
unsafe { set_callback(f) }13
}modified编译选项text+1 line, -1 line
v1.0.5
Section Text
1
使用 C 互操作通常需要手动链接 C 的库,仓颉编译器提供了相应的编译选项。2
3
- `--library-path <value>`, `-L <value>`, `-L<value>`:指定要链接的库文件所在的目录。4
5
`--library-path <value>` 指定的路径会被加入链接器的库文件搜索路径。另外环境变量 `LIBRARY_PATH` 中指定的路径也会被加入链接器的库文件搜索路径中,通过 `--library-path` 指定的路径会比 `LIBRARY_PATH` 中的路径拥有更高的优先级。6
7
- `--library <value>`, `-l <value>`, `-l<value>`:指定要链接的库文件。8
9
给定的库文件会被直接传给链接器,库文件名的格式应为 `lib[arg].[extension]`。10
11
关于仓颉编译器支持的所有编译选项,详情请参见 "附录 > cjc 编译选项"。v1.1.0
Section Text
1
使用 C 互操作通常需要手动链接 C 的库,仓颉编译器提供了相应的编译选项。2
3
- `--library-path <value>`, `-L <value>`, `-L<value>`:指定要链接的库文件所在的目录。4
5
`--library-path <value>` 指定的路径会被加入链接器的库文件搜索路径。另外环境变量 `LIBRARY_PATH` 中指定的路径也会被加入链接器的库文件搜索路径中,通过 `--library-path` 指定的路径会比 `LIBRARY_PATH` 中的路径拥有更高的优先级。6
7
- `--library <value>`, `-l <value>`, `-l<value>`:指定要链接的库文件。8
9
给定的库文件会被直接传给链接器,库文件名的格式应为 `lib[arg].[extension]`。10
11
关于仓颉编译器支持的所有编译选项,详情请参见[cjc 编译选项](../Appendix/compile_options.md)。modified示例text+1 line
v1.0.5
Section Text
1
这里演示如何使用 C 互操作以及 `write/read` 接口对一个结构体进行赋值和读取值。2
3
C 代码如下:4
5
6
仓颉代码如下:7
8
9
编译仓颉代码的命令如下(以 CJNative 后端为例):10
11
12
其中编译命令中 `-L .` 表示链接库时从当前目录查找(假设 `libdraw.so` 存在于当前目录),`-l draw` 表示链接的库的名字,编译成功后默认生成二进制文件 `main`,执行二进制文件的命令如下:13
14
15
运行结果如下:Code 1 · c
1
// draw.c2
#include<stdio.h>3
#include<stdint.h>4
5
typedef struct {6
int64_t x;7
int64_t y;8
} Point;9
10
typedef struct {11
float x;12
float y;13
float z;14
} Cube;15
16
void drawPicture(Point* point, Cube* cube) {17
point->x = 1;18
point->y = 2;19
printf("Draw Point finished.\n");20
21
printf("Before draw cube\n");22
printf("%f\n", cube->x);23
printf("%f\n", cube->y);24
printf("%f\n", cube->z);25
cube->x = 4.4;26
cube->y = 5.5;27
cube->z = 6.6;28
printf("Draw Cube finished.\n");29
}Code 2 · cangjie
1
// main.cj2
@C3
struct Point {4
var x: Int64 = 05
var y: Int64 = 06
}7
8
@C9
struct Cube {10
var x: Float32 = 0.011
var y: Float32 = 0.012
var z: Float32 = 0.013
14
init(x: Float32, y: Float32, z: Float32) {15
this.x = x16
this.y = y17
this.z = z18
}19
}20
21
foreign func drawPicture(point: CPointer<Point>, cube: CPointer<Cube>): Int3222
23
main() {24
let pPoint = unsafe { LibC.malloc<Point>() }25
let pCube = unsafe { LibC.malloc<Cube>() }26
27
var cube = Cube(1.1, 2.2, 3.3)28
unsafe {29
pCube.write(cube)30
drawPicture(pPoint, pCube) // in which x, y will be changed31
32
println(pPoint.read().x)33
println(pPoint.read().y)34
println(pCube.read().x)35
println(pCube.read().y)36
println(pCube.read().z)37
38
LibC.free(pPoint)39
LibC.free(pCube)40
}41
}Code 3 · shell
1
cjc -L . -l draw ./main.cjCode 4 · shell
1
LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./mainCode 5 · shell
1
Draw Point finished.2
Before draw cube3
1.1000004
2.2000005
3.3000006
Draw Cube finished.7
18
29
4.40000010
5.50000011
6.600000v1.1.0
Section Text
1
这里演示如何使用 C 互操作以及 `write/read` 接口对一个结构体进行赋值和读取值。2
3
C 代码如下:4
5
6
仓颉代码如下:7
8
<!-- code_check_manual -->9
10
11
编译仓颉代码的命令如下(以 CJNative 后端为例):12
13
14
其中编译命令中 `-L .` 表示链接库时从当前目录查找(假设 `libdraw.so` 存在于当前目录),`-l draw` 表示链接的库的名字,编译成功后默认生成二进制文件 `main`,执行二进制文件的命令如下:15
16
17
运行结果如下:Code 1 · c
1
// draw.c2
#include<stdio.h>3
#include<stdint.h>4
5
typedef struct {6
int64_t x;7
int64_t y;8
} Point;9
10
typedef struct {11
float x;12
float y;13
float z;14
} Cube;15
16
void drawPicture(Point* point, Cube* cube) {17
point->x = 1;18
point->y = 2;19
printf("Draw Point finished.\n");20
21
printf("Before draw cube\n");22
printf("%f\n", cube->x);23
printf("%f\n", cube->y);24
printf("%f\n", cube->z);25
cube->x = 4.4;26
cube->y = 5.5;27
cube->z = 6.6;28
printf("Draw Cube finished.\n");29
}Code 2 · cangjie
1
// main.cj2
@C3
struct Point {4
var x: Int64 = 05
var y: Int64 = 06
}7
8
@C9
struct Cube {10
var x: Float32 = 0.011
var y: Float32 = 0.012
var z: Float32 = 0.013
14
init(x: Float32, y: Float32, z: Float32) {15
this.x = x16
this.y = y17
this.z = z18
}19
}20
21
foreign func drawPicture(point: CPointer<Point>, cube: CPointer<Cube>): Int3222
23
main() {24
let pPoint = unsafe { LibC.malloc<Point>() }25
let pCube = unsafe { LibC.malloc<Cube>() }26
27
var cube = Cube(1.1, 2.2, 3.3)28
unsafe {29
pCube.write(cube)30
drawPicture(pPoint, pCube) // in which x, y will be changed31
32
println(pPoint.read().x)33
println(pPoint.read().y)34
println(pCube.read().x)35
println(pCube.read().y)36
println(pCube.read().z)37
38
LibC.free(pPoint)39
LibC.free(pCube)40
}41
}Code 3 · shell
1
cjc -L . -l draw ./main.cjCode 4 · shell
1
LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./mainCode 5 · shell
1
Draw Point finished.2
Before draw cube3
1.1000004
2.2000005
3.3000006
Draw Cube finished.7
18
29
4.40000010
5.50000011
6.600000