Unerforschtes Ziel Wir verwenden Qulacs, einen Quantencomputersimulator, der hauptsächlich in C ++ für die Entwicklung des Quantencomputers 2018 geschrieben wurde. Qulacs hat bereits Python-Bindungen, aber ich wollte schon immer in Scala entwickeln, einer bekannteren Programmiersprache. Also habe ich Java CPP verwendet, um viel zu recherchieren, um eine native Bibliothek wie Qulacs von Scala über Java und schließlich das sbt-Plug-In aufzurufen. Ich konnte machen.
Eigentlich hat die GitHub-Organisation von JavaCPP auch einen sbt-Plug, aber ich habe herausgefunden, dass dies dazu dient, OpenCV usw., das bereits in JavaCPP verfügbar ist, von Scala aus einfacher zu verwenden, sodass eine neue native Bibliothek von Scala aus verwendet werden kann. Ich habe mich dieses Mal entschlossen, ein neues zu erstellen, weil es nichts zu tun war. Dieser Artikel beschreibt die Verwendung dieses sbt-Plugins und einiger interner Implementierungen. Wenn Sie nach dem Lesen dieses Artikels Fragen oder Verbesserungen haben, teilen Sie uns dies bitte in den Kommentaren mit.
Dieses sbt-Plug-In macht drei Dinge:
Zunächst scheint (1) je nach Bibliothek unterschiedlich erstellt zu sein (mit make
usw.), sodass der OS-Befehl als settingsKey
von sbt übergeben wird. Und (2) startet auch einen neuen Java-Prozess von sbt. Dies müsste mit den entsprechenden Änderungen an den an die JVM übergebenen Optionen erfolgen und hätte mit den richtigen Änderungen am Befehl "run" von sbt, "javaOptions" usw. erreicht werden können. Ich habe das getan, weil es keine Änderung gab. Dann führt Java CPP (3) aus und wird zu einer dynamischen Linkbibliothek, die von Java aus aufgerufen werden kann.
Von hier aus werde ich anhand von Beispielprojekt erklären. Der C ++ - Quellcode und die Header-Datei sind jetzt wie folgt vorhanden.
cpp_src/HelloWorld.hpp
class HelloWorld {
public:
int printN(int n);
};
cpp_src/HelloWorld.cpp
#include <iostream>
#include <string>
#include "HelloWorld.hpp"
using namespace std;
int HelloWorld::printN(int n) {
for (int i = 0; i < n; i++) {
cout << "Hello World!\n";
}
return n;
}
Schreiben Sie zuerst die Einstellungen zum Kompilieren in build.sbt
.
build.sbt
includePath := (baseDirectory in Compile).value / "cpp_src"
libraryName := "libHelloWorld"
makeLibraryCommands := Seq(
gppCompilerPath.value,
"-I", includePath.value.toString,
currentLibraryMeta.value.option,
"-o",
(libraryDestinationPath.value / s"${libraryName.value}.${currentLibraryMeta.value.extension}").toString,
((baseDirectory in Compile).value / "cpp_src" / "HelloWorld.cpp").toString
)
Hier ist makeLibraryCommands
der Betriebssystembefehl, mit dem libHelloWorld
erstellt wird. Ich werde in der Reihenfolge erklären.
Erstens verwendet gppCompilerPath
standardmäßig clang ++
.
Als nächstes speichert currentLibraryMeta
Verarbeitungsoptionen wie clang ++
vom Betriebssystem und die DatenstrukturDynamicLibraryMeta
, um die Erweiterung der generierten nativen Bibliothek zu absorbieren.
DynamicLibraryMeta.scala
sealed abstract class DynamicLibraryMeta(
val option: String,
val extension: String
)
object DynamicLibraryMeta {
case object Mac extends DynamicLibraryMeta("-dynamiclib", "dylib")
case object Linux extends DynamicLibraryMeta("-shared", "so")
}
Und currentLibraryMeta
wird standardmäßig automatisch aus der Java-Eigenschaft System.getProperty (" os.name ")
ausgewählt. Standardmäßig verwendet "libraryDestinationPath" auch ein dediziertes Verzeichnis unter dem "Ziel" -Verzeichnis.
Erstellen Sie als Nächstes Java-Code, der die native Bibliothek verwendet.
src/main/java/javacpp/sbt/HelloWorld.java
package javacpp.sbt;
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;
@Platform(include = {"HelloWorld.hpp"}, link = "HelloWorld")
public class HelloWorld extends Pointer {
static {
Loader.load();
}
public HelloWorld() {
allocate();
}
public native void allocate();
public native int printN(int n);
}
Gehen Sie zurück zu build.sbt
und geben Sie die native Bibliothek und den entsprechenden Java-Klassenpfad an.
build.sbt
nativeJavaClassPath := "javacpp.sbt.*"
enablePlugins(SbtJavaCPP4S)
Sie können auch solche Platzhalter verwenden. Wenn dieses Plug-In aktiviert ist, ist die Arbeit in build.sbt
abgeschlossen.
Erstellen Sie abschließend den Java-Teileaufrufcode von Scala.
src/main/scala/javacpp/HelloWorld.scala
object HelloWorld {
def main(args: Array[String]): Unit = {
val instance = new HelloWorld()
instance.printN(5)
}
}
Danach, wenn Sie dies ausführen, wird es wie folgt sein.
$ sbt run
[info] Loading settings for project example from build.sbt ...
[info] Set current project to example (in build file:/Users/yyu/Desktop/javacpp-sbt/example/)
[info] Compiling 1 Scala source to /Users/yyu/Desktop/javacpp-sbt/example/target/scala-2.12/classes ...
[info] Success!
[info] running (fork) org.bytedeco.javacpp.tools.Builder -cp /Users/yyu/Desktop/javacpp-sbt/example/target/scala-2.12/classes:/Users/yyu/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/bytedeco/javacpp/1.5.1/javacpp-1.5.1.jar:/Users/yyu/.sbt/boot/scala-2.12.10/lib/scala-library.jar -Dplatform.compiler=clang++ -Dplatform.includepath=/Users/yyu/Desktop/javacpp-sbt/example/cpp_src -Dplatform.linkpath=/Users/yyu/Desktop/javacpp-sbt/example/target/libjni -d /Users/yyu/Desktop/javacpp-sbt/example/target/libjni javacpp.sbt.*mple / Compile / generateJNILibrary 0s
[info] Generating /Users/yyu/Desktop/javacpp-sbt/example/target/libjni/jnijavacpp.cpp
[info] Generating /Users/yyu/Desktop/javacpp-sbt/example/target/libjni/jniHelloWorld.cpp
[info] Compiling /Users/yyu/Desktop/javacpp-sbt/example/target/libjni/libjniHelloWorld.dylib
[info] clang++ -I/Users/yyu/Desktop/javacpp-sbt/example/cpp_src -I/Library/Java/JavaVirtualMachines/openjdk-11.0.2.jdk/Contents/Home/include -I/Library/Java/JavaVirtualMachines/openjdk-11.0.2.jdk/Contents/Home/include/darwin /Users/yyu/Desktop/javacpp-sbt/example/target/libjni/jniHelloWorld.cpp /Users/yyu/Desktop/javacpp-sbt/example/target/libjni/jnijavacpp.cpp -march=x86-64 -m64 -O3 -Wl,-rpath,@loader_path/. -Wall -fPIC -dynamiclib -undefined dynamic_lookup -o libjniHelloWorld.dylib -L/Users/yyu/Desktop/javacpp-sbt/example/target/libjni -Wl,-rpath,/Users/yyu/Desktop/javacpp-sbt/example/target/libjni -lHelloWorld -framework JavaVM
[info] Deleting /Users/yyu/Desktop/javacpp-sbt/example/target/libjni/jniHelloWorld.cpp
[info] Deleting /Users/yyu/Desktop/javacpp-sbt/example/target/libjni/jnijavacpp.cpp
[info] running (fork) javacpp.HelloWorld
[info] Hello World!
[info] Hello World!
[info] Hello World!
[info] Hello World!
[info] Hello World!
[success] Total time: 6 s, completed 2019/11/09 4:57:50
So funktioniert es gut.
Das kleine Beispiel hat funktioniert, daher möchte ich jetzt mit großen C ++ - Bibliotheken wie Qulacs arbeiten können. Diese Entwicklung erfordert viel Aufwand für die Menge an Code. Immerhin hatte ich viele Probleme, z. B. den nativen Fehler nicht gut zu kennen und keine dynamischen Links zu kennen. Abgesehen davon denke ich, dass die Komplexität dynamischer Links einer der Gründe ist, warum die Go-Sprache eine einzelne Binärdatei ist.
Recommended Posts