How to use general C/C++ code in KMP common source set?

Viewed 631

I'm building an app for Android and iOS and I want to reuse as much code as possible. I have some generic C code (an algorithm) that doesn't include any system library. Is it possible to expose it to my common Kotlin source set using cinterop or any other tool?

My build.gradle.kts:

plugins {
    id("com.android.library")
    kotlin("multiplatform")
    kotlin("native.cocoapods")
}

android {
    compileSdkVersion(29)

    defaultConfig {
        minSdkVersion(21)
        targetSdkVersion(29)
    }

    sourceSets.all {
        manifest.srcFile("src/androidMain/AndroidManifest.xml")
        java.srcDirs("src/androidMain/java")
        res.srcDirs("src/androidMain/res")
    }
}

version = "1.0"

kotlin {
    android()
    ios()

    cocoapods {
        // Configure fields required by CocoaPods.
        summary = "..."
        homepage = "..."

        // You can change the name of the produced framework.
        // By default, it is the name of the Gradle project.
        frameworkName = "SharedModule"
    }

    // Workaround for ios platform imports to work on Android Studio
//    iosX64("ios")

    sourceSets["commonMain"].dependencies {
        implementation("org.jetbrains.kotlin:kotlin-stdlib-common")
    }

    sourceSets["androidMain"].dependencies {
        implementation("org.jetbrains.kotlin:kotlin-stdlib")
    }

    sourceSets.all {
        languageSettings.progressiveMode = true
    }
}
2 Answers

Not automatically. On the JVM side you'll need to use JNI to talk to the C code, and on the native side you'll need to use Kotlin cinterop. JNI and Kotlin cinterop will have an interface to the C code that's very similar to each other, but not the same. To expose that to common code, you'll need to write a common API layer that delegates to JNI code on the JVM and Kotlin cinterop code on native.

Wrapping very similar platform-specific API's is pretty straightforward once you get used to it. Ideally, you could automatically wrap them, but right now you can't. I gave a talk that discusses some techniques for this: https://vimeo.com/371460823

Below follows a simple example of how to call c code within Kotlin:

cCaller.kt

class cCaller {
  init {
    System.loadLibrary("cCode")
  }  external fun callCFunction()
}

In your code:

fun main() {
  cCaller().callCFunction()
}

cCode.c

#include <stdio.h>
#include "cCaller.h"JNIEXPORT void JNICALL Java_cCaller_callCFunction(JNIEnv *env, jobject obj) {
  // YOUR CODE HERE
  return;
}

Notice that the function callCFunction is prefixed with Java_ and cCaller_ Also notice the #include "cCaller.h" added to the second line of cCode.c - we need to create this file:

cCaller.h

#include <jni.h>#ifndef _Included_NativeSample
#define _Included_NativeSample
#ifdef __cplusplus
extern "C" {
#endifJNIEXPORT void JNICALL Java_cCaller_callCFunction(JNIEnv *, jobject);#ifdef __cplusplus
}
#endif
#endif

Compile

gcc cCode.c -o libcCode.so -shared -fPIC -I <jdk_path>/include -I <jdk_path>/include/linux
Related