Verknüpfen Sie Java- und C ++ - Code mit SWIG

Über diesen Artikel

Ich werde vorstellen, wie man C ++ - Code aus Java mit SWIG mit Beispielcode aufruft. In diesem Artikel werde ich von Hallo Welt über Klassendefinition und einfache STL-Verwendung schreiben.

Beispielcode: https://github.com/TkrUdagawa/swig_java_sample

Was ist SWIG?

Eine Abkürzung für Simplified Wrapper und Interface Generator, ein Tool, das Wrapper generiert, die in C / C ++ geschriebenen Code aus anderen Sprachen aufrufen können. Neben Java werden auch Skriptsprachen wie Python, Ruby und PHP unterstützt.

Offizielle Seite: http://www.swig.org/

Ausführungsumgebung zur Hand

SWIG-Installation

Die neueste Version zum Zeitpunkt des Schreibens ist 4.0.0, die Ende April 2019 veröffentlicht wurde. In den Versionshinweisen scheint es, dass der C ++ 11 STL-Container in 4.0.0 hinzugefügt wurde. Daher scheint es gut, 4.0.0 zu installieren, auch wenn dies etwas problematisch ist.

herunterladen

Download von Offizielle Website-Download-Seite.

Installation

Extrahieren Sie die heruntergeladene Datei und installieren Sie sie mit configure, make

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

Stichprobe

  1. Hello world Erstens das einfachste Beispiel. Erstellen Sie ein Beispiel, das eine in C ++ definierte Funktion von Java aus aufruft. Die folgenden drei Dateien werden von Ihnen selbst erstellt
├── Main.java
├── hello.cpp
└── hello.i

Vorbereitung auf der C ++ Seite

Bereiten Sie zunächst den C ++ - Code vor, der von Java aufgerufen wird.

hello.cpp


#include <iostream>

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

Erstellen Sie dann eine SWIG-Schnittstellendatei, um sie zu verpacken

hello.i


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

% module gibt den Namen des zu erstellenden Moduls an. Beschreiben Sie den C / C ++ - Header und die Deklaration zwischen% {%} und schreiben Sie schließlich, was beim Aufrufen aus einer anderen Sprache als Schnittstelle bereitgestellt wird. Ich habe zweimal das gleiche void hello () geschrieben, aber wenn ich ① nicht geschrieben habe, ist es beim Kompilieren mit einem nicht deklarierten Fehler fehlgeschlagen, und wenn ich ② gelöscht habe, konnte ich die Funktion bei der Ausführung von Java nicht finden.

Sobald Sie dies vorbereitet haben, generieren Sie den Wrapper-Code mit dem Befehl swig.

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

Die Bedeutung des Arguments besteht darin, die Zielsprache des von -java generierten Quellcodes anzugeben, die Verarbeitung von C ++ - Funktionen mit -c ++ zu ermöglichen und den von -cppext cpp generierten C ++ - Code zu erweitern. Es ist auf ".cpp" gesetzt. Wenn nichts angegeben ist, lautet die Erweiterung cxx. Wenn dieser Befehl ausgeführt wird, werden die folgenden drei Dateien generiert.

Kompilieren Sie dann den C ++ - Code, um eine gemeinsam genutzte Bibliothek zu erstellen.

$ 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

Es ist erforderlich, einen Pfad mit "jni.h", "jni_md.h" usw. im Include-Pfad anzugeben. Wenn Sie es nicht angeben, werden Sie verärgert sein, dass eine bestimmte Header-Datei aufgrund eines Kompilierungsfehlers nicht gefunden werden kann. Sie können also mit dem Befehl location usw. nach der Datei suchen und sie jedes Mal zum Include-Pfad hinzufügen.

Vorbereitung auf der Java-Seite

Implementieren Sie auf der Java-Seite, die die C ++ - Bibliothek lädt.

Main.java


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

Kompilieren Sie diese und führen Sie sie mit der von SWIG generierten Datei aus. In einem Beispiel wie diesem können Sie so etwas wie "javac * .java" ausführen, ohne an irgendetwas zu denken.

$ javac hello.java helloJNI.java Main.java
$ java Main
Hello World! <- C++Funktionsausführungsergebnis von

Ich konnte den String drucken, indem ich die Hallo-Funktion auf der C ++ - Seite aufrief.

2. Verwenden Sie Class

Erstellen Sie eine rechteckige Klasse, die die Fläche berechnen kann, indem Sie die vertikalen und horizontalen Längen festlegen. Die Vorgehensweise ist grundsätzlich die gleiche wie bei 1. Datei, um diese Zeit vorzubereiten

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

Dieses Mal werde ich den Header und die Quelle trennen.

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) {
}

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

square.i


%module square

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

%include "square.hpp" <- ②
$ swig -java -c++ -cppext cpp square.i

Implementieren Sie die SquareC-Klasse als rechteckige Klasse auf der C ++ - Seite. Der in der Schnittstellendatei definierte Modul- und Klassenname darf nicht dupliziert werden. Gibt den Bereich zurück. Bereiten Sie als Nächstes die Schnittstellendatei vor. Wie ich in 1. geschrieben habe, wird (1) square.hpp deklariert und (2) square.hpp für die Schnittstellendefinition gelesen. Durch Ausführen des Befehls swig werden diesmal vier Dateien generiert. SquareC.java, das der in C ++ definierten Klasse entspricht, ist eine Datei, die kein Beispiel für Hello World war....

$ 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

Vorbereitung auf der Java-Seite

Laden Sie die quadratische Bibliothek. Die auf der C ++ - Seite definierte Klasse kann wie eine normale Java-Klasse verwendet werden.

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());
  }
}

Kompilieren und ausführen

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

Ich konnte eine Instanz der SquareC-Klasse erstellen und ihre Methode aufrufen, um das Ergebnis in Java zu drucken.

3. Verwenden Sie eine Zeichenfolge

Ein Beispiel, das std :: string verwendet. Machen Sie ein Beispiel mit String Input / Output mit einem einfachen Setter und Getter. Die folgenden vier Dateien werden vorbereitet

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

Vorbereitung auf der C ++ Seite

Erstellen Sie eine PersonC-Klasse mit Mitgliedern mit Namen und Alter.

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;
}

Fügen Sie der Schnittstellendatei ein neues% include <std_string.i> hinzu. Dies ist eine Schnittstellendatei für die Verarbeitung von "std :: string", die von SWIG im Voraus erstellt wurde.

person.i


%module person
%include <std_string.i>

%{
  #include "person.hpp"
%}

%include "person.hpp"

Führen Sie den Befehl swig wie zuvor aus.

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

Die folgenden 5 Dateien werden generiert.

Eine neue Datei mit dem Namen "SWIGTYPE_p_std__string.java", die std :: string umschließt, wurde erstellt.

Erstellen Sie als Nächstes eine C ++ - Bibliothek.

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

Vorbereitung auf der Java-Seite

Sie können mit der String-Klasse ohne besondere Kenntnis wie folgt schreiben.

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. Verwenden Sie Vector

Ein Beispiel, das std :: vector verwendet, um das innere Produkt zu berechnen. Sie können Vektor in Java einfach verwenden, indem Sie die Vorlage in der Schnittstellendatei verwenden.

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

Vorbereitung auf der C ++ Seite

Schreiben Sie auf der C ++ - Seite ein Programm, das das innere Produkt von Doppelvektoren berechnet, ohne etwas besonders zu beachten.

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;
}

Als nächstes schreiben Sie die Schnittstellendatei.

%include <std_vector.i>

%{
  #include "inner.hpp"
%}

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

Enthält "<std_vector.i>" zur Verwendung der Vektorschnittstelle, und eine neue Zeile mit dem Namen "% template" wird angezeigt. Wie der Name schon sagt, definiert es eine Schnittstelle zum Umschließen von C ++ - Vorlagen, über die die Zielsprache Objekte verarbeiten kann, die C ++ - Vorlagen verwenden. In diesem Beispiel kann auf "std :: vector " mit dem Typ "DVec" zugegriffen werden.

Wenn Sie den Befehl swig ausführen, wird die folgende Datei generiert.

Eine DVec entsprechende Java-Quelle wurde generiert. Erstellen Sie dann wie zuvor eine gemeinsam genutzte Bibliothek.

$ 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

Vorbereitung auf der Java-Seite

Implementieren Sie mit <std :: vector>, definiert als DVec. Wenn Sie sich die generierte DVec.java ansehen, können Sie sehen, um welche Art von Implementierung es sich in Java handelt.

public class DVec extends java.util.AbstractList<Double> implements java.util.RandomAccess {
  private transient long swigCPtr;
  protected transient boolean swigCMemOwn;
(Folgendes wird weggelassen)

Sie können sehen, dass es implementiert wird, indem Sie AbstractList erben. Daher bei Verwendung von Java Sie müssen eine Methode von AbstractList (z. B. add) anstelle von std :: vector aufrufen.

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));
  }
}

Kompilieren Sie diese und führen Sie sie aus.

$ javac *.java
$ java Main
26.0

5. Verwenden Sie den Vektorvektor

Behandeln Sie Beispiele, in denen Vorlagen verschachtelt sind. Wenn Sie nicht vorhaben, den Zwischentyp auf der Zielsprachenseite zu verwenden, können Sie nur die äußerste Vorlage in die Schnittstellendatei schreiben.

Erstellen Sie ein Beispiel für das Erstellen und Anzeigen einer zufälligen 3x3-Matrix.

Vorbereitung auf der C ++ Seite

Wir implementieren die Funktion create, die eine 3x3-Matrix erstellt, und die Funktion print_matrix, die sie anzeigt.

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;  
}

Erstellen Sie als Nächstes eine Schnittstellendatei.

matrix.i


%module matrix

%include <std_vector.i>

%{
  #include "matrix.hpp"
%}

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

Schreiben Sie nur DMatrix in die Schnittstellendatei und versuchen Sie, nichts über std :: vector zu schreiben.

Wenn Sie den Befehl swig in diesem Status ausführen, werden die folgenden Dateien erstellt.

Es sieht so aus, als ob auch eine Datei erstellt wurde, die dem Vektordoppel entspricht.

Erstellen Sie wie zuvor eine gemeinsam genutzte Bibliothek und beenden Sie die Vorbereitung auf der C ++ - Seite.

$ 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

Vorbereitung auf der Java-Seite

Implementieren Sie den Code auf der Java-Seite. Erstellen Sie eine Variable der DMatrix-Klasse und lesen Sie die Funktion oder greifen Sie auf das Element zu.

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)); <-Versuchen Sie, Element 0 von m zu drucken
      System.out.println(m);        <-Versuchen Sie, m selbst zu drucken
  }
}

Wenn Sie dies tun, erhalten Sie die folgenden Ergebnisse.

$ 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]

Die C ++ - Seite erstellt Variablen vom Typ DMatrix ordnungsgemäß, und print_matrix funktioniert wie erwartet. Auf der Java-Seite werden dagegen nur die Instanzinformationen gedruckt, und der Inhalt des Vektors wird nicht gedruckt.

SWIGTYPE_p_std__vectorT_double_t ist eine Klasse, die anscheinend DVec entspricht. Wenn Sie sich jedoch den Quellcode ansehen, sehen Sie, dass sich die Implementierung deutlich von der im Beispiel von 4 unterscheidet.

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;
  }
}

Sie können sehen, dass diese Klasse nur eine Mitgliedsvariable namens swigCPtr und ihren Getter bereitstellt.

Wenn Sie auch DVec, ein Element von DMatrix, in der Schnittstellendatei mit Vorlage deklarieren, können Sie den Inhalt des Vektors aus Java drucken.

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]]

Sie sollten in der Lage sein, von SWIG unterstützte STLs auf ähnliche Weise zu behandeln.

Es ist lange her, also liegt es bis hier.

Recommended Posts

Verknüpfen Sie Java- und C ++ - Code mit SWIG
Mit Java verschlüsseln und mit C # entschlüsseln
Versuchen Sie, Ruby und Java in Dapr zu integrieren
Lösen mit Ruby, Perl und Java AtCoder ABC 128 C.
C # und Java überschreiben Story
Kotlin Post- und Pre-Inkrement und Operatorüberladung (Vergleich mit C, Java, C ++)
Beispielcode zum Parsen von Datum und Uhrzeit mit Java SimpleDateFormat
Lösen mit Ruby, Perl und Java AtCoder ABC 129 C (Teil 1)
Verwenden Sie Java mit MSYS und Cygwin
Verteilte Ablaufverfolgung mit OpenCensus und Java
Installieren Sie Java und Tomcat mit Ansible
Code Java von Emacs mit Eclim
Verwenden Sie JDBC mit Java und Scala.
Hallo Welt mit Docker und C Sprache
Java 9 neue Funktionen und Beispielcode
Erstellen Sie Java mit Mac vs Code
Führen Sie gepackten Java-Code mit Befehlen aus
Folgen Sie dem Link mit Selen (Java)
PDF und TIFF mit Java 8 ausgeben
Bereiten Sie die Java-Entwicklungsumgebung mit VS Code vor
Überwachen Sie Java-Anwendungen mit Jolokia und Hawtio
Java-Kern: HotSpot-Compiler und C-Heap
Probieren wir WebSocket mit Java und Javascript aus!
[Java] Lesen und Schreiben von Dateien mit OpenCSV
[JaCoCo (Java Code Coverage)] Verwendung mit NetBeans
Die Richtung von Java in "C ++ Design and Evolution"
Führen Sie Java-Code von cpp auf cocos2dx aus
Seitenzahllogik und Referenzcode (Java)
Von Java nach C und von C nach Java in Android Studio
Lösen mit Ruby, Perl und Java AtCoder ABC 129 C (Teil 2) Dynamische Planungsmethode
So erstellen Sie eine App mit einem Plug-In-Mechanismus [C # und Java]
Erleben Sie .NET 5 mit Docker und Visual Studio Code
Führen Sie Java-Code skriptweise aus
Java-Code-TIPPS
Führen Sie eine statische Code-Analyse mit Checkstyle mit Java + Gradle durch
Ich habe versucht, Ruby mit Ruby (und C) zu implementieren (ich habe mit Builtin gespielt)
Java-Beispielcode 02
Java-Beispielcode 03
Erstellen und testen Sie Java + Gradle-Anwendungen mit Wercker
Ich habe auch Web Assembly mit Nim und C ausprobiert
JSON mit Java und Jackson Teil 2 XSS-Maßnahmen
Unterschiede beim Schreiben von Java-, C # - und Javascript-Klassen
Java-Beispielcode 04
Komfortablere Java-Codeabdeckung mit Jacoco 0.8.0
Erstellen Sie mit Gradle mit VSCode Java → Ausführen
Fassen Sie die Unterschiede zwischen C # - und Java-Schrift zusammen
Java und JavaScript
Durchbrechen Sie gewaltsam das C-Problem "* 3 oder / 2" von [AtCoder Problem-ABC100] mit Java [Code]
XXE und Java
Bereiten Sie eine Scraping-Umgebung mit Docker und Java vor
Java-Beispielcode 01
KMS) Umschlagverschlüsselung mit OpenSL- und Java-Entschlüsselung
Java-Zeichencode
Versuchen Sie, ein Java-Programm mit VS-Code zu debuggen
Verschlüsseln / Entschlüsseln mit AES256 in PHP und Java
[Java] Konvertieren und Importieren von Dateiwerten mit OpenCSV
[Review] Lesen und Schreiben von Dateien mit Java (JDK6)
Erstellen Sie eine Java-Entwicklungsumgebung mit VS Code