Sibi
Sibi

Reputation: 48734

Making SWIG understand char** for using it in Java

I have following three files:

sab1.hpp:

class CRectangle 

{
    int x, y;

public:
    void set_values (int,int);
    void print(char **db);
    int area ();
};

sab1.cpp:

#include <iostream>
#include "sab1.hpp"
using namespace std;

int CRectangle::area () 
{
    return (x*y);
}

void CRectangle::set_values (int a, int b) 
{
    x = a;
    y = b;
}

void CRectangle::print(char **db) 
{
    cout <<db[0];
}

int main () 
{
    CRectangle rect;
    char *a[] = {"Hi", "bye" }  ;

    rect.set_values (3,4);
    cout << "area: " << rect.area();
    rect.print(&a[0]);
    return 0;
}

sab1.i :

%module Rec
%{
/* Includes the header in the wrapper code */
#include "sab1.hpp"
%}
class CRectangle {
    int x, y;
public:
    void set_values (int,int);
    void print(char **db);
    int area ();
};

I create various intermediary file using the following usual commands:

1) swig -c++ -java sab1.i

2) g++ -fpic -c sab1.cpp sab1_wrap.cxx -I /usr/lib/jvm/java-6-openjdk-i386/include/ -I /usr/lib/jvm/java-6-openjdk-i386/include/linux/

Then I create a shared library using this: g++ --shared sab1.o sab1_wrap.o -o libegs.so

Then I create a following Java file for accessing the functions of the C++ class:

public class Test 
{
    public static void main(String args[]) 
    {
        System.loadLibrary("egs");
        CRectangle a = new CRectangle();
        a.set_values(4,5);
        System.out.println(a.area());
        a.print("Hi");
    }
}

When I compile the Java File (javac -classpath . Test.java ), I get the following error:

Test.java:17: print(SWIGTYPE_p_p_char) in CRectangle cannot be applied to (java.lang.String)  a.print("Hi");
 ^

So, how can I make the Java code understand the char ** variable? I have tried creating typemaps in the SWIG interface file (sab1.i) but I'm not able to correctly figure it out.

Thanks in advance for the help.

Upvotes: 2

Views: 2303

Answers (1)

Oktalist
Oktalist

Reputation: 14714

%module Rec
%include "various.i"

%apply char **STRING_ARRAY { char **db }

%include "sab1.hpp"

The file various.i which you linked to should work this way, just put it somewhere where SWIG can find it, in your project include directory or SWIG include path.

Using %include instead of #include "sab1.hpp" means we shouldn't have to copy the definition of classes we want to wrap. The %apply directive applies the typemap from various.i to all arguments of char** type with the name db. If there are other argument names you want to wrap, add them like this:

%apply char **STRING_ARRAY { char **db, char **foo, char **bar }

If you just want to wrap all argument of type char** (unsafe!), simply omit the name:

%apply char **STRING_ARRAY { char ** }

As your C++ code expects the array to contain 2 strings, then as long as you pass an array of 2 or more Strings from Java, it should be fine. The typemap will add a third null pointer to the array, but your C++ code won't notice. If you pass an array with fewer than 2 Strings, you will likely crash your C++ code.

If any of the C++ functions you are wrapping also returns an array of strings, then you are in trouble. SWIG will attempt to convert those back into Java arrays, but may crash when it looks for a third element in the array (it will keep looking for elements until it finds a null pointer). If you don't mind discarding the return value, you can do this:

%module Rec
%include "various.i"

%typemap(jni)     char **db = char **STRING_ARRAY;
%typemap(jtype)   char **db = char **STRING_ARRAY;
%typemap(jstype)  char **db = char **STRING_ARRAY;
%typemap(in)      char **db = char **STRING_ARRAY;
%typemap(freearg) char **db = char **STRING_ARRAY;
%typemap(javain)  char **db = char **STRING_ARRAY;

%include "sab1.hpp"

This will only apply the parts of the typemap that deal with input arguments.

Upvotes: 5

Related Questions