การสนับสนุนเชิงพาณิชย์:
JavaCPP ให้การเข้าถึง C++ ดั้งเดิมภายใน Java ได้อย่างมีประสิทธิภาพ ไม่ต่างจากวิธีที่คอมไพเลอร์ C/C++ บางตัวโต้ตอบกับภาษาแอสเซมบลี ไม่จำเป็นต้องคิดค้นภาษาใหม่ๆ เช่น SWIG, SIP, C++/CLI, Cython หรือ RPython แต่คล้ายกับสิ่งที่ cppyy พยายามทำเพื่อ Python โดยใช้ประโยชน์จากความคล้ายคลึงทางวากยสัมพันธ์และความหมายระหว่าง Java และ C++ ภายใต้ประทุนนั้นใช้ JNI ดังนั้นจึงใช้งานได้กับการใช้งาน Java SE ทั้งหมด นอกเหนือจาก Android, Avian และ RoboVM (คำแนะนำ)
โดยเฉพาะอย่างยิ่งเมื่อเปรียบเทียบกับแนวทางด้านบนหรือที่อื่น ๆ (CableSwig, JNIGeneratorApp, cxxwrap, JNIWrapper, Platform Inrigg, GlueGen, LWJGL Generator, JNIDirect, ctypes, JNA, JNIEasy, JniMarshall, JNative, J/Inrigg, HawtJNI, JNR, BridJ, CFFI, fficxx, CppSharp, cgo, pybind11, rub-bindgen, Panam Native ฯลฯ) โดยแมปคุณสมบัติทั่วไปหลายอย่างอย่างเป็นธรรมชาติและมีประสิทธิภาพโดยภาษา C++ และมักถูกพิจารณาว่าเป็นปัญหา รวมถึงตัวดำเนินการที่โอเวอร์โหลด เทมเพลตคลาสและฟังก์ชัน การเรียกกลับผ่านพอยน์เตอร์ฟังก์ชัน ออบเจ็กต์ฟังก์ชัน (หรือที่รู้จักในชื่อ functors) ฟังก์ชันเสมือนและพอยน์เตอร์ฟังก์ชันสมาชิก คำจำกัดความของโครงสร้างแบบซ้อน อาร์กิวเมนต์ความยาวแปรผัน เนมสเปซแบบซ้อน โครงสร้างข้อมูลขนาดใหญ่ที่มีวงจรตามใจชอบ การสืบทอดเสมือนและแบบทวีคูณ การส่งผ่าน/ส่งคืนโดย ค่า/การอ้างอิง/สตริง/เวกเตอร์ ยูเนี่ยนที่ไม่ระบุชื่อ ฟิลด์บิต ข้อยกเว้น ตัวทำลาย และตัวชี้ที่ใช้ร่วมกันหรือเฉพาะ (ผ่านทาง try-with-resources หรือการรวบรวมขยะ) และความคิดเห็นเกี่ยวกับเอกสารประกอบ แน่นอนว่าการสนับสนุน C++ ทั้งหมดอย่างเรียบร้อยจะต้องอาศัยการทำงานมากขึ้น (แม้ว่าใครๆ ก็สามารถโต้แย้งเกี่ยวกับความเรียบร้อยภายในของ C++ ได้) แต่เรากำลังเผยแพร่มันที่นี่เพื่อพิสูจน์แนวคิด
ในกรณีนี้ เราได้ใช้มันเพื่อสร้างอินเทอร์เฟซที่สมบูรณ์ให้กับ OpenCV, FFmpeg, libdc1394, PGR FlyCapture, OpenKinect, videoInput, ARToolKitPlus, Leptonica, Tesseract, GSL, LLVM, HDF5, MKL, CUDA, Caffe, MXNet, TensorFlow , System API และอื่นๆ ที่เป็นส่วนหนึ่งของโปรเจ็กต์ย่อย JavaCPP Presets เช่นกัน แสดงให้เห็นถึงความสามารถในการแยกวิเคราะห์เบื้องต้นของไฟล์ส่วนหัว C/C++ ที่แสดงผลลัพธ์ที่น่าหวังและมีประโยชน์
โปรดอย่าลังเลที่จะถามคำถามในรายชื่อผู้รับจดหมายหรือกระดานสนทนาหากคุณประสบปัญหาใด ๆ กับซอฟต์แวร์! ฉันแน่ใจว่ามันยังห่างไกลจากความสมบูรณ์แบบ...
ไฟล์เก็บถาวรที่มีไฟล์ JAR พร้อมใช้งานในรูปแบบรีลีส
เรายังสามารถดาวน์โหลดและติดตั้งทุกสิ่งโดยอัตโนมัติด้วย:
Maven (ภายในไฟล์ pom.xml
)
<การพึ่งพา> <groupId>org.bytedeco</groupId> <artifactId>javacpp</artifactId> <เวอร์ชั่น>1.5.11</เวอร์ชั่น> </การพึ่งพา>
Gradle (ภายในไฟล์ build.gradle.kts
หรือ build.gradle
)
การพึ่งพา { การใช้งาน ("org.bytedeco:javacpp:1.5.11") -
Leiningen (ภายในไฟล์ project.clj
)
:การพึ่งพา [ [org.bytedeco/javacpp "1.5.11"] -
sbt (ภายในไฟล์ build.sbt
)
ไลบรารีการพึ่งพา += "org.bytedeco" % "javacpp" % "1.5.11"
ตัวเลือกอื่นสำหรับผู้ใช้ Gradle คือ Gradle JavaCPP และในทำนองเดียวกันสำหรับผู้ใช้ Scala ก็มี SBT-JavaCPP
หากต้องการใช้ JavaCPP คุณจะต้องดาวน์โหลดและติดตั้งซอฟต์แวร์ต่อไปนี้:
การใช้งาน Java SE 7 หรือใหม่กว่า:
OpenJDK http://openjdk.java.net/install/ หรือ
Oracle JDK http://www.oracle.com/technetwork/java/javase/downloads/ หรือ
ไอบีเอ็ม JDK http://www.ibm.com/developerworks/java/jdk/
คอมไพเลอร์ C++ ซึ่งได้รับการทดสอบแล้ว:
https://docs.microsoft.com/en-us/cpp/build/walkthrough-compiling-a-native-cpp-program-on-the-command-line
สำหรับ Windows x86 และ x64 http://mingw-w64.org/
คอมไพเลอร์ GNU C/C++ (Linux ฯลฯ) http://gcc.gnu.org/
LLVM Clang (Mac OS X ฯลฯ) http://clang.llvm.org/
คอมไพเลอร์ Microsoft C/C++ ซึ่งเป็นส่วนหนึ่งของ Visual Studio https://visualstudio.microsoft.com/
หากต้องการสร้างไฟล์ไบนารีสำหรับ Android 4.0 หรือใหม่กว่า คุณจะต้องติดตั้ง:
Android NDK r7 หรือใหม่กว่า http://developer.android.com/ndk/downloads/
และเช่นเดียวกับเป้าหมาย iOS คุณจะต้องติดตั้ง:
Gluon VM http://gluonhq.com/products/mobile/vm/ หรือ
RoboVM 1.x หรือใหม่กว่า http://robovm.mobidevelop.com/downloads/
หากต้องการแก้ไขซอร์สโค้ด โปรดทราบว่าไฟล์โปรเจ็กต์ถูกสร้างขึ้นสำหรับ:
Maven 3.x http://maven.apache.org/download.html
สุดท้ายนี้ เนื่องจากเรากำลังเผชิญกับโค้ดแบบเนทีฟ จุดบกพร่องอาจทำให้เครื่องเสมือนเสียหายได้ง่าย โชคดีที่ HotSpot VM มีเครื่องมือบางอย่างเพื่อช่วยเราแก้ไขจุดบกพร่องภายใต้สถานการณ์เหล่านั้น:
คู่มือการแก้ไขปัญหาสำหรับ Java SE พร้อม HotSpot VM
http://docs.oracle.com/javase/7/docs/webnotes/tsg/TSG-VM/html/
http://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/
เพื่อให้เข้าใจถึงวิธีการใช้งาน JavaCPP อันดับแรกควรดูที่ Mapping Recipes สำหรับไลบรารี C/C++ แต่ภาพรวมระดับสูงของสถาปัตยกรรมพื้นฐานก็มีให้เพื่อทำความเข้าใจภาพรวม พื้นที่เก็บข้อมูลของ JavaCPP Presets มีตัวอย่างที่ซับซ้อนเพิ่มเติมที่เราสามารถใช้เป็นเทมเพลตได้ แต่ยังมีหน้าวิกิเกี่ยวกับวิธีการสร้าง Presets ใหม่ที่จะอธิบายโครงสร้างอย่างละเอียดพร้อมกับโปรเจ็กต์ตัวอย่างขนาดเล็กแต่สมบูรณ์ซึ่งใครๆ ก็สามารถเริ่มการทดลองได้ กับ.
หากต้องการนำเมธอด native
ไปใช้ JavaCPP จะสร้างโค้ดที่เหมาะสมสำหรับ JNI และส่งต่อไปยังคอมไพเลอร์ C++ เพื่อสร้างไลบรารีเนทิฟ เราไม่จำเป็นต้องยุ่งกับ JNI, makefile หรือเครื่องมือเนทิฟอื่นๆ เลย สิ่งสำคัญที่ต้องตระหนักในที่นี้คือ แม้ว่าเราจะปรับแต่งทั้งหมดภายในภาษา Java โดยใช้คำอธิบายประกอบ แต่ JavaCPP จะสร้างโค้ดที่มี ค่าใช้จ่ายเป็นศูนย์ เมื่อเทียบกับฟังก์ชัน JNI ที่เข้ารหัสด้วยตนเอง (ตรวจสอบไฟล์ .cpp ที่สร้างขึ้นเพื่อโน้มน้าวใจตัวเอง) ยิ่งไปกว่านั้น ณ รันไทม์ เมธอด Loader.load()
จะโหลดไลบรารีเนทิฟจากทรัพยากร Java โดยอัตโนมัติ ซึ่งถูกวางไว้ในไดเร็กทอรีที่ถูกต้องโดยกระบวนการสร้าง สามารถเก็บถาวรได้ในไฟล์ JAR โดยไม่มีการเปลี่ยนแปลงใดๆ ผู้ใช้ไม่จำเป็นต้องหาวิธีให้ระบบโหลดไฟล์ คุณลักษณะเหล่านี้ทำให้ JavaCPP เหมาะสำหรับทั้งสองอย่าง
การเข้าถึง API ดั้งเดิม
ใช้ประเภท C ++ ที่ซับซ้อน
การเพิ่มประสิทธิภาพการทำงานของโค้ดหรือ
การสร้างฟังก์ชันการโทรกลับ
นอกเหนือจากตัวอย่างบางส่วนที่ให้ไว้ด้านล่าง หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับวิธีการใช้คุณสมบัติของเครื่องมือนี้ โปรดดูตัวอย่างการทำแผนที่สูตรสำหรับไลบรารี C/C++ รวมถึงซอร์สโค้ดของการตั้งค่าล่วงหน้า JavaCPP สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ API นั้น อาจอ้างอิงถึงเอกสารที่สร้างโดย Javadoc
แน่นอนว่าทั้งหมดนี้ใช้ได้กับภาษา Scala เช่นกัน แต่เพื่อให้กระบวนการราบรื่นยิ่งขึ้น ไม่ควรยากเกินไปที่จะเพิ่มการรองรับ "คุณสมบัติดั้งเดิม" เช่นการประกาศเช่น @native var
สามารถสร้าง getter ดั้งเดิมได้ และวิธีการตั้งค่า...
กรณีการใช้งานที่พบบ่อยที่สุดเกี่ยวข้องกับการเข้าถึงไลบรารีดั้งเดิมที่เขียนสำหรับ C++ ตัวอย่างเช่น ภายในไฟล์ชื่อ NativeLibrary.h
ซึ่งมีคลาส C++ นี้:
#include <string>namespace NativeLibrary { คลาส NativeClass { สาธารณะ: const std::string& get_property() { คืนคุณสมบัติ; } เป็นโมฆะ set_property (const std :: string & คุณสมบัติ) { นี้ -> คุณสมบัติ = คุณสมบัติ; - มาตรฐาน :: คุณสมบัติสตริง; - -
เพื่อให้งานสำเร็จด้วย JavaCPP เราสามารถกำหนดคลาส Java เช่นนี้ได้อย่างง่ายดาย ถึงแม้ว่าเราจะสามารถใช้ Parser
เพื่อสร้างมันจากไฟล์ส่วนหัว ตามที่แสดงโดยโปรเจ็กต์ย่อย JavaCPP Presets ตามหลักการที่สรุปไว้ใน Mapping Recipes สำหรับไลบรารี C/C++:
import org.bytedeco.javacpp.*;import org.bytedeco.javacpp.annotation.*;@Platform(include="NativeLibrary.h")@Namespace("NativeLibrary")คลาสสาธารณะ NativeLibrary { คลาสคงที่สาธารณะ NativeClass ขยายตัวชี้ { คงที่ { ตัวโหลด.โหลด(); } สาธารณะ NativeClass() { จัดสรร (); } } จัดสรรโมฆะพื้นเมืองส่วนตัว (); // เพื่อเรียกใช้ฟังก์ชัน getter และ setter public Native @StdString String get_property(); โมฆะพื้นเมืองสาธารณะ set_property (คุณสมบัติ String); // เพื่อเข้าถึงตัวแปรสมาชิกโดยตรง สาธารณะโดยตรง @StdString String property(); ทรัพย์สินโมฆะสาธารณะ (ทรัพย์สินสตริง); } public static void main(String[] args) { // Pointer object ที่ถูกจัดสรรใน Java จะได้รับการจัดสรรคืนเมื่อไม่สามารถเข้าถึงได้ // แต่ตัวทำลาย C++ ยังคงสามารถเรียกได้ในเวลาที่เหมาะสมด้วย Pointer.deallocate() NativeClass l = new NativeClass (); l.set_property("สวัสดีชาวโลก!"); System.out.println(l.คุณสมบัติ()); - -
หลังจากคอมไพล์ซอร์สโค้ด Java ด้วยวิธีปกติแล้ว เรายังจำเป็นต้องสร้างโดยใช้ JavaCPP ก่อนที่จะรันมัน หรือเราจะปล่อยให้มันทำทุกอย่างดังต่อไปนี้:
$ java -jar javacpp.jar NativeLibrary.java -exec สวัสดีชาวโลก!
เพื่อแสดงให้เห็นถึงความง่ายในการใช้งานแม้ต้องเผชิญกับประเภทข้อมูลที่ซับซ้อน ลองจินตนาการว่าเรามีฟังก์ชัน C++ ที่ใช้ vector<vector<void*> >
เป็นอาร์กิวเมนต์ เพื่อสนับสนุนประเภทนั้น เราสามารถกำหนดคลาสเปลือยได้ดังนี้:
import org.bytedeco.javacpp.*;import org.bytedeco.javacpp.annotation.*;@Platform(include="<vector>")public class VectorTest { @Name("std::vector<std::vector<void *> >") คลาสคงที่สาธารณะ PointerVectorVector ขยาย Pointer { static { Loader.load(); } สาธารณะ PointerVectorVector() { จัดสรร (); } สาธารณะ PointerVectorVector (ยาว n) { จัดสรร (n); } สาธารณะ PointerVectorVector (ตัวชี้ p) { ซุปเปอร์ (p); } // this = (vector<vector<void*> >*)p ส่วนตัว void จัดสรร (); // this = new vector<vector<void*> >() ส่วนตัว void จัดสรร (ยาว n); // this = new vector<vector<void*> >(n) @Name("operator=") public Native @ByRef PointerVectorVector ใส่(@ByRef PointerVectorVector x); @Name("operator[]") สาธารณะ @StdVector PointerPointer get(long n); สาธารณะพื้นเมือง @StdVector PointerPointer ที่ (ยาว n); ขนาดยาวพื้นเมืองสาธารณะ (); สาธารณะ @Cast("bool") บูลีน ว่างเปล่า(); ปรับขนาดโมฆะพื้นเมืองสาธารณะ (ยาว n); สาธารณะชนพื้นเมือง @Index ขนาดยาว (ยาว i); // return (*this)[i].size() สาธารณะ @Index @Cast("bool") บูลีน ว่างเปล่า (long i); // return (*this)[i].empty() public Native @Index void resize(long i, long n); // (*this)[i].resize(n) สาธารณะ @Index Pointer get(long i, long j); // return (*this)[i][j] public Native void put (long i, long j, Pointer p); // (*นี่)[i][j] = p } โมฆะสาธารณะคง main (String [] args) { PointerVectorVector v = new PointerVectorVector (13); v.ปรับขนาด(0, 42); // v[0].resize(42) ตัวชี้ p = ตัวชี้ใหม่ () { { ที่อยู่ = 0xDEADBEEFL; - v.ใส่(0, 0, p); // v[0][0] = p PointerVectorVector v2 = ใหม่ PointerVectorVector().put(v); ตัวชี้ p2 = v2.get(0).get(); // p2 = *(&v[0][0]) System.out.println(v2.size() + " " + v2.size(0) + " " + p2); v2.at(42); - -
การรันโปรแกรมนั้นโดยใช้คำสั่งนี้จะสร้างเอาต์พุตต่อไปนี้:
$ java -jar javacpp.jar VectorTest.java -exec 13 42 org.bytedeco.javacpp.Pointer[ที่อยู่=0xdeadbeef,ตำแหน่ง=0,ขีดจำกัด=0,ความจุ=0,deallocator=null] ข้อยกเว้นในเธรด "main" java.lang.RuntimeException: vector::_M_range_check: __n (ซึ่งก็คือ 42) >= this->size() (ซึ่งก็คือ 13) ที่ VectorTest$PointerVectorVector.at (วิธีดั้งเดิม) ที่ VectorTest.main (VectorTest.java:44)
ในบางครั้ง เราอาจต้องการโค้ดในภาษา C++ (รวมถึง CUDA) เพื่อเหตุผลด้านประสิทธิภาพ สมมติว่าผู้สร้างโปรไฟล์ของเราระบุว่าวิธีการที่ชื่อ Processor.process()
ใช้เวลาดำเนินการ 90% ของโปรแกรม:
ตัวประมวลผลคลาสสาธารณะ { กระบวนการโมฆะสาธารณะคงที่ (บัฟเฟอร์ java.nio.Buffer, ขนาด int) { System.out.println ("การประมวลผลใน Java ... "); - } โมฆะสาธารณะคงหลัก (สตริง [] args) { กระบวนการ (null, 0); - -
หลังจากทำงานหนักและทำงานหนักมาหลายวัน วิศวกรก็ค้นพบเคล็ดลับบางอย่างและทำให้อัตราส่วนนั้นลดลงเหลือ 80% แต่คุณรู้ไหมว่าผู้จัดการยังคงไม่พอใจ ดังนั้นเราสามารถลองเขียนมันใหม่ในภาษา C ++ (หรือแม้แต่ภาษาแอสเซมบลีสำหรับเรื่องนั้นผ่านแอสเซมเบลอร์แบบอินไลน์) และวางฟังก์ชันผลลัพธ์ในไฟล์ชื่อว่า Processor.h
:
#include <iostream> กระบวนการโมฆะแบบอินไลน์แบบคงที่ (โมฆะ * บัฟเฟอร์ขนาด int) { std::cout << "กำลังประมวลผลด้วย C++..." << std::endl; -
หลังจากปรับซอร์สโค้ด Java ให้เป็นดังนี้:
import org.bytedeco.javacpp.*;import org.bytedeco.javacpp.annotation.*;@Platform(include="Processor.h")ตัวประมวลผลคลาสสาธารณะ { คงที่ { Loader.load(); } กระบวนการโมฆะเนทิฟสาธารณะแบบคงที่ (บัฟเฟอร์ java.nio.Buffer, ขนาด int); โมฆะคงที่สาธารณะ main (String [] args) { กระบวนการ (null, 0); - -
จากนั้นมันจะคอมไพล์และดำเนินการดังนี้:
$ java -jar javacpp.jar ตัวประมวลผล java -exec กำลังประมวลผลในภาษา C++...
แอปพลิเคชันบางตัวยังต้องการวิธีโทรกลับเข้าสู่ JVM จาก C/C++ ดังนั้น JavaCPP จึงจัดเตรียมวิธีง่ายๆ ในกำหนดการเรียกกลับแบบกำหนดเอง ไม่ว่าจะเป็นพอยน์เตอร์ฟังก์ชัน อ็อบเจ็กต์ฟังก์ชัน หรือฟังก์ชันเสมือน แม้ว่าจะมีเฟรมเวิร์กอยู่ ซึ่งอาจจะใช้งานยากกว่า เช่น Jace, JunC++ion, JCC, jni.hpp หรือ Scapix ที่สามารถแมป Java APIs ที่สมบูรณ์กับ C++ ได้ เนื่องจากการเรียกใช้เมธอด Java จากโค้ดเนทีฟต้องใช้เวลาอย่างน้อย ลำดับความสำคัญของเวลามากกว่าวิธีอื่น ๆ ในความคิดของฉันมันไม่สมเหตุสมผลเลยที่จะส่งออกเช่นเดียวกับ API ที่ออกแบบมาเพื่อใช้ใน Java อย่างไรก็ตาม สมมติว่าเราต้องการดำเนินการบางอย่างใน Java โดยวางแผนที่จะรวมมันไว้ในฟังก์ชันชื่อ foo()
ที่เรียกเมธอดบางอย่างภายในคลาส Foo
เราสามารถเขียนโค้ดต่อไปนี้ในไฟล์ชื่อ foo.cpp
โดยดูแลในการเริ่มต้น JVM หากจำเป็นด้วย JavaCPP_init()
หรือโดยวิธีอื่นใด:
#include <iostream>#include "jniFoo.h"int main() { JavaCPP_init(0, NULL); ลอง { foo(6, 7); } จับ (std::ข้อยกเว้น &e) { std::cout << e.what() << std::endl; } JavaCPP_uninit(); -
จากนั้นเราอาจประกาศฟังก์ชันนั้นให้กับเมธอด call()
หรือ apply()
ที่กำหนดไว้ใน FunctionPointer
ดังนี้
import org.bytedeco.javacpp.*;import org.bytedeco.javacpp.annotation.*;@Platform(include="<algorithm>")@Namespace("std")คลาสสาธารณะ Foo { คงที่ { Loader.load(); } } public static class Callback ขยาย FunctionPointer { // Loader.load() และ allowance() จำเป็นเฉพาะเมื่อมีการสร้างอินสแตนซ์แบบคงที่อย่างชัดเจน { Loader.load(); } การป้องกันการโทรกลับ () { จัดสรร (); } } จัดสรรโมฆะพื้นเมืองส่วนตัว (); @Name สาธารณะ ("foo") การโทรบูลีน (int a, int b) พ่นข้อยกเว้น { โยนข้อยกเว้นใหม่ ("bar" + a * b); - } // นอกจากนี้เรายังสามารถส่ง (หรือรับ) FunctionPointer เป็นอาร์กิวเมนต์ไปยัง (หรือคืนค่าจาก) ฟังก์ชั่นอื่น ๆ สาธารณะ static Native void stable_sort(IntPointer ก่อน, IntPointer สุดท้าย, Callback เปรียบเทียบ); // และเพื่อส่ง (หรือรับ) มันเป็นวัตถุฟังก์ชัน C ++ ใส่คำอธิบายประกอบด้วย @ByVal หรือ @ByRef การเรียงลำดับโมฆะดั้งเดิมแบบคงที่สาธารณะ (IntPointer ก่อน, IntPointer สุดท้าย, @ByVal Callback เปรียบเทียบ); -
เนื่องจากฟังก์ชันต่างๆ มีพอยน์เตอร์ด้วย เราจึงสามารถใช้ FunctionPointer
instance ตามลำดับได้ ในลักษณะที่คล้ายคลึงกับประเภท FunPtr
ของ Haskell FFI แต่ที่ java.lang.Throwable
object ใดๆ ที่ถูกโยนออกมาจะถูกแปลเป็น std::exception
การสร้างและรันโค้ดตัวอย่างนี้ด้วยคำสั่งเหล่านี้ภายใต้ Linux x86_64 จะให้ผลลัพธ์ที่คาดหวัง:
$ java -jar javacpp.jar Foo.java -ส่วนหัว $ g++ -I/usr/lib/jvm/java/include/ -I/usr/lib/jvm/java/include/linux/ foo.cpp linux-x86_64/libjniFoo.so -o foo $ ./fo java.lang.Exception: บาร์ 42
ในตัวอย่างนี้ ออบเจ็กต์ FunctionPointer
ถูกสร้างขึ้นโดยปริยาย แต่หากต้องการเรียกใช้ตัวชี้ฟังก์ชันดั้งเดิม เราสามารถกำหนดวัตถุที่มีเมธอด native call()/apply()
แทน และสร้างอินสแตนซ์ได้อย่างชัดเจน คลาสดังกล่าวสามารถขยายใน Java เพื่อสร้างการเรียกกลับได้ และเช่นเดียวกับอ็อบเจ็กต์ Pointer
ปกติอื่น ๆ จะต้องได้รับการจัดสรรด้วยวิธี native void allocate()
ดังนั้นโปรดอย่าลืมอ้างอิงถึงการอ้างอิงใน Java เนื่องจากสิ่งเหล่านั้นจะถูกรวบรวมขยะ . เป็นโบนัส FunctionPointer.call()/apply()
แมปในความเป็นจริงกับ operator()
ของวัตถุฟังก์ชัน C ++ ที่เราสามารถส่งผ่านไปยังฟังก์ชันอื่น ๆ โดยการใส่คำอธิบายประกอบพารามิเตอร์ด้วย @ByVal
หรือ @ByRef
เช่นเดียวกับ sort()
ฟังก์ชั่นในตัวอย่างข้างต้น
นอกจากนี้ยังสามารถทำสิ่งเดียวกันกับฟังก์ชันเสมือนได้ ไม่ว่าจะ "บริสุทธิ์" หรือไม่ก็ตาม พิจารณาคลาส C++ ต่อไปนี้ที่กำหนดไว้ในไฟล์ชื่อ Foo.h
:
#include <stdio.h>คลาสฟู {สาธารณะ: int n; Foo(int n) : n(n) { } virtual ~Foo() { } virtual void bar() { printf("Callback in C++ (n == %d)n", n); - }; โทรกลับเป็นโมฆะ (Foo *foo) { ฟู->บาร์(); -
ฟังก์ชัน Foo::bar()
สามารถเขียนทับได้ใน Java หากเราประกาศวิธีการในคลาสเพียร์ว่าเป็น native
หรือ abstract
และใส่คำอธิบายประกอบด้วย @Virtual
เช่น:
นำเข้า org.bytedeco.javacpp.*; นำเข้า org.bytedeco.javacpp.annotation.*;@Platform (รวม = "Foo.h") คลาสสาธารณะ VirtualFoo { คงที่ { Loader.load (); } คลาสคงที่สาธารณะ Foo ขยายตัวชี้ { คงที่ { Loader.load (); } สาธารณะ Foo (int n) { จัดสรร (n); } } จัดสรรโมฆะพื้นเมืองส่วนตัว (int n); @NoOffset สาธารณะ int n(); สาธารณะชนพื้นเมือง Foo n(int n); @ แถบโมฆะพื้นเมืองสาธารณะเสมือน (); } การโทรกลับเป็นโมฆะแบบคงที่สาธารณะ (Foo foo); โมฆะคงที่สาธารณะ main (String [] args) { Foo foo = new Foo (13); Foo foo2 = new Foo(42) { public void bar() { System.out.println("โทรกลับใน Java (n == " + n() + ")"); - - foo.bar(); foo2.bar(); โทรกลับ (foo); โทรกลับ (foo2); - -
ซึ่งส่งผลให้สิ่งที่เราจะถือว่าโดยธรรมชาติ:
$ java -jar javacpp.jar VirtualFoo.java -exec โทรกลับใน C ++ (n == 13) โทรกลับใน Java (n == 42) โทรกลับใน C ++ (n == 13) โทรกลับใน Java (n == 42)
วิธีที่ง่ายที่สุดในการทำงานคือ Avian ที่คอมไพล์ด้วยไลบรารีคลาส OpenJDK ดังนั้นเรามาเริ่มกันก่อน หลังจากสร้างและสร้างโปรแกรมตามที่อธิบายไว้ข้างต้น โดยไม่ต้องแก้ไขเพิ่มเติมใดๆ เราสามารถรันโปรแกรมได้โดยตรงด้วยคำสั่งนี้:
$ /path/to/avian-dynamic -Xbootclasspath/a:javacpp.jar <MainClass>
อย่างไรก็ตาม ในกรณีของ Android เราจำเป็นต้องทำงานเพิ่มขึ้นอีกเล็กน้อย สำหรับระบบการสร้างบรรทัดคำสั่งที่ใช้ Ant ภายในไดเร็กทอรีของโปรเจ็กต์:
คัดลอกไฟล์ javacpp.jar
ลงในไดเร็กทอรีย่อย libs/
และ
รันคำสั่งนี้เพื่อสร้างไฟล์ไลบรารี *.so
ใน libs/armeabi/
:
$ java -jar libs/javacpp.jar -classpath bin/ -classpath bin/classes/ > -properties <android-arm|android-x86> -Dplatform.root=/path/to/android-ndk/ > -Dplatform .compiler=/path/to/<arm-linux-androideabi-g++|i686-linux-android-g++> -d libs/<อาร์มีอาบี|x86>/
เพื่อให้ทุกอย่างเป็นอัตโนมัติ เราอาจแทรกคำสั่งนั้นลงในไฟล์ build.xml
อีกทางหนึ่ง สำหรับการทำงานร่วมกับ Android Studio เราสามารถใช้ Gradle JavaCPP ได้
ในทำนองเดียวกันสำหรับ RoboVM สมมติว่าคลาสที่คอมไพล์อยู่ในไดเร็กทอรีย่อยของ classes
:
คัดลอกไฟล์ javacpp.jar
ลงในไดเร็กทอรีโปรเจ็กต์ และ
รันคำสั่งต่อไปนี้เพื่อสร้างไฟล์ไบนารีดั้งเดิม:
$ java -jar javacpp.jar -cp คลาส/ -properties <ios-arm|ios-x86> -o lib $ /path/to/robovm -arch <thumbv7|x86> -os ios -cp javacpp.jar:classes/ -libs คลาส/<ios-arm|ios-x86>/lib.o <MainClass>
และแทนที่จะเป็น Loader.load()
ไลบรารีควรโหลดด้วย System.load("lib.o")
ในกรณีนี้ และอาจไม่จำเป็นเลย
หัวหน้าโครงการ: Samuel Audet samuel.audet at
gmail.com
ไซต์ผู้พัฒนา: https://github.com/bytedeco/javacpp
กลุ่มสนทนา: http://groups.google.com/group/javacpp-project