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
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/
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.
Download von Offizielle Website-Download-Seite.
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
├── Main.java
├── hello.cpp
└── hello.i
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.
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.
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
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.
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
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
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
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
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
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
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
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.
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
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
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.