Verwenden Sie native Bibliotheken von Scala über Java CPP + Java

Einführung

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.

Plugin Übersicht

Dieses sbt-Plug-In macht drei Dinge:

  1. Erstellen Sie eine native Bibliothek wie ".so" (oder ".dylib").
  2. Übergeben Sie die Bibliothek und die Header-Datei von (1) an JavaCPP, um eine Datei für JNI (Java Native Interface) zu erstellen.
  3. Kompilieren Sie (2) mit einem Verarbeitungssystem wie "g ++", um eine native Bibliothek zu erstellen, die von Java aus verwendet werden kann.

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.

Einstellungen und Verwendung

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.

abschließend

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

Verwenden Sie native Bibliotheken von Scala über Java CPP + Java
Verwenden Sie Matplotlib aus Java oder Scala mit Matplotlib4j
Verwenden Sie JDBC mit Java und Scala.
Verwenden Sie Chrome Headless von Selenium / Java
Rufen Sie die Java-API von TensorFlow von Scala aus auf
Verwenden Sie den PostgreSQL-Datentyp (jsonb) aus Java
Java-Bibliotheken
Versuchen Sie, Nim von Java über JNI aufzurufen
Führen Sie Java-Code von cpp auf cocos2dx aus
Führen Sie Rust von Java mit JNA (Java Native Access) aus.
Erstellen Sie Scala Seq aus Java, machen Sie Scala Seq zu einer Java-Liste
Holen Sie sich Unixtime (Sekunden) von ZonedDateTime in Scala / Java
[Java] Verwenden Sie Collectors.collectingAndThen
Wie schreibe ich Scala aus der Perspektive von Java
Verhalten beim Aufrufen von Java-Argumentmethoden mit variabler Länge aus Scala / Kotlin / Java
6 Funktionen, die ich nach meiner Rückkehr von Scala nach Java verpasst habe
Ich habe versucht, nativen Java / Objective-C-Code von Flutter aus aufzurufen
JAWJAW ist praktisch, wenn Sie WordNet aus Java verwenden
Pessimistische Sperre für NW ABAP von Java über JCo