Wednesday, June 1, 2011

Getting Started with SWIG

Creating JNI by hand can be pretty challenging especially if we intend to wrap a large C++ code base. In such scenario, SWIG can dramatically reduce the effort of creating the JNI wrapper. There is difference between using javah and swig tools. Swig is useful for creating JNI from C++ while javah is useful for creating JNI from Java.

Hello.h
#ifndef HELLO_H_
#define HELLO_H_

#include <string>
#include <stdint.h>

class Hello {
public:
    Hello();
    virtual ~Hello();
    void sayHello(const std::string& name) const;
    uint32_t getNumber() const;
    const char* getMessage() const;
};

#endif /* HELLO_H_ */

Hello.cpp
#include <iostream>
#include "Hello.h"
using namespace std;

Hello::Hello() {
}

Hello::~Hello() {
}

void Hello::sayHello(const std::string& name) const {
    cout << "Hello, " + name << endl;
}

uint32_t Hello::getNumber() const {
    return 10;
}

const char* Hello::getMessage() const {
    return "Hello";
}

SWIG requires an interface file.
Hello.i
%module JniHello
%{
#include "Hello.h"
typedef unsigned int uint32_t;
%}

%include "std_string.i"
%include "Hello.h"

typedef unsigned int uint32_t;
As we can see here, there is %module verbatim that indicates the module name. Everything in the %{..%} block will be copied to the resulting wrapper file created by SWIG. Here we use include std_string.i library, which will convert std::string to java.lang.String. Without including it, only char* will be converted to java.lang.String and std::string will be a pointer. We can also define a typedef to tell that uint32_t is basically an unsigned int. As a matter of fact, there is stdint.i library that can help to deal with stdint type mapping, but for this tutorial, let's keep it as it is.

Now let's compile the C++ programs and create shared libraries.
swig -c++ -java Hello.i

g++ -c -fpic Hello.cpp
g++ -shared Hello.o -o libhello.so

g++ -c -fpic Hello_wrap.cxx -I$JAVA_HOME/include -I$JAVA_HOME/include/linux
g++ -shared Hello_wrap.o -o libjnihello.so libhello.so

Test it out with a simple Java program. Make sure to add -Djava.library.path or set LD_LIBRARY_PATH to point to the directory where libhello.so and libjnihello.so are located.
Main.java
public class Main {
    static {
        try {
            System.loadLibrary("jnihello");
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Hello hello = new Hello();
        hello.sayHello("Foo");
        System.out.println(hello.getMessage());
        System.out.println(hello.getNumber());
    }
}

The output is:
Hello, Foo
Hello
10

No comments:

Post a Comment