Kotlin JVM JNI using SWIG Arrays and Pointers

Submitted by Dickens A S on Mon, 07/19/2021 - 09:28

Based on Article kotlin-jni-swig-gradle-cpp

This Article demonstrates how to pass pointers and arrays in and out using pure C code and JNI and force pure C code in Gradle

GitHub: https://github.com/dickensas/kotlin-gradle-templates/tree/master/swig-jni-cpointer

The Gradle SWIG Task of cpplib

val swigTask: Exec by tasks.creating(Exec::class) {
	commandLine (
		"swig",
		"-java",
		"-cppext",
		"c",
		"-addextern",
		"-module",
		"${project.name}",
		"src\\main\\swig\\${project.name}.i"
	)
}

The Gradle C Compiler Arguments (-lc -x c -std=gnu99) forces the g++ to use gcc compiler as pure C compiler

tasks.withType(CppCompile::class.java).configureEach {
    dependsOn(swigTask)
    doFirst {
        copy {
            from("${project.rootDir}/${project.name}/src/main/swig") {
                rename("${project.name}_wrap.c", "${project.name}_wrap.cpp")
            }
            into("${project.rootDir}/${project.name}/src/main/cpp")
            exclude("*.java")
            exclude("*.i")
        }
    }
    compilerArgs.addAll(toolChain.map { toolChain ->
        when (toolChain) {
            is Gcc, is Clang -> listOf(
                    "-c",
                    "-v",
                    "-x",
                    "c",
                    "-fpic",
                    "-std=gnu99",
                    "-Wall",
                    "-Werror",
                    "-Wno-unused",
                    "-nodefaultlibs",
                    "-lc",
                    "-g",
                    "-I/usr/java/include",
                    "-I$javaHome/include",
                    "-I$javaHome/include/win32"
            )
            else -> listOf()
        }
    })
}

Gradle C Linker Arguments

tasks.withType(LinkSharedLibrary::class.java).configureEach {
    linkerArgs.addAll(toolChain.map { toolChain ->
        when (toolChain) {
            is Gcc, is Clang -> listOf(
                "-v",
                "-g",
                "-Wl,--out-implib=${project.name}.dll.a", 
                "-Wl,--export-all-symbols",
                "-Wl,--enable-auto-import",
                "-Wl,--output-def=${project.name}.def"
            )
            else -> listOf()
        }
    })
    doLast {
        copy {
            from("${project.rootDir}/${project.name}/build/lib/main/debug")
            into("${project.rootDir}")
            exclude("*.java")
            exclude("*.i")
            exclude("*.obj")
        }
        
        copy {
            from("${project.rootDir}/${project.name}/src/main/swig")
            into("${project.rootDir}/src/main/java")
            exclude("*.i")
            exclude("*.cpp")
            exclude("*.c")
        }
        
        copy {
            from("${project.rootDir}/${project.name}/${project.name}.dll.a")
            into("${project.rootDir}")
        }
        
        copy {
            from("${project.rootDir}/${project.name}/${project.name}.def")
            into("${project.rootDir}")
        }
    }
}

this code is written generic based on the folder name of the sub project is assumed as the DLL file name

These gradle tasks plays the important role, otherwise you need to manually copy the files

The Kotlin Code

import cpplibJNI.*

class App {
    fun demo() {
        //Example 1
        var a = new_intp()
        var b = new_intp()
        var c = new_intp()
        
        intp_assign(a,37)
        intp_assign(b,42)
        
        println("calling C add function")
        
        add(a,b,c)
        
        var res = intp_value(c)
        println("printing result")
        println("37 + 42 = " + res)
        
        //Example 2
        var a1 = new_doubleArray(10)
        
        for( i in 0..10){
            doubleArray_setitem(a1, i, 2.0 * i)
        }
        
        print_array(a1)
        
        delete_doubleArray(a1)
        
        //Example 3
        var x1 = new_doubleArray(10)
        for( i in 0..10){
            doubleArray_setitem(x1, i, 2.0 * i)
        }
        
        modify_array(x1)
        
        print_array(x1)
        
        delete_doubleArray(x1)
        
        //Example 4
        var x2 = new_doubleArray(10)
        var y2 = new_doubleArray(10)
        for( i in 0..10){
            doubleArray_setitem(x2, i, 2.0 * i)
        }
        
        copy_array(x2, y2)
        
        print_array(y2)
        
        delete_doubleArray(x2)
        delete_doubleArray(y2)
        
    }
        
    companion object {
        init {
            System.loadLibrary("cpplib")
        }
    }
}

fun main(args: Array<String>) {
    var result = App().demo();
}

The C code (but stored as .cpp) 

#include "cpplib.h"
#include <stdlib.h>
#include <stdio.h>

void add(int *x, int *y, int *result) {
  *result = *x + *y;
}

void print_array(double x[10]) {
  printf("\nprint_array\n");
  int i;
  for (i = 0; i < 10; i++) {
    printf("[%d] = %g\n", i, x[i]);
  }
}

void modify_array(double x[10]) {
  printf("\nmodify_array\n");
  int i;
  for (i = 0; i < 10; i++) {
    x[i] = x[i] * 3.0;
  }
}

void copy_array(double x[10], double y[10]) {
  printf("\ncopy_array\n");
  int i;
  for (i = 0; i < 10; i++) {
    y[i] = x[i];
  }
}

The SWIG Code

%include "cpointer.i"
%include "carrays.i"
%pointer_functions(int, intp);
%array_functions(double, doubleArray);

%{
#include "../public/cpplib.h"
%}

%include "../public/cpplib.h"

Final Output

> Task :run
calling C add function
printing result
37 + 42 = 79

print_array
[0] = 0
[1] = 2
[2] = 4
[3] = 6
[4] = 8
[5] = 10
[6] = 12
[7] = 14
[8] = 16
[9] = 18

modify_array

print_array
[0] = 0
[1] = 6
[2] = 12
[3] = 18
[4] = 24
[5] = 30
[6] = 36
[7] = 42
[8] = 48
[9] = 54

copy_array

print_array
[0] = 0
[1] = 2
[2] = 4
[3] = 6
[4] = 8
[5] = 10
[6] = 12
[7] = 14
[8] = 16
[9] = 18

Finished!

Add new comment