访问C++;使用JNI从Java获取对象数组

访问C++;使用JNI从Java获取对象数组,java,c++,java-native-interface,Java,C++,Java Native Interface,在我和的回答之后,我想以相反的方式尝试并强化前面的问题: 我想执行以下步骤: Java中的缓冲区分配 用C填充数组++ 在Java中编辑值 用C做一些数字运算++ 读取Java中的值 考虑C++中的以下类: #pragma once #pragma pack(push, 8) class Point { public: double x; double y; Point(); virtual ~Point(); void ebeProduct(Poin

在我和的回答之后,我想以相反的方式尝试并强化前面的问题:

我想执行以下步骤:

  • Java中的缓冲区分配
  • 用C填充数组++
  • 在Java中编辑值
  • 用C做一些数字运算++
  • 读取Java中的值
  • 考虑C++中的以下类:

    #pragma once
    #pragma pack(push, 8)
    class Point
    {
    
    public:
        double x;
        double y;
        Point();
        virtual ~Point();
    
        void ebeProduct(Point &other) {
            x = x * other.x;
            y = y * other.y;
        }
    };
    
    
    
    Point::Point()
    {
    }
    
    
    Point::~Point()
    {
    }
    
    #pragma pack(pop)
    
    以下代码段:

    Point point = Point();
    std::cout << "Size of Point: " << sizeof(Point) << std::endl;
    std::cout << "Address is: " << &point << std::endl;
    std::cout << "x address is: " << &(point.x) << " and offset is: " << (long long)&(point.x) - (long long)&point << std::endl;
    std::cout << "y address is: " << &(point.y) << " and offset is: " << (long long)&(point.y) - (long long)&point << std::endl;
    
    现在我可以假设Java中的以下大小:

    final int NUMBER_OF_POINTS = 10;
    final int SIZE_OF_POINT = 24;
    final int X_OFFSET = 8;
    final int Y_OFFSET = 16;
    
    for (int i = 0; i < NUMBER_OF_POINTS; i++) {
      System.err.print(unsafe.getDouble(address + i * SIZE_OF_POINT + X_OFFSET));
      System.err.print(" ; ");
      System.err.println(unsafe.getDouble(address + i * SIZE_OF_POINT + Y_OFFSET));
    }
    
    接下来,我将使用
    unsafe
    分配一个内存区域,并调用
    公共静态本机void allocatePoints(长地址,int numberOfPoints)以便用
    对象填充它们:

    long address = unsafe.allocateMemory(NUMBER_OF_POINTS * SIZE_OF_POINT);
    allocatePoints(address, NUMBER_OF_POINTS);
    

    填充点的C++代码:

    Point * points = (Point *) address;
    for (int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++) {
        new(points + pointIndex) Point();
    }
    
    这些阶段实际上会打印正确的结果。

    我的问题是,这些阶段是否合理:我能假定给定的偏移量是正确的(在同一台机器上),并且直接将值写入java中的缓冲区,并从C++中读取这些对象,总是会得到正确的结果吗?

    < P>我一般看到三个潜在的陷阱:

    <> > > > >填充> VTABLE >代码>点>代码>可能会使大小< /强>:C++编译器可能决定将<代码>点>代码>成员对齐到它们想要的对齐(例如,A代码>双< /代码>要8字节对齐,而<代码> char < /C> >不指定任何对齐。

    同样,如果C++对象包含任何<代码>虚拟< /COD>方法(如您的示例所示),则 对象的前面将有一个vtable。你需要考虑到这一点 分配内存和访问单个成员时的帐户

  • 对齐
    可能会弄乱数组的步幅
    :假设类的最后一个元素有一个额外的
    字符
    ,那么下一个元素将对齐到8字节的最接近倍数。(换句话说,数组跨距为8+8+1(数据)+7字节填充)

    在分配内存和访问单个成员时,都需要考虑这一点

  • sun.misc.Unsafe
    即将退出。这段代码最终可能会停止在JDK11及更高版本中工作


  • 1+2:关于对齐:我使用的是
    #pragma pack(16)
    指令,它应该保证所有内容都对齐。3:如果
    Unsafe
    将被弃用,并且没有其他方法分配大块内存,我将使用ByteBuffer。
    for (int i = 0; i < NUMBER_OF_POINTS; i++) {
      unsafe.putDouble(address + i * SIZE_OF_POINT + X_OFFSET, i);
      unsafe.putDouble(address + i * SIZE_OF_POINT + Y_OFFSET, i * 2);
    }
    
    for (int i = 0; i < NUMBER_OF_POINTS; i++) {
      System.err.print(unsafe.getDouble(address + i * SIZE_OF_POINT + X_OFFSET));
      System.err.print(" ; ");
      System.err.println(unsafe.getDouble(address + i * SIZE_OF_POINT + Y_OFFSET));
    }