«

Dec 04

Using SWIG

Using SWIG

 

 

SWIG – Simplified Wrapper and Interface Generator:

 

When writing native code in our Android project, we need to set up the “glue” code between java and C++, this “glue” code is the JNI code.

I wrote This JNI Article about JNI in Android, the problem with writing JNI code is that it is very complex and hard to code.

SWIG is a software development tool that enables you, instead of writing the JNI code yourself, to write a script for the SWIG tool, and SWIG will automatically generate all the JNI glue you need, so you won’t have to write the complex and hard JNI code.

SWIG is not just for Android, but it connects all sort of languages to c++, but we will use it to connect Java and C++.

In this article I will not get into details on how to code and use SWIG, but I will explain the part that we need – how to use SWIG to connect the native code of our CrossPlatform application to the java code.

You can read more about SWIG in its official site:

http://www.swig.org/

 

1. Let us generate the SWG files:

 

SWG files in swig define a module, and each module contains SWIG script that instructs SWIG on how to generate the JNI module code.

The *.swg files that define a module will call one or more *.i files which is a SWIG script that instructs SWIG on how to generate the JNI code for a specific class.

 

The *.SWG files in our project:

 

CrossPlatformProjectExample_Common.swg

This is the common swg file, it doesn’t define a module by itself, but it will be included from all other swg files,

 

// SWIG generates the java code constructors with the default visibility level of protected
// however when we take dependency on a class of one module from a class of another module, we need
// the constructor to have public visibility, this code sets the default creation of the java proxy
// code constructors to public
#define SWIG_SHARED_PTR_TYPEMAPS(CONST, TYPE...) SWIG_SHARED_PTR_TYPEMAPS_IMPLEMENTATION(public, public, CONST, TYPE)
SWIG_JAVABODY_PROXY(public, public, SWIGTYPE)
SWIG_JAVABODY_TYPEWRAPPER(public, public, public, SWIGTYPE)

// SWIG include files that can be found in the SWIG directory under: Lib\java\ and under: Lib\
%include <std_string.i>
%include <std_wstring.i>
%include <std_shared_ptr.i>
%include <stdint.i>

// Some definitions necessary to detect std::string correctly.
%apply const std::string & {std::string &};
using namespace std;

// Forward declaration of the namespace.
namespace LIOR_TEST {}

// We add the android.util.Log in order to add logging capabilities
// so we will be able to write the Log.e("Lior..." in the jniclasscode following
%pragma(java) jniclassimports=%{
import android.util.Log;
%}

// Add a static block to the generated java JNI class of each module to load the libCrossPlatformProjectExample lib.
// Calling loadLibrary repeatedly is allowed http://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html#loadLibrary%28java.lang.String%29
%pragma(java) jniclasscode=%{
    static {
        try {
            System.loadLibrary("CrossPlatformProjectExample");
        } catch (UnsatisfiedLinkError e) {
            Log.e("Lior", "Lior: Native library failed to load: " + e);
            System.exit(1);
        }
    }
%}

// Define using of shared_ptr classes
%shared_ptr(LIOR_TEST::Adder);
%shared_ptr(LIOR_TEST::Calculator);

 

CrossPlatformProjectExample_Resolver.swg

 

This is the swg file that will be used to generate the resolver module – the module that resolves all our c++ modules (classes that represent a functional module in our application).

 

%include "CrossPlatformProjectExample_Common.swg"

%module CrossPlatformProjectExample_Resolver

// This will inject the import to the generated java JNI class CrossPlatformProjectExample_ResolverJNI
%pragma(java) jniclassimports=%{
import android.util.Log;

import zivi.lior.crossplatformprojectexample.swigAutoGeneratedClasses.Calculator.Calculator;
%}

// Include the .i files ordered by dependencies, the bottom depends on the top.
%include "Resolver/ModulesResolver.i"

 

CrossPlatformProjectExample_Calculator.swg

 

This is the swg file that will be used to generate the Calculator module.

 

%include "CrossPlatformProjectExample_Common.swg"

%module CrossPlatformProjectExample_Calculator

// Include the .i files ordered by dependencies, the bottom depends on the top.
%include "Calculator/Adder.i"
%include "Calculator/Calculator.i"

 

The *.i files in our project:

 

ModuleResolver.i:

 

// This will inject the import to the generated java class ModulesResolver
%typemap(javaimports) LIOR_TEST::ModulesResolver
%{
import zivi.lior.crossplatformprojectexample.swigAutoGeneratedClasses.Calculator.Calculator;
%}

%{
#include <Resolver/ModulesResolver.hpp>
%}

// When we add %import statement SWIG will know about classes that are generated in different modules
// So since we take dependency on these classes, SWIG won't create new proxy classes for them.
%import <Calculator.hpp>

%include <Resolver/ModulesResolver.hpp>

 

Calculator.i:

 

%{
#include <Calculator.hpp>
%}

%include <Calculator.hpp>

 

Adder.i:

 

%{
#include <Adder.hpp>
%}

%include <Adder.hpp>

 

Now that we have the SWIG code that instructs how to generate the JNI code,

We will:

2. Integrate SWIG with the Gradle build system:

In the build.gradle file add:

 

//**********************************************************************************************
    // Setup the SWIG generation

    def swigDir = properties.getProperty('swig.dir')
    def swigCompleteExecPath = swigDir.toString() + '/swig'

    task generateSWIGFiles {

        // In relation to where this gradle is run: <ROOT>\Android\CrossPlatformProjectExample\app
        ext.srcSwigDir = file('swig')
        ext.srcIncDir = file(RootFolder + '/XPlat/Code/inc')
        ext.destSwigGenerateJavaDir = file('src/main/java/zivi/lior/crossplatformprojectexample/swigAutoGeneratedClasses')
        // This path can not be too long, this is already almost at the max length for the path, it won't compile if it is too long
        ext.destSwigGenerateJNIDir = file(RootFolder + '/XPlat/BuildSupport/Android/SwigOut')

        // Define the inputs and outputs of this task so Gradle can skip this task when they are up to date
        inputs.dir(srcSwigDir)
        inputs.dir(srcIncDir)
        outputs.dir(destSwigGenerateJavaDir)
        outputs.dir(destSwigGenerateJNIDir)

        doLast {

            // Delete all old JNI generated files and make sure the output folder exists
            delete destSwigGenerateJNIDir.absolutePath
            destSwigGenerateJNIDir.mkdirs()

            // Delete all old SWIG Java generated files
            delete destSwigGenerateJavaDir.absolutePath

            // Execute SWIG.exe on every sub-package
            def subPackages = ['Resolver', 'Calculator']
            subPackages.each { subPackage ->
                exec {

                    // Make sure the Java sub-package folder exists
                    def destSwigGenerateJavaSubPackageDir = new File(destSwigGenerateJavaDir, subPackage)
                    destSwigGenerateJavaSubPackageDir.mkdirs()

                    commandLine swigCompleteExecPath,
                            '-java',
                            '-c++',
                            '-w516', // Swig can't use move semantics for Java, This flags sets to ignore warnings on this issue
                            '-package', 'zivi.lior.crossplatformprojectexample.swigAutoGeneratedClasses.' + subPackage,
                            '-I' + srcIncDir.absolutePath,
                            '-outdir', destSwigGenerateJavaSubPackageDir.absolutePath,
                            '-o', destSwigGenerateJNIDir.absolutePath + '/' + subPackage + '_jni_wrapper.cpp',
                            srcSwigDir.absolutePath + '/CrossPlatformProjectExample_' + subPackage + '.swg'
                }
            }
        }
    }

 
 

That is all for now…

 

If you followed all of my XPlat articles you now have a the knowledge to create the skeleton for your XPlat project.

 

Here is a link to download my complete example project:

Link to download the example project

 

There are more articles I would like to write like:

Debugging c++ using VisualGDB

Creating modules in c++ using module injection

Creating modules in java using Dagger

About shared_ptr and using it in c++ / SWIG

Defining callbacks to c++ methods using SWIG

Creating java listener classes to c++ events using SWIG

Saving to local storage from c++ using java PALS

Using common resource files for all environments

And many more…

 

Hopefully I will get the time, but as always if you have any question please don’t hesitate to comment or write me a personal message.

 
 

As always,

Good Luck !

Leave a Reply

Your email address will not be published.

אתם יכולים להשתמש באפשרויות ותגי ה-HTMLהבאים: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>