4

I'm having difficulty getting the SWIG typemap(javapackage) to work properly. I tried making a simple version of the problem, and even that seems to fail.

foo.h:

#ifndef FOO_H
#define FOO_H

class Foo
{
public:
    Foo() {};
    int doSomething() { return 1 };
};

#endif

bar.h:

#ifndef BAR_H
#define BAR_H

#include "foo.h"

class Bar
{
public:
    Bar() {};
    int doSomething(Foo foo) { return foo.doSomething(); };
};

#endif

Foo.i

%module FooMod

%include "typemaps.i"
%include "stdint.i"

%{
#include "../header/foo.h"
%}

%include "../header/foo.h"

Bar.i

%module BarMod

%import "Foo.i"

%typemap("javapackage") Foo, Foo *, Foo & "com.me.t.foo";

%include "typemaps.i"
%include "stdint.i"

%{
#include "../header/bar.h"
%}

%include "../header/bar.h"

Running these with this the following commands:

swig -c++ -java -package com.me.t.foo -outdir ../../src/com/me/t/foo -o ../src/Foo.cpp Foo.i
swig -c++ -java -package com.me.t.bar -outdir ../../src/com/me/t/bar -o ../src/Bar.cpp Bar.i

And I get this output:

package com.me.t.bar;

public class Bar {
  private long swigCPtr;
  protected boolean swigCMemOwn;

  protected Bar(long cPtr, boolean cMemoryOwn) {
    swigCMemOwn = cMemoryOwn;
    swigCPtr = cPtr;
  }

  protected static long getCPtr(Bar obj) {
    return (obj == null) ? 0 : obj.swigCPtr;
  }

  protected void finalize() {
    delete();
  }

  public synchronized void delete() {
    if (swigCPtr != 0) {
      if (swigCMemOwn) {
        swigCMemOwn = false;
        BarModJNI.delete_Bar(swigCPtr);
      }
      swigCPtr = 0;
    }
  }

  public Bar() {
    this(BarModJNI.new_Bar(), true);
  }

  public int doSomething(Foo foo) {
    return BarModJNI.Bar_doSomething(swigCPtr, this, Foo.getCPtr(foo), foo);
  }

}

BarModJNI.java:

package com.me.t.bar;

public class BarModJNI {
  public final static native long new_Bar();
  public final static native int Bar_doSomething(long jarg1, Bar jarg1_, long jarg2, Foo jarg2_);
  public final static native long Bar_getFoo(long jarg1, Bar jarg1_);
  public final static native void delete_Bar(long jarg1);
}

The files are generated properly, but notice that there is no import statement, so Foo can't be found from either of the Bar Java classes. This is a simple example, but just hard-coding an import statement isn't an option for me since the generated source files containing the C JNI code might have the wrong locations of the "Foo" class files.

This seems like a very simple and common problem, so, what I'm wondering is if I'm missing something or if I'm doing something wrong.

Thanks for the help!

gninja
  • 343
  • 1
  • 3
  • 10

3 Answers3

4

Got the same problem and found answer, so posting it for community.

You need to make 3 changes.

  1. Add import statements to generated proxy class (Bar.java):

    // Bar.i
    %pragma(java) jniclassimports=%{
    import com.me.t.foo.Foo;
    %}
    
  2. Add import statements to generated JNI wrapper class (BarModJNI.java):

    // Bar.i
    %typemap(javaimports) Bar %{
    import com.me.t.foo.Foo;
    %}
    
  3. Tell SWIG to make Foo.getCPtr a public member variable because Bar class will want to access it:

    // Foo.i
    SWIG_JAVABODY_PROXY(public, public, SWIGTYPE)
    SWIG_JAVABODY_TYPEWRAPPER(public, public, public, SWIGTYPE)
    

Reference:

Zbigniew Zagórski
  • 1,921
  • 1
  • 13
  • 23
1

Just a couple of comments on the answer provided by Zbigniew. I have faced the same problem described in this post. I would like to clarify two points.

Firstly, it seems to me that step 1 is for adding the import in JNI wrapper class (whateverJNI.java) while step 2 is for adding import to the specific class.

Secondly, step 2 is not working for me, instead of %typemap(javaimports) <class> I had to use %typemap(javaimports) SWIGTYPE. Bad thing is it adds the imports to all the generated java classes and not only to the desired one.

Finally, I am still having the problem that SWIG does not recognize the imported class when wrapping the specific type and it still uses SWIGTYPE_<type> instead of directly <type>

Bernat
  • 379
  • 1
  • 2
  • 12
0

I have faced the same issue. I solved by writing (You need to write inside .i file)

%typemap(javaimports) namespace::classname
%{
import com.yourcompany.yourapp.*;
%}

This code generates import statement only the class that you have specified.
CAUTION: You must specify the native namespace, otherwise your class will not be seen!

Example: If your C++ Code would be like this:

namespace A{  
class MyPerfectClass{

};  
}

You need to write

%typemap(javaimports) A::MyPerfectClass
%{
import com.yourcompany.yourapp.*;
%}

If you want to add this import statement to all java wrapped classes, you need to write

%typemap(javaimports) SWIGTYPE
%{
import com.yourcompany.yourapp.*;
%}

For JNI Java Class:

%pragma(java) jniclassimports=%{
import com.yourcompany.yourapp.*;
%}

The proof code is here:

package com.mycompany.myproject.A_package;

import com.mycompany.myproject.B_package.*;

public class MYCLASS{
  private transient long swigCPtr;
  protected transient boolean swigCMemOwn;

  public MYCLASS(long cPtr, boolean cMemoryOwn) {
    swigCMemOwn = cMemoryOwn;
    swigCPtr = cPtr;
  }

  public static long getCPtr(MYCLASSobj) {
    return (obj == null) ? 0 : obj.swigCPtr;
  }
  .
  . etc.

If you have more than one class, you can continue like:

%typemap(javaimports) A::MyPerfectClass , A::B::MyFantasticClass , MyNoNamespaceClass 

I've tested with SWIG 4.0 and it works.
I've changed package and class names on proof code because I cannot share my company and class name. I hope this answer would be helpful for others who will have the same issue.

astarakastara
  • 475
  • 5
  • 17