首页 > 编程语言 >javacpp是什么?

javacpp是什么?

时间:2024-02-24 21:36:21浏览次数:39  
标签:Java void C++ public javacpp 什么 native

javaCPP提供了在Java中高效访问本地C++的方法。采用JNI技术实现,支持所有Java实现包括Android系统,Avian 和 RoboVM。 JavaCPP提供了一系列的Annotation将Java代码映射...

Introduction

JavaCPP provides efficient access to native C++ inside Java, not unlike the way some C/C++ compilers interact with assembly language. No need to invent new languages such as with SWIGSIPC++/CLICython, or RPython. Instead, similar to what cppyy strives to do for Python, it exploits the syntactic and semantic similarities between Java and C++. Under the hood, it uses JNI, so it works with all implementations of Java SE, in addition to AndroidAvian, and RoboVM (instructions).

More specifically, when compared to the approaches above or elsewhere (CableSwigJNIGeneratorAppcxxwrapJNIWrapperPlatform InvokeGlueGenLWJGL GeneratorJNIDirectctypesJNAJNIEasyJniMarshallJNativeJ/InvokeHawtJNIJNRBridJCFFIfficxxCppSharpcgopybind11rust-bindgenPanama Native, etc.), it maps naturally and efficiently many common features afforded by the C++ language and often considered problematic, including overloaded operators, class and function templates, callbacks through function pointers, function objects (aka functors), virtual functions and member function pointers, nested struct definitions, variable length arguments, nested namespaces, large data structures containing arbitrary cycles, virtual and multiple inheritance, passing/returning by value/reference/string/vector, anonymous unions, bit fields, exceptions, destructors and shared or unique pointers (via either try-with-resources or garbage collection), and documentation comments. Obviously, neatly supporting the whole of C++ would require more work (although one could argue about the intrinsic neatness of C++), but we are releasing it here as a proof of concept.

As a case in point, we have already used it to produce complete interfaces to OpenCV, FFmpeg, libdc1394, PGR FlyCapture, OpenKinect, videoInput, ARToolKitPlus, Leptonica, Tesseract, GSL, LLVM, HDF5, MKL, CUDA, Caffe, MXNet, TensorFlow, System APIs, and others as part of the JavaCPP Presets subproject, also demonstrating early parsing capabilities of C/C++ header files that show promising and useful results.

Please feel free to ask questions on the mailing list or the discussion forum if you encounter any problems with the software! I am sure it is far from perfect...

Downloads

Archives containing JAR files are available as releases.

We can also have everything downloaded and installed automatically with:

  • Maven (inside the pom.xml file)
  <dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacpp</artifactId>
    <version>1.5.10</version>
  </dependency>
 
  • Gradle (inside the build.gradle file)
  dependencies {
    implementation group: 'org.bytedeco', name: 'javacpp', version: '1.5.10'
  }
 
  • Leiningen (inside the project.clj file)
  :dependencies [
    [org.bytedeco/javacpp "1.5.10"]
  ]
 
  • sbt (inside the build.sbt file)
  libraryDependencies += "org.bytedeco" % "javacpp" % "1.5.10"
 

Another option available to Gradle users is Gradle JavaCPP, and similarly for Scala users there is SBT-JavaCPP.

Required Software

To use JavaCPP, you will need to download and install the following software:

To produce binary files for Android 4.0 or newer, you will also have to install:

And similarly to target iOS, you will need to install either:

To modify the source code, please note that the project files were created for:

Finally, because we are dealing with native code, bugs can easily crash the virtual machine. Luckily, the HotSpot VM provides some tools to help us debug under those circumstances:

Getting Started

To understand how JavaCPP is meant to be used, one should first take a look at the Mapping Recipes for C/C++ Libraries, but a high-level overview of the Basic Architecture is also available to understand the bigger picture. The repository of the JavaCPP Presets further provides complex examples that we can use as templates, but it also includes a wiki page on how to Create New Presets that explains their structure in detail along with a small but complete sample project from which one can start experimenting with.

Key Use Cases

To implement native methods, JavaCPP generates appropriate code for JNI, and passes it to the C++ compiler to build a native library. At no point do we need to get our hands dirty with JNI, makefiles, or other native tools. The important thing to realize here is that, while we do all customization inside the Java language using annotations, JavaCPP produces code that has zero overhead compared to manually coded JNI functions (verify the generated .cpp files to convince yourself). Moreover, at runtime, the Loader.load() method automatically loads the native libraries from Java resources, which were placed in the right directory by the building process. They can even be archived in a JAR file, it changes nothing. Users simply do not need to figure out how to make the system load the files. These characteristics make JavaCPP suitable for either

In addition to the few examples provided below, to learn more about how to use the features of this tool, please refer to the Mapping Recipes for C/C++ Libraries as well as the source code of the JavaCPP Presets for examples. For more information about the API itself, one may refer to the documentation generated by Javadoc.

As a matter of course, this all works with the Scala language as well, but to make the process even smoother, it should not be too hard to add support for "native properties", such that declarations like @native var could generate native getter and setter methods...

Accessing Native APIs

The most common use case involves accessing some native library written for C++, for example, inside a file named NativeLibrary.h containing this C++ class:

#include <string>

namespace NativeLibrary {
    class NativeClass {
        public:
            const std::string& get_property() { return property; }
            void set_property(const std::string& property) { this->property = property; }
            std::string property;
    };
}
 

To get the job done with JavaCPP, we can easily define a Java class such as this one--although one could use the Parser to produce it from the header file as demonstrated by the JavaCPP Presets subproject, following the principles outlined in the Mapping Recipes for C/C++ Libraries:

import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;

@Platform(include="NativeLibrary.h")
@Namespace("NativeLibrary")
public class NativeLibrary {
    public static class NativeClass extends Pointer {
        static { Loader.load(); }
        public NativeClass() { allocate(); }
        private native void allocate();

        // to call the getter and setter functions 
        public native @StdString String get_property(); public native void set_property(String property);

        // to access the member variable directly
        public native @StdString String property();     public native void property(String property);
    }

    public static void main(String[] args) {
        // Pointer objects allocated in Java get deallocated once they become unreachable,
        // but C++ destructors can still be called in a timely fashion with Pointer.deallocate()
        NativeClass l = new NativeClass();
        l.set_property("Hello World!");
        System.out.println(l.property());
    }
}
 

After compiling the Java source code in the usual way, we also need to build using JavaCPP before executing it, or we can let it do everything as follows:

$ java -jar javacpp.jar NativeLibrary.java -exec
Hello World!
 

Using Complex C++ Types

To demonstrate its relative ease of use even in the face of complex data types, imagine we had a C++ function that took a vector<vector<void*> > as argument. To support that type, we could define a bare-bones class like this:

import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;

@Platform(include="<vector>")
public class VectorTest {

    @Name("std::vector<std::vector<void*> >")
    public static class PointerVectorVector extends Pointer {
        static { Loader.load(); }
        public PointerVectorVector()       { allocate();  }
        public PointerVectorVector(long n) { allocate(n); }
        public PointerVectorVector(Pointer p) { super(p); } // this = (vector<vector<void*> >*)p
        private native void allocate();                  // this = new vector<vector<void*> >()
        private native void allocate(long n);            // this = new vector<vector<void*> >(n)
        @Name("operator=")
        public native @ByRef PointerVectorVector put(@ByRef PointerVectorVector x);

        @Name("operator[]")
        public native @StdVector PointerPointer get(long n);
        public native @StdVector PointerPointer at(long n);

        public native long size();
        public native @Cast("bool") boolean empty();
        public native void resize(long n);
        public native @Index long size(long i);                   // return (*this)[i].size()
        public native @Index @Cast("bool") boolean empty(long i); // return (*this)[i].empty()
        public native @Index void resize(long i, long n);         // (*this)[i].resize(n)

        public native @Index Pointer get(long i, long j);  // return (*this)[i][j]
        public native void put(long i, long j, Pointer p); // (*this)[i][j] = p
    }

    public static void main(String[] args) {
        PointerVectorVector v = new PointerVectorVector(13);
        v.resize(0, 42); // v[0].resize(42)
        Pointer p = new Pointer() { { address = 0xDEADBEEFL; } };
        v.put(0, 0, p);  // v[0][0] = p

        PointerVectorVector v2 = new PointerVectorVector().put(v);
        Pointer p2 = v2.get(0).get(); // p2 = *(&v[0][0])
        System.out.println(v2.size() + " " + v2.size(0) + "  " + p2);

        v2.at(42);
    }
}
 

Executing that program using this command produces the following output:

$ java -jar javacpp.jar VectorTest.java -exec
13 42  org.bytedeco.javacpp.Pointer[address=0xdeadbeef,position=0,limit=0,capacity=0,deallocator=null]
Exception in thread "main" java.lang.RuntimeException: vector::_M_range_check: __n (which is 42) >= this->size() (which is 13)
	at VectorTest$PointerVectorVector.at(Native Method)
	at VectorTest.main(VectorTest.java:44)
 

Optimizing Code Performance

Other times, we may wish to code in C++ (including CUDA) for performance reasons. Suppose our profiler had identified that a method named Processor.process() took 90% of the program's execution time:

public class Processor {
    public static void process(java.nio.Buffer buffer, int size) {
        System.out.println("Processing in Java...");
        // ...
    }

    public static void main(String[] args) {
        process(null, 0);
    }
}
 

After many days of hard work and sweat, the engineers figured out some hacks and managed to make that ratio drop to 80%, but you know, the managers were still not satisfied. So, we could try to rewrite it in C++ (or even assembly language for that matter via the inline assembler) and place the resulting function in a file named say Processor.h:

#include <iostream>

static inline void process(void *buffer, int size) {
    std::cout << "Processing in C++..." << std::endl;
    // ...
}
 

After adjusting the Java source code to something like this:

import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;

@Platform(include="Processor.h")
public class Processor {
    static { Loader.load(); }

    public static native void process(java.nio.Buffer buffer, int size);

    public static void main(String[] args) {
        process(null, 0);
    }
}
 

It would then compile and execute like this:

$ java -jar javacpp.jar Processor.java -exec
Processing in C++...
 

Creating Callback Functions

Some applications also require a way to call back into the JVM from C/C++, so JavaCPP provides a simple way to define custom callbacks, either as function pointers, function objects, or virtual functions. Although there exist frameworks, which are arguably harder to use, such as JaceJunC++ionJCCjni.hpp, or Scapix that can map complete Java APIs to C++, since invoking a Java method from native code takes at least an order of magnitude more time than the other way around, it does not make much sense in my opinion to export as is an API that was designed to be used in Java. Nevertheless, suppose we want to perform some operations in Java, planning to wrap that into a function named foo() that calls some method inside class Foo, we can write the following code in a file named foo.cpp, taking care to initialize the JVM if necessary with either JavaCPP_init() or by any other means:

#include <iostream>
#include "jniFoo.h"

int main() {
    JavaCPP_init(0, NULL);
    try {
        foo(6, 7);
    } catch (std::exception &e) {
        std::cout << e.what() << std::endl;
    }
    JavaCPP_uninit();
}
 

We may then declare that function to a call() or apply() method defined in a FunctionPointer as follows:

import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;

@Platform(include="<algorithm>")
@Namespace("std")
public class Foo {
    static { Loader.load(); }

    public static class Callback extends FunctionPointer {
        // Loader.load() and allocate() are required only when explicitly creating an instance
        static { Loader.load(); }
        protected Callback() { allocate(); }
        private native void allocate();

        public @Name("foo") boolean call(int a, int b) throws Exception { 
            throw new Exception("bar " + a * b);
        }
    }

    // We can also pass (or get) a FunctionPointer as argument to (or return value from) other functions
    public static native void stable_sort(IntPointer first, IntPointer last, Callback compare);

    // And to pass (or get) it as a C++ function object, annotate with @ByVal or @ByRef
    public static native void sort(IntPointer first, IntPointer last, @ByVal Callback compare);
}
 

Since functions also have pointers, we can use FunctionPointer instances accordingly, in ways similar to the FunPtr type of Haskell FFI, but where any java.lang.Throwable object thrown gets translated to std::exception. Building and running this sample code with these commands under Linux x86_64 produces the expected output:

$ java -jar javacpp.jar Foo.java -header
$ g++ -I/usr/lib/jvm/java/include/ -I/usr/lib/jvm/java/include/linux/ foo.cpp linux-x86_64/libjniFoo.so -o foo
$ ./foo
java.lang.Exception: bar 42
 

In this example, the FunctionPointer object gets created implicitly, but to call a native function pointer, we could define one that instead contains a native call()/apply() method, and create an instance explicitly. Such a class can also be extended in Java to create callbacks, and like any other normal Pointer object, must be allocated with a native void allocate() method, so please remember to hang on to references in Java, as those will get garbage collected. As a bonus, FunctionPointer.call()/apply() maps in fact to an overloaded operator() of a C++ function object that we can pass to other functions by annotating parameters with @ByVal or @ByRef, as with the sort() function in the example above.

It is also possible to do the same thing with virtual functions, whether "pure" or not. Consider the following C++ class defined in a file named Foo.h:

#include <stdio.h>

class Foo {
public:
    int n;
    Foo(int n) : n(n) { }
    virtual ~Foo() { }
    virtual void bar() {
        printf("Callback in C++ (n == %d)\n", n);
    }
};

void callback(Foo *foo) {
    foo->bar();
}
 

The function Foo::bar() can be overridden in Java if we declare the method in the peer class either as native or abstract and annotate it with @Virtual, for example:

import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;

@Platform(include="Foo.h")
public class VirtualFoo {
    static { Loader.load(); }

    public static class Foo extends Pointer {
        static { Loader.load(); }
        public Foo(int n) { allocate(n); }
        private native void allocate(int n);

        @NoOffset public native int n(); public native Foo n(int n);
        @Virtual  public native void bar();
    }

    public static native void callback(Foo foo);

    public static void main(String[] args) {
        Foo foo = new Foo(13);
        Foo foo2 = new Foo(42) {
            public void bar() {
                System.out.println("Callback in Java (n == " + n() + ")");
            }
        };
        foo.bar();
        foo2.bar();
        callback(foo);
        callback(foo2);
    }
}
 

Which outputs what one would naturally assume:

$ java -jar javacpp.jar VirtualFoo.java -exec
Callback in C++ (n == 13)
Callback in Java (n == 42)
Callback in C++ (n == 13)
Callback in Java (n == 42)
 

Instructions for Android, Avian, and RoboVM

The easiest one to get working is Avian compiled with OpenJDK class libraries, so let's start with that. After creating and building a program as described above, without any further modifications, we can directly execute it with this command:

$ /path/to/avian-dynamic -Xbootclasspath/a:javacpp.jar <MainClass>
 

However, in the case of Android, we need to do a bit more work. For the command line build system based on Ant, inside the directory of the project:

  1. Copy the javacpp.jar file into the libs/ subdirectory, and
  2. Run this command to produce the *.so library files in 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/<armeabi|x86>/
 

To make everything automatic, we may also insert that command into the build.xml file. Alternatively, for integration with Android Studio, we can use Gradle JavaCPP.

Similarly for RoboVM, assuming that the compiled classes are in the classes subdirectory:

  1. Copy the javacpp.jar file into the project directory, and
  2. Run the following commands to produce the native binary file:
$ java -jar javacpp.jar -cp classes/ -properties <ios-arm|ios-x86> -o lib
$ /path/to/robovm -arch <thumbv7|x86> -os ios -cp javacpp.jar:classes/ -libs classes/<ios-arm|ios-x86>/lib.o <MainClass>
 

And instead of Loader.load(), the library should be loaded with System.load("lib.o"), in this case, and might not be required at all.


Project lead: Samuel Audet samuel.audet at gmail.com
Developer site: https://github.com/bytedeco/javacpp
Discussion group: http://groups.google.com/group/javacpp-project

JavaCPP快速入门:https://blog.csdn.net/boling_cavalry/article/details/118636417

github:https://github.com/bytedeco/javacpp

标签:Java,void,C++,public,javacpp,什么,native
From: https://www.cnblogs.com/2008nmj/p/18031611

相关文章

  • 什么是转换矩阵以及如何使用它
    项目地址:Pdfium.Net:https://github.com/1000374/Pdfium.NetPdfiumViewer:https://github.com/1000374/PdfiumViewer当您使用PDFium库处理PDF文件中的对象时,您可以使用SetMatrix函数以各种方式转换对象(通常是图像,但也包括任何其他嵌入对象)。使用变换矩阵,您可以旋转、平移(移......
  • 什么是句柄
    什么是句柄从广义上,能够从一个数值拎起一大堆数据的东西都可以叫做句柄。句柄的英文是"Handle",本义就是"柄",只是在计算机科学中,被特别地翻译成"句柄",其实还是个"柄"。形象为从一个小东西拎起一大堆东西;指针其实也是一种"句柄",只是由于指针同时拥有更特殊的含义——实实在在地对......
  • 开源的表单设计器拥有什么显著特点?
    开源的表单设计器的特点是什么?广州流辰信息是专业研发低代码技术平台的服务商,可以为企业提供系统开发、数据治理、数据分析各环节技术和方案支撑。为了帮助大家了解开源的表单设计器的相关优势特点,小编将为大家做一个详细介绍。什么是开源的表单设计器?都有哪些功能和作用?表单工具......
  • SSR解决了什么问题?有做过SSR吗?你是怎么做的?
    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助一、是什么Server-SideRendering 我们称其为SSR,意为服务端渲染指由服务侧完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程先来看看Web3个阶段的发展史:......
  • 什么是Java中的SPI机制
    SPI,全称ServiceProviderInterface,是Java中提供的一种服务发现机制它允许应用程序动态地加载和使用第三方提供的服务实现,而无需在代码中引用这些实现类。JavaSPI是基于接口编程思想的具体体现,通过将服务接口和其实现分离,从而具备更好的可扩展性和可维护性如何定义一个Java......
  • 一图揭秘为什么开发者都选择华为云软件开发生产线CodeArts
    华为云软件开发生产线CodeArts是一站式、全流程、安全可信的云原生DevSecOps云平台,集华为30年研发实践、前沿研发理念、先进研发工具为一体,覆盖需求、开发、测试、部署等软件交付全生命周期环节,为开发者打造全云化研发体验。体验通道→软件开发生产线CodeArts_DevOps_开发者平......
  • let、const、var、function所谓的”变量提升“、暂时性死区到底是什么
    今天看了大佬一个文章我用了两个月的时间才理解let-知乎(zhihu.com),文章中其实说得很清楚,还有大佬解决这个问题的整个心路历程。我这里做一个总结记录,专注于“变量提升”、暂时性死区这两个点做一个讨论。现象讨论下面这两段代码,我们都知道这段代码在控制台会打印undefined......
  • 机器学习策略篇:详解为什么是ML策略?(Why ML Strategy?)
    为什么是ML策略?从一个启发性的例子开始讲,假设正在调试的猫分类器,经过一段时间的调整,系统达到了90%准确率,但对的应用程序来说还不够好。可能有很多想法去改善的系统,比如,可能想去收集更多的训练数据吧。或者会说,可能的训练集的多样性还不够,应该收集更多不同姿势的猫咪图片,或者更......
  • 2024.2.22 梦想 在什么地方 总是那么令人向往
    字符串太难了。今天早上起来牙又疼了,很难受。UOJ461考虑当前已经把整张图分成了\(L,R\)两个点集,考虑extend一个点进来。可以使用二分的方式,具体的将未加入的点按任意顺序排列,二分一个前缀并断掉\(L,R\)与这个前缀的所有边,找到一个最小的前缀满足此时不连通,此时对于这个......
  • 什么是PendSV
    什么是PendSV一、什么是PendSVPendSV是可悬起异常,如果我们把它配置最低优先级,那么如果同时有多个异常被触发,它会在其他异常执行完毕后再执行,而且任何异常都可以中断它。更详细的内容在《Cortex-M3权威指南》里有介绍,下面我摘抄了一段。OS可以利用它“缓期执行”......