Link Java and C ++ code with SWIG

About this article

I will introduce how to call C ++ code from Java using SWIG with sample code. In this article, I will write from hello world to class definition and simple STL usage.

Sample code: https://github.com/TkrUdagawa/swig_java_sample

What is SWIG?

An abbreviation for Simplified Wrapper and Interface Generator, a tool that generates wrappers that can call code written in C / C ++ from other languages. In addition to Java, scripting languages such as Python, Ruby, and PHP are also supported.

Official page: http://www.swig.org/

Execution environment at hand

SWIG installation

The latest version at the time of writing is 4.0.0, which was released at the end of April 2019. Looking at the release notes, it seems that C ++ 11 STL container is added in 4.0.0, so it seems good to install 4.0.0 even if it is a little troublesome.

download

Download from Download page of official website.

Installation

Extract the downloaded file and install it with configure, make

$ tar xvfz swig-4.0.0.tar.gz
$ cd swig-4.0.0
$ ./configure
$ make
$ make install

sample

  1. Hello world First, the simplest example. Create a sample that calls a function defined in C ++ from Java. The following three files are prepared by yourself
├── Main.java
├── hello.cpp
└── hello.i

Preparation on the C ++ side

First, prepare the C ++ code that is called from Java.

hello.cpp


#include <iostream>

void hello() {
  std::cout << "Hello World!" << std::endl;
}

Then create a SWIG interface file to wrap it

hello.i


%module hello                                                                   
                                                                                
%{                                                                              
  void hello();   <-①                                                                 
%}                                                                              
                                                                                
void hello();   <- ②

In% module, specify the name of the module to be created. Describe the C / C ++ header and declaration between% {%}, and finally write what is provided as an interface when calling from another language. I wrote the same void hello () twice, but if I didn't write ①, it failed with an undeclared error at compile time, and if I deleted ②, I couldn't find the function when Java was executed.

Once you have prepared this, generate the wrapper code with the swig command.

$ swig -java -c++ -cppext cpp hello.i

The meaning of the argument is to specify the target language of the source code generated by -java, to enable processing of C ++ functions with-c ++, and to extend the C ++ code generated by -cppext cpp. It is set to .cpp. If nothing is specified, the extension will be cxx. When this command is executed, the following three files are generated.

Then compile the C ++ code to create a shared library.

$ g++ -shared -o libhello.so -I/usr/lib/jvm/java-11-openjdk-amd64/include -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux -fPIC hello.cpp hello_wrap.cpp

It is necessary to specify a path with jni.h, jni_md.h, etc. in the include path. If you do not specify it, you will be angry that a specific header file cannot be found due to a compile error, so you can search for the file with the locate command etc. and add it to the include path each time.

Java side preparation

Implement on the Java side that loads the C ++ library.

Main.java


public class Main {
  static {
    System.loadLibrary("hello"); // <- C++Loading the library
  }
  public static void main(String[] args) {
    hello.hello();
  }
}

Compile and run this with the SWIG-generated files. In this case, you can do something like javac * .java without thinking about anything.

$ javac hello.java helloJNI.java Main.java
$ java Main
Hello World! <- C++Function execution result of

I was able to print the string by calling the hello function on the C ++ side.

2. Use Class

Create a rectangle class that can calculate the area by setting the length and width. The procedure is basically the same as 1. File to prepare this time

├── Main.java
├── square.cpp
├── square.hpp
└── square.i

This time I will separate the header and the source.

Preparation on the C ++ side

Implement the SquareC class as a rectangular class on the C ++ side. The module name and class name defined in the interface file must not be duplicated.

square.hpp


class SquareC {
public:
  SquareC(double x, double y);
  double area();
private:
  double height_;
  double width_;
};

square.cpp


#include "square.hpp"

SquareC::SquareC(double x, double y) : height_(x), width_(y) {
}

//Returns the area
double SquareC::area() {
  return height_ * width_;
}

Next, prepare the interface file.

square.i


%module square

%{
  #include "square.hpp" <- ①
%}

%include "square.hpp" <- ②

As I wrote in 1., (1) square.hpp is declared, and (2) square.hpp is read for interface definition.

Executing the swig command will generate four files this time. SquareC.java, which corresponds to the class defined in C ++, is a file that was not an example of Hello World.

$ swig -java -c++ -cppext cpp square.i

Then compile in the same way

$ g++ -shared -o libsquare.so -I/usr/lib/jvm/java-11-openjdk-amd64/include -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux -fPIC square.cpp square_wrap.cpp

Java side preparation

Load the square library. The class defined on the C ++ side can be used in the same way as a normal Java class.

Main.java


public class Main {
  static {
    System.loadLibrary("square");
  }
  public static void main(String[] args) {
      SquareC s = new SquareC(3.0, 4.0);
      System.out.println(s.area());
  }
}

Compile and run

$javac Main.java squareJNI.java SquareC.java square.java
$java Main
12.0

I was able to create an instance of the SquareC class and call its method to print the result in Java.

3. use string

Sample using std :: string. Make an example using string I / O with a simple setter and getter. The following four files are prepared

├── Main.java
├── person.cpp
├── person.hpp
└── person.i

Preparation on the C ++ side

Create a PersonC class with name and age members.

person.hpp


#include <string>

class PersonC {
public:
  PersonC(const std::string&, int);
  const std::string& get_name() const;
  void set_name(std::string&);
  int get_age() const;
  void set_age(int);
private:
  std::string name_;
  int age_;
};

person.cpp


#include <string>
#include "person.hpp"

PersonC::PersonC(const std::string& name, int age) : name_(name), age_(age) {}

const std::string& PersonC::get_name() const {
  return name_;
}

void PersonC::set_name(std::string& name) {
  name_ = name;
}

int PersonC::get_age() const {
  return age_;
}

void PersonC::set_age(int age) {
  age_ = age;
}

Add a new % include <std_string.i> to the interface file. This is an interface file for handling std :: string prepared by SWIG in advance.

person.i


%module person
%include <std_string.i>

%{
  #include "person.hpp"
%}

%include "person.hpp"

Execute the swig command as before.

$ swig -java -c++ -cppext cpp person.i

The following 5 files are generated.

A new file called SWIGTYPE_p_std__string.java that wraps std :: string has been created.

Next, create a C ++ library.

g++ -shared -o libperson.so -I/usr/lib/jvm/java-11-openjdk-amd64/include -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux -fPIC person.cpp person_wrap.cpp

Java side preparation

You can write as follows using String class without any particular consciousness.

public class Main {
  static {
    System.loadLibrary("person");
  }
  public static void main(String[] args) {
      String p_name = "Taro";
      PersonC p = new PersonC(p_name, 30);
      String n = p.get_name();
      System.out.println(n);
  }
}

$ javac *.java
$ java Main
Taro

4. Use Vector

A sample that calculates the inner product using std :: vector. If you use template in the interface file, you can easily use vector in Java.

├── Main.java
├── inner.cpp
├── inner.hpp
└── inner.i

Preparation on the C ++ side

On the C ++ side, write a program that calculates the inner product of double vectors without paying special attention to anything.

inner.hpp


#include<vector>

double inner_product(const std::vector<double>&, const std::vector<double>&);

inner.cpp


#include "inner.hpp"

double inner_product(const std::vector<double>& a,
                     const std::vector<double>& b) {
  double ret_val = 0;
  for (size_t i = 0; i < a.size(); ++i) {
    ret_val += a[i] * b[i];
  }
  return ret_val;
}

Next, write the interface file.

%include <std_vector.i>

%{
  #include "inner.hpp"
%}

%include "inner.hpp"
%template(DVec) std::vector<double>;

Includes <std_vector.i> to use the vector interface, and a new line called % template appears. As the name implies, it defines an interface for wrapping C ++ templates, which makes it possible to handle objects using C ++ templates even in the target language. In this example, std :: vector <double> can be accessed with the type DVec.

When you execute the swig command, the following file is generated.

Java source corresponding to DVec has been generated. Then, create a shared library as before.

$ g++ -shared -o libinner.so -I/usr/lib/jvm/java-11-openjdk-amd64/include -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux -fPIC inner.cpp inner_wrap.cpp

Java side preparation

Implement using <std :: vector> defined as DVec. If you look at the generated DVec.java, you can see what kind of implementation it is in Java.

public class DVec extends java.util.AbstractList<Double> implements java.util.RandomAccess {
  private transient long swigCPtr;
  protected transient boolean swigCMemOwn;
(The following is omitted)

You can see that it is implemented by inheriting AbstractList. Therefore, when using from Java You need to call the AbstractList method (such as add) instead of std :: vector.

Main.java


public class Main {
  static {
    System.loadLibrary("inner");
  }
  public static void main(String[] args) {
      DVec a = new DVec();
      a.add(1.0);
      a.add(2.0);
      a.add(3.0);
      DVec b = new DVec();
      b.add(3.0);
      b.add(4.0);
      b.add(5.0);

      System.out.println(inner.inner_product(a, b));
  }
}

Compile and run this.

$ javac *.java
$ java Main
26.0

5. Use vector vector

Handle examples where templates are nested. If you do not plan to use the intermediate type on the target language side, you can write only the outermost template in the interface file.

Create an example of creating and displaying a random 3x3 matrix.

Preparation on the C ++ side

We will implement the create function that creates a 3x3 matrix and the print_matrix function that displays it.

matrix.hpp


#include <vector>

std::vector<std::vector<double>> create();
void print_matrix(const std::vector<std::vector<double>>&);

matrix.cpp


#include <random>
#include <iostream>

#include "matrix.hpp"

std::vector<std::vector<double>> create() {
  std::random_device rnd {};
  std::vector<std::vector<double>> m {};
  for (size_t i = 0; i < 3; ++i) {
    std::vector<double> v {};    
    for (size_t j = 0; j < 3; ++j) {
      v.push_back(rnd());
    }
    m.push_back(v);
  }
  return m;
}

void print_matrix(const std::vector<std::vector<double>>& m) {
  std::cout << "[" << std::endl;
  for (const auto& r : m) {
    std::cout << "  ";
    for (const auto& e : r) {
      std::cout << e << " " ;
    }
    std::cout << std::endl;  
  }
  std::cout << "]" << std::endl;  
}

Next, create an interface file.

matrix.i


%module matrix

%include <std_vector.i>

%{
  #include "matrix.hpp"
%}

%include "matrix.hpp"
%template (DMatrix) std::vector<std::vector<double>>;

Write only DMatrix in the interface file and try not to write anything about std :: vector .

If you execute the swig command in this state, the following files will be created.

It looks like a file corresponding to vector double has also been created.

Create a shared library as before and finish the preparation on the C ++ side.

$ g++ -shared -o libmatrix.so -I/usr/lib/jvm/java-11-openjdk-amd64/include -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux -fPIC matrix.cpp matrix_wrap.cpp

Java side preparation

Implement the code on the Java side. Create a variable of DMatrix class and read the function or access the element.

public class Main {
  static {
    System.loadLibrary("matrix");
  }
  public static void main(String[] args) {
      DMatrix m = matrix.create();
      matrix.print_matrix(m);
      System.out.println(m.get(0)); <-Try to print element 0 of m
      System.out.println(m);        <-Try to print m itself
  }
}

If you do this, you will get the following results.

$ javac *.java
$ java Main
[
  1.99974e+09 2.96596e+08 1.57757e+09 
  1.71478e+09 6.51067e+08 2.89146e+09 
  1.63441e+09 9.24007e+08 2.31229e+09 
]
SWIGTYPE_p_std__vectorT_double_t@27c170f0
[SWIGTYPE_p_std__vectorT_double_t@2626b418, SWIGTYPE_p_std__vectorT_double_t@5a07e868, SWIGTYPE_p_std__vectorT_double_t@76ed5528]

The C ++ side creates variables of type DMatrix properly, and print_matrix works as expected. On the Java side, on the other hand, only the instance information is printed, and the contents of the vector are not printed.

SWIGTYPE_p_std__vectorT_double_t is a class that seems to correspond to DVec, but if you look at the source code of it, you can see that the implementation is clearly different from the one seen in the sample of 4.

SWIGTYPE_p_std__vectorT_double_t.java



public class SWIGTYPE_p_std__vectorT_double_t {
  private transient long swigCPtr;

  protected SWIGTYPE_p_std__vectorT_double_t(long cPtr, @SuppressWarnings("unused") boolean futureUse) {
    swigCPtr = cPtr;
  }

  protected SWIGTYPE_p_std__vectorT_double_t() {
    swigCPtr = 0;
  }

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

You can see that this class only provides a member variable called swigCPtr and its getter.

If you declare DVec, which is an element of DMatrix, in the interface file with template, you can print the contents of the vector from Java.

matrix.i


%module matrix

%include <std_vector.i>

%{
  #include "matrix.hpp"
%}

%include "matrix.hpp"
%template (DMatrix) std::vector<std::vector<double>>;
%template (DVec) std::vector<double>;
$ java Main
java Main
[
  3.37025e+09 3.25125e+09 1.91348e+08 
  2.32276e+09 2.57749e+09 3.0991e+09 
  1.9426e+09 2.75113e+09 4.03224e+09 
]
[3.370253657E9, 3.251246011E9, 1.91347842E8]
[[3.370253657E9, 3.251246011E9, 1.91347842E8], [2.322762433E9, 2.577487148E9, 3.099102289E9], [1.942601516E9, 2.751128283E9, 4.032242749E9]]

STL supported by SWIG should be treated in the same way.

It's been long, so it's up to here.

Recommended Posts

Link Java and C ++ code with SWIG
Encrypt with Java and decrypt with C #
Try to link Ruby and Java with Dapr
Solving with Ruby, Perl and Java AtCoder ABC 128 C
C # and Java Overrides Story
Kotlin post- and pre-increment and operator overload (comparison with C, Java, C ++)
Sample code to parse date and time with Java SimpleDateFormat
Solving with Ruby, Perl and Java AtCoder ABC 129 C (Part 1)
Use java with MSYS and Cygwin
Distributed tracing with OpenCensus and Java
Install Java and Tomcat with Ansible
Code Java from Emacs with Eclim
Java 15 implementation and VS Code preferences
Use JDBC with Java and Scala.
Writing code with classes and instances
Hello World with Docker and C
Java 9 new features and sample code
Java build with mac vs code
Execute packaged Java code with commands
Follow the link with Selenium (Java)
Output PDF and TIFF with Java 8
Prepare Java development environment with VS Code
Monitor Java applications with jolokia and hawtio
java core: HotSpot compiler and C heap
Let's try WebSocket with Java and javascript!
[Java] Reading and writing files with OpenCSV
[JaCoCo (Java Code Coverage)] Use with NetBeans
Java Direction in C ++ Design and Evolution
Execute Java code from cpp with cocos2dx
Page number logic and reference code (java)
Java to C and C to Java in Android Studio
Solving with Ruby, Perl and Java AtCoder ABC 129 C (Part 2) Dynamic programming
How to make an app with a plugin mechanism [C # and Java]
Experience .NET 5 with Docker and Visual Studio Code
Script Java code
Java code TIPS
Static code analysis with Checkstyle in Java + Gradle
I implemented Ruby with Ruby (and C) (I played with builtin)
Java sample code 02
Java sample code 03
Build and test Java + Gradle applications with Wercker
I also tried WebAssembly with Nim and C
[Java] Explanation of Strategy pattern (with sample code)
JSON with Java and Jackson Part 2 XSS measures
Differences in writing Java, C # and Javascript classes
Java sample code 04
Make Java code coverage more comfortable with Jacoco 0.8.0
Using Gradle with VS Code, build Java → run
Summarize the differences between C # and Java writing
Java and JavaScript
[Code] Forcibly breaks through the C problem "* 3 or / 2" of [AtCoder Problem-ABC100] with Java [Code]
XXE and Java
Prepare a scraping environment with Docker and Java
Java sample code 01
KMS) Envelope encryption with openssl and java decryption
Java character code
Try debugging a Java program with VS Code
Encrypt / decrypt with AES256 in PHP and Java
[Java] Convert and import file values with OpenCSV
[Review] Reading and writing files with java (JDK6)
Build a Java development environment with VS Code