java - 用于 Java/JNA 对象引用的 C 中的 C++ API 包装器

标签 java c++ c jna

我正在为我自己的 C++ 库编写一个 C API 包装器。我想在 Java 中将 C-API 与 JNA 一起使用。
对于 C++ 库中的每个类,我将构造函数包装在 C 函数调用中,并返回一个句柄。有了这个句柄,我可以调用 C 函数,这些函数包装了对象的成员函数(将句柄转换为它的类并调用它的成员函数)。
这工作正常。
为了简化我在 Java 中的 API 的使用,我为所有 C++ 类编写了类。这些 Java 类仅包含一个 JNA 指针(存储我的对象句柄)和 C++ 调用的公共(public)成员函数。 Java 类的每个成员函数调用包装 C++ 对象的成员函数的 C 函数。
现在我面临一个问题:
当 C++ 成员函数返回对另一个 C++ 对象的引用时,我的 C-API 将返回指针。但是如何用这个指针构造我的 Java 对象呢?创建一个新的 Java 对象并将其指针值设置为返回值是否合法。尽管在 Java 中现在有 2 个对象,它们持有相同的指针值。
或者我应该在 Java 中有一个 List,跟踪所有 C++ 对象,这样我就可以从这个列表中获取 C++ 返回的引用。
垃圾收集器何时从堆中删除 JNA-Pointer 对象?什么时候删除我的 C++ 对象?
(一些代码来指定我的答案)
在我的 C-API 中,我有以下函数来创建一个对象

    int C_API_createFoo(void **handle) {
        try {
            *handle = reinterpret_cast<void*>(new Foo());
            return SUCCESS;
        } catch (std::exception &err) {
            std::cout << err.what() << std::endl;
            return ERROR;
        }
    }
在 Java/JNA 中,我加载我的 C-lib 并使用以下代码调用该函数:
    public Pointer createFoo(){
        PointerByReference pRef = new PointerByReference();
        int eC = MyClib.INSTANCE.C_API_createFoo(pRef);
        CHECK(eC);
        return pRef.getValue();
    }
Foo 在哪里:
    public class Foo{
    private Pointer handle;
    public Foo(){
            this.handle = createFoo();
        }
    public Foo(Pointer handle){
            this.handle = handle;
        }
    }
所以现在上课Foo存储对象句柄,可以用作普通的 Java 对象。
但建议我有一个 C++ 类,例如 FooWorker它有一个类型为 Foo 的成员变量并具有 Foo 的 getter 函数:
    int C_API_FooWorker_getFoo(void* FooWorkerHandle, void** fooHandle) {
        try {
            FooWorker* fW= reinterpret_cast<FooWorker*>(FooWorkerHandle);
            Foo *foo = fW.getFoo();
            *fooHandle = reinterpret_cast<void*>(foo);
            return SUCCESS;
        } catch (std::exception &err) {
            std::cout << err.what() << std::endl;
            return ERROR;
        }
    }
在 JNA 中映射如下:
    public Foo FooWorker_getFoo(Pointer fooWorkerHandle){
        PointerByReference pRef = new PointerByReference();
        int eC = MyClib.INSTANCE.C_API_FooWorker_getFoo(fooWorkerHandle, pRef);
        CHECK(eC);
        return new Foo(pRef.getValue());
    }
所以当我调用函数 FooWorker_getFoo(Pointer fooWorkerHandle) fooWorkerHandle 多次使用相同的值,然后我将获得多个 Foo 类型的 Java 对象,但它们的句柄都包含相同的值(即相同的 C++ 对象引用)。
问题
我现在的问题是:这是保存还是会引起任何痛苦?所有这些 Foo 对象(最后是唯一的 C++ 对象,所有指向的句柄)都会被垃圾收集器收集吗?

最佳答案

假设您有一个用于释放 Foo 的本地方法,您编写的代码不会引起任何问题。 C 中的对象。
您的描述似乎缺少的是 Java 端内存和 native 内存之间的区别。它们是单独分配和处理的。一些关键点:

  • 调用new Foo()里面C_API_createFoo()函数为该对象分配 native 端内存。您还没有显示释放该内存的代码;或任何关于该对象将持续多长时间的保证。这不是 Java 自动发生的事情,您需要确保 native API 允许您在使用完对象后同时保留和处置对象。
  • 您可能应该有对应于 JNA 映射的 C 方法,例如 dispose(PointerByReference pRef) (或类似的)您可以将指针传递给 Foo 以释放该内存。访问指针时,某些 API 会根据计数器自动处理,您可能需要 keep(PointerByReference pRef) 之类的东西在 native 端增加该计数器并确保它不会过早释放该内存。

  • 您在 Java 端创建的对象只是普通的 Java 对象,只要不再引用它们就会被垃圾回收。
  • 特别是,如果该对象是 PointerByReferencePointer在你的 Java 中 Foo对象,它具有 native 指针地址的值,但是当它被 GC 时,它对该 native 地址没有做任何特别的事情,你必须故意这样做。
  • 为了“有意”释放它(与任何 native 句柄一样)并避免句柄泄漏,我更喜欢 try-with-resources 或 try-finally block 。


  • 您不需要创建 Java 类来封装 Pointer .您可能更喜欢扩展 PointerType类(正是这样做的),它为您提供了一个装饰指针,例如,
    class FooHandle extends PointerType() {
    }
    
    然后你可以改变你的createFoo()方法:
    public FooHandle createFoo(){
        PointerByReference pRef = new PointerByReference();
        int eC = MyClib.INSTANCE.C_API_createFoo(pRef);
        CHECK(eC);
        // call code to make sure the native object is persisted
        return new FooHandle(pRef.getValue());
    }
    
    正如我提到的,你需要一个 disposeFoo()方法完成后,释放 native 端内存。
    对于仅使用工作程序的第二个示例,您不会创建新的 Foo()在 native 端的对象,你只是调用一个“get”方法。这意味着您只是在检索指针地址的拷贝,并且您可以根据需要获得(双关语)尽可能多的地址,没有任何问题。

    关于java - 用于 Java/JNA 对象引用的 C 中的 C++ API 包装器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64699116/

    相关文章:

    c - 如何计算long long/int

    CodeChef 购买新的平板电脑解决方案

    python - mujoco_py.MjModel(<filepath>) 在哪里定义?

    java - 如何在 POJO 类中存储 TestNG 数据提供程序值

    java - SimpleDateFormat 给出错误的日期而不是错误

    java - 写入文件时附加 JSONObjects

    c++ - 亚洲 : is it safe to restart a async_read_some/async_write_some after cancel

    java - Spring 中的 ServerSocket 范围?

    c++ - 表达式 _BLOCK_TYPE_IS_VALID(pHead->nBlockUse) 错误

    c++ - cublasSdot 的工作速度比 cublasSgemm 慢