Kotlin Navtive Dynamic Libraries

上一篇主要说了如果通过编写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:

  1. macOS: demo_api.h and libdemo.dylib
  2. Linux: demo_api.h and libdemo.so
  3. Windows: demo_api.h, demo_symbols.def and demo.dll

如下图,执行完刚才的命令就可以得到如下的文件,由于我是在linux平台下编译的,所以会生成 demo_api.h的C语言的头文件和一个so动态库。


生成的文件

这样就可以在linux平台下面去使用这个动态库,下面是在linux上使用eclipse创建的C语言工程去调用刚才生成的动态库,首先你先要有一个eclipse,这里就不再附赘,这里主要想说一下如何去调用刚才的动态库。

2. 调用kotlin生成的动态库

  1. 在C Compiler选项下的includes选项下的include paths增加刚才编译好的动态库的文件夹路径:


    添加动态库路径

    动态库所在目录
  2. 这里只是把头文件加入了编译,还需要把so库引用加入,继续设置, 在C Linker 的 Miscellaneous的 Other objects下增加对libdemo.so库的引用:
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代码调用的过程。

你可能感兴趣的:(Kotlin Navtive Dynamic Libraries)