上一篇主要说了如果通过编写Kotlin代码编译成各个平台的可执行文件,这一篇介绍如何通过Kotlin Native编译成各平台的动态或者静态库,以供其他语言去调用。当然,Kotlin也可以调用C语言编写的静态库,或者直接引用C语言代码。
官网地址 kotlin navtive dynamic libraries
1. 首先让我们编写一个lib.kt文件:
object Object {
val field = "A"
}
class Clazz {
fun memberFunction(p: Int): Long = 42L
}
fun forIntegers(b: Byte, s: Short, i: Int, l: Long) { }
fun forFloats(f: Float, d: Double) { }
//自己添加的方法
fun add(a: Int, b: Int): Int {
return a + b
}
fun sub(a: Int, b: Int): Int {
return a - b
}
fun strings(str: String) : String? {
return "That is '$str' from C"
}
val globalString = "A global String"
当然这段代码是摘自官网教程,只不过我增加了自己写的add和sub方法。
然后只需要在lib.kt所在的文件夹下敲入以下命令:
kotlinc-native lib.kt -produce dynamic -output demo
这样就可生成对应的动态库文件,当然在不同的平台下生成的文件不同,如下:
The kotlinc-native (with v0.9.2) generates the following files, depending on the host OS:
- macOS: demo_api.h and libdemo.dylib
- Linux: demo_api.h and libdemo.so
- Windows: demo_api.h, demo_symbols.def and demo.dll
如下图,执行完刚才的命令就可以得到如下的文件,由于我是在linux平台下编译的,所以会生成 demo_api.h的C语言的头文件和一个so动态库。
这样就可以在linux平台下面去使用这个动态库,下面是在linux上使用eclipse创建的C语言工程去调用刚才生成的动态库,首先你先要有一个eclipse,这里就不再附赘,这里主要想说一下如何去调用刚才的动态库。
2. 调用kotlin生成的动态库
-
在C Compiler选项下的includes选项下的include paths增加刚才编译好的动态库的文件夹路径:
- 这里只是把头文件加入了编译,还需要把so库引用加入,继续设置, 在C Linker 的 Miscellaneous的 Other objects下增加对libdemo.so库的引用:
至此动态库的引用已经添加完毕,我们现在就可以在C代码里调用刚才用kotlin提供的方法了,但是在这个之前,我们还是看一看demo_api.h这个头文件里面都有哪些东西:
#ifndef KONAN_DEMO_H
#define KONAN_DEMO_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
typedef bool demo_KBoolean;
#else
typedef _Bool demo_KBoolean;
#endif
typedef unsigned short demo_KChar;
typedef signed char demo_KByte;
typedef short demo_KShort;
typedef int demo_KInt;
typedef long long demo_KLong;
typedef unsigned char demo_KUByte;
typedef unsigned short demo_KUShort;
typedef unsigned int demo_KUInt;
typedef unsigned long long demo_KULong;
typedef float demo_KFloat;
typedef double demo_KDouble;
typedef void* demo_KNativePtr;
struct demo_KType;
typedef struct demo_KType demo_KType;
typedef struct {
demo_KNativePtr pinned;
} demo_kref_Object;
typedef struct {
demo_KNativePtr pinned;
} demo_kref_Clazz;
typedef struct {
/* Service functions. */
void (*DisposeStablePointer)(demo_KNativePtr ptr);
void (*DisposeString)(const char* string);
demo_KBoolean (*IsInstance)(demo_KNativePtr ref, const demo_KType* type);
/* User functions. */
struct {
struct {
void (*forIntegers)(demo_KByte b, demo_KShort s, demo_KInt i, demo_KLong l);
void (*forFloats)(demo_KFloat f, demo_KDouble d);
demo_KInt (*add)(demo_KInt a, demo_KInt b);
demo_KInt (*sub)(demo_KInt a, demo_KInt b);
const char* (*strings)(const char* str);
const char* (*get_globalString)();
struct {
demo_KType* (*_type)(void);
demo_kref_Object (*_instance)();
const char* (*get_field)(demo_kref_Object thiz);
} Object;
struct {
demo_KType* (*_type)(void);
demo_kref_Clazz (*Clazz)();
demo_KLong (*memberFunction)(demo_kref_Clazz thiz, demo_KInt p);
} Clazz;
} root;
} kotlin;
} demo_ExportedSymbols;
extern demo_ExportedSymbols* demo_symbols(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* KONAN_DEMO_H */
因为在C语言中没有对象的概念,所以kotlin生成的方法都是以结构体的形式存在的,我们可以看到刚才在生成头文件的时候是以demo开头的,所以头文件里面的命名都是demo开头的,并且C语言也没有包的概念,如果一个函数同名,那就无法区分,所以就以加前缀的方式来区分。
更具体的分析我们可以查阅官网资料,下面我们来看如果调用这些方法:
demo_ExportedSymbols* lib = demo_symbols();
//use C and Kotlin/Native strings
const char* str = "Hello from Native!";
const char* response = lib->kotlin.root.strings(str);
printf("in: %s\nout:%s\n", str, response);
lib->DisposeString(response);
printf("调用kotlin的add 方法 %d\n", lib->kotlin.root.add(2, 1));
printf("调用kotlin的sub 方法 %d\n", lib->kotlin.root.sub(1, 2));
我们分析头文件可以看出如果我们想要调用刚才kotlin编写的方法,我们必须先创建demo_ExportedSymbols这个结构体,所以我们就先创建出来:
demo_ExportedSymbols* lib = demo_symbols();
然后我们尝试调用 strings这个方法:
//use C and Kotlin/Native strings
const char* str = "Hello from Native!";
const char* response = lib->kotlin.root.strings(str);
printf("in: %s\nout:%s\n", str, response);
lib->DisposeString(response);
当然使用完是要释放掉这个内存的。
然后我们尝试调用add 和 sub方法:
printf("调用kotlin的add 方法 %d\n", lib->kotlin.root.add(2, 1));
printf("调用kotlin的sub 方法 %d\n", lib->kotlin.root.sub(1, 2));
我们看一下运行结果:
仔细看还会发现在lib.kt里面还定义了类,在C中都会被翻译成结构体。
以上就是参照官网教程实现的简单的使用kotlin native实现编程成为动态库,供C代码调用的过程。