当前位置: 首页 > 知识库问答 >
问题:

如何利用JNA在Java对象中转换C++结构(由Mathlab编译)

颛孙俊
2023-03-14

我有这个C++结构和方法,它在.dll/.so中返回:

struct emxArray_real_T
{
  double *data;
  int *size;
  int allocatedSize;
  int numDimensions;
  boolean_T canFreeData;
};

emxArray_real_T *emxCreate_real_T(int rows, int cols)
{
  emxArray_real_T *emx;
  ***
  return emx;
}

我试着从Java通过南国防军给它打电话。我的目标是在Java接收这个结构“emxarray_real_t”,以获得对其字段的访问权。我从一个非常简单的代码开始,并以此结束,但仍然有问题--我收到了错误的数据在“大小”,“数据”等。

public interface TestJna extends Library {
    TestJna INSTANCE = (TestJna)Native.load(Platform.isWindows() ? "some.dll" : "some.so", TestJna.class);

    @Structure.FieldOrder({"allocatedSize","canFreeData","data", "numDimensions", "size"})
    public static class ContentsJna extends Structure {

        public ContentsJna() {
        }

        public int allocatedSize;
        public boolean canFreeData;

        private Pointer bufferData;
        private Pointer bufferSize;
        public Pointer data = new Memory(Double.SIZE);
        public int numDimensions;
        public Pointer size = new Memory(Integer.SIZE);

        public double[] getData() {
            Pointer p = data.getPointer(0);
            if (p == null) return null;
            return p.getDoubleArray(0, allocatedSize);
        }

        public void setData(double[] data) {
            Pointer p = this.data.getPointer(0);
            if (p == null) {
                p = bufferData = new Memory(data.length * 8);
                this.data.setPointer(0, bufferData);
            }
            p.write(0, data, 0, data.length);
        }

        public int[] getSize() {
            Pointer p = data.getPointer(0);
            if (p == null) return null;
            return p.getIntArray(0, allocatedSize);
        }

        public void setSize(int[] size) {
            Pointer p = this.size.getPointer(0);
            if (p == null) {
                p = bufferSize = new Memory(size.length * 4);
                this.size.setPointer(0, bufferSize);
            }
            p.write(0, size, 0, size.length);
        }
    }
    ContentsJna emxCreate_real_T(int rows, int cols);
}

我该怎么修好它?我遵循了这一指南,但它并没有帮助我完全解决问题:JNA本机html" target="_blank">函数调用和具有双指针/数组内存分配的结构

UPD16.02/21:@Matthias Bläsing,谢谢你这个伟大的解决方案--它帮助我认识到了自己的错误。与C++输出相比,我有一些错误的数据输出。也许我应该提供完整的C++代码:

struct emxArray_real_T
{
  double *data;
  int *size;
  int allocatedSize;
  int numDimensions;
  boolean_T canFreeData;
};

emxArray_real_T *emxCreate_real_T(int rows, int cols)
{
  emxArray_real_T *emx;
  int numEl;
  emxInit_real_T(&emx, 2);
  emx->size[0] = rows;
  numEl = rows * cols;
  emx->size[1] = cols;
  emx->data = (double *)calloc((unsigned int)numEl, sizeof(double));
  emx->numDimensions = 2;
  emx->allocatedSize = numEl;
  return emx;
}

void emxInit_real_T(emxArray_real_T **pEmxArray, int numDimensions)
{
  emxArray_real_T *emxArray;
  int i;
  *pEmxArray = (emxArray_real_T *)malloc(sizeof(emxArray_real_T));
  emxArray = *pEmxArray;
  emxArray->data = (double *)NULL;
  emxArray->numDimensions = numDimensions;
  emxArray->size = (int *)malloc(sizeof(int) * numDimensions);
  emxArray->allocatedSize = 0;
  emxArray->canFreeData = true;
  for (i = 0; i < numDimensions; i++) {
    emxArray->size[i] = 0;
  }
}

c++和Java的比较输出与参数:emxcreate_real_t(1024,12)此处的调用的比较输出:emxcreate_real_t(1024,12)

C++(正确):

=============
[0]:
data:  0.0
size:  1024
allocatedSize:  12288
numDimensions:  2
canFreeData:  True
=============
[1]:
data:  0.0
size:  12
allocatedSize:  12288
numDimensions:  2
canFreeData:  True
=============
[2]:
data:  0.0
size:  0
allocatedSize:  12288
numDimensions:  2
canFreeData:  True
=============

Java(不正确):

=============
[0]:
data: 6.9475440396446E-310
size: -1672347344
allocatedSize: 32740
numDimensions: 12288
canFreeData: true
=============
[1]:
data: 6.94754379426234E-310
size: 32740
allocatedSize: 32740
numDimensions: 12288
canFreeData: true
=============
[2]:
data: 4.243997653E-314
size: 12288
allocatedSize: 32740
numDimensions: 12288
canFreeData: true
=============

我认为问题在于分配规模的差异如下:

emx->data = (double *)calloc((unsigned int)numEl, sizeof(double));

但不知道如何解决它--使用字节和UnsignedInt的转换对我没有帮助。:(

UPD:20.02.21:@Matthias Bläsing再次感谢你的帮助。如何将Getters和Setters写入这个新结构,其中包含指向工作结构“emxarray_real_t”的指针(“this Pointer”*emxdata“”)C++结构代码:

typedef struct {
  emxArray_real_T *emxData;
  double timeStamp;
} struct0T;

Java结构代码:

@Structure.FieldOrder({"emxData","timeStamp"})
public class Struct0T extends Structure {

    public Pointer emxData;
    public double timeStamp;

    public emxArray_real_T getEmxData() {
        if (emxData == null) {
            throw new IllegalArgumentException("emxData is null");
        }
        // ???
        return ???;
    }

    public void setData(emxArray_real_T emxArray) {
        if (emxData == null ) {
            throw new IllegalArgumentException("emxData is null");
        }
        final Pointer pointer = emxArray.getPointer(); // Or re-calculate Pointer by multiplying arrays size on Double/Integer sizes etc here for all fields?
        if(pointer == null) {
            throw new IllegalArgumentException("pointer is null");
        }
        this.emxData = pointer;
    }
}

Jna库中的代码:

public interface TestJna extends Library {
***
    void calculation(emxArray_real_T input_str, emxArray_real_T output_str);
}

UPD 11.03.21:double[]<->byte[]转换(试图解决Linux问题):

getData()setData中的更改:

public double[] getData() {
    final int times = Double.SIZE / Byte.SIZE;
    if (data == null) {
        return new double[0];
    }
    return ByteDoubleConverterUtils.toDoubleArray(data.getByteArray(0, allocatedSize * times));
}

public void setData(double[] data) {
    final byte[] bytes = ByteDoubleConverterUtils.toByteArray(data);
    this.data.write(0, bytes, 0, bytes.length);
}

实用工具类:

public class ByteDoubleConverterUtils {

    public static byte[] toByteArray(double[] doubleArray){
        int times = Double.SIZE / Byte.SIZE;
        byte[] bytes = new byte[doubleArray.length * times];
        for(int i=0;i<doubleArray.length;i++){
            final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes, i * times, times);
            byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
            byteBuffer.putDouble(doubleArray[i]);
        }
        return bytes;
    }

    public static double[] toDoubleArray(byte[] byteArray){
        int times = Double.SIZE / Byte.SIZE;
        double[] doubles = new double[byteArray.length / times];
        for(int i=0;i<doubles.length;i++){
            final ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray, i * times, times);
            byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
            doubles[i] = byteBuffer.getDouble();
        }
        return doubles;
    }
}

共有1个答案

章茂
2023-03-14

主要问题是,@Structure.FieldOrder是错误的。您需要匹配本机字段顺序,以便运行库能够将正确的Java变量与正确的内存位置关联起来。在声明中,第一个成员是allocatedSize(Java站点),但在本机代码中,它是指向数据的指针。

@Structure.FieldOrder({"data", "size", "allocatedSize", "numDimensions", "canFreeData"})
public static class emxArray_real_T extends Structure {
    public static class ByReference extends emxArray_real_T implements Structure.ByReference {
        public ByReference() {
        }

        public ByReference(Pointer p) {
            super(p);
        }
    }

    public Pointer data;
    public Pointer size;
    public int allocatedSize = 1;
    public int numDimensions = 1;
    public boolean canFreeData = false;

    public emxArray_real_T() {
    }

    public emxArray_real_T(Pointer p) {
        super(p);
    }

    public double[] getData() {
    if (data == null) {
        return new double[0];
    }
    return data.getDoubleArray(0, allocatedSize);
    }

    public void setData(double[] data) {
    if (data.length != allocatedSize) {
        throw new IllegalArgumentException("Data must have a length of " + allocatedSize + " but was " + data.length);
    }
    this.data.write(0, data, 0, data.length);
    }

    public int[] getSize() {
    if (size == null) {
        return new int[0];
    }
    return size.getIntArray(0, numDimensions);
    }

    public void setSize(int[] size) {
    if (size.length != numDimensions) {
        throw new IllegalArgumentException("Size must have a length of " + numDimensions + " but was " + size.length);
    }
    this.size.write(0, size, 0, size.length);
    }
}

当您使用它时,请检查boolean_T是否为32bit。如果是1byte,则映射到byte。

对于包含对emxarray_real_t引用的结构,您需要一个实现标记接口com.sun.jna.Structure.byreference的结构。具有该标记接口的结构通过引用传递,并且当嵌入到其他结构中时,指向这些结构的指针嵌入到父结构中,而不是子结构本身。

@Structure.FieldOrder({"emxData","timeStamp"})
public class Struct0T extends Structure {

    public emxArray_real_T.ByReference emxData;
    public double timeStamp;
}

要从Structure.ByReference转换为Structure.ByReference,可以运行(如果来自本机):

    emxArray_real_T.ByReference t1ByReference = Structure.newInstance(emxArray_real_T.ByReference.class, input.getPointer());
    t1ByReference.read();

如果结构是在Java端创建的,并且数据是由内存对象支持的,那么建议使用复制构造函数,以便保持引用不变。

 类似资料:
  • 我使用的是名为Gson的Google JSON API,在设置Java类以便使用方法时遇到了困难。 从JSON数据中,我知道有一个包含所有数据的对象数组。让我感到困惑的是外部标记。我知道我必须构造一个表示对象的类,但是我是否也必须创建一个表示的类,因为它包含? 目前,我有一个名为的文件,其中包含以下内容: 我只包括了我计划使用的变量。上面的JSON数据包含在一个名为的字符串中,这就是我在main方

  • 这是我第一次使用JNA。我想做的是在一个DLL中调用一个函数 这是我的JNA结构“新设备” 我正在尝试使用“updateStrucureByReference(类类型、对象、对象指针)”更新字段。我不相信我的“类类型”参数是正确的还是正确的?我是不是做错了什么事?将非常感谢任何输入。 当我尝试打印字段时,它们似乎都是零。 在我的主要班级里

  • 问题内容: 这个问题已经在这里有了答案 : 7年前关闭。 可能重复: 如何从文件内容创建Java字符串 我有一个HTML文件,我想用它来提取信息。为此,我正在使用Jsoup。现在使用Jsoup,我需要将html文件转换为字符串。我怎样才能做到这一点? 现在,我想要一个String对象,其中包含html文件中的内容。 问题答案: 我使用apache common IO将文本文件读取为单个字符串 简单

  • 问题内容: 我对用Java转换对象的含义感到困惑。 说你有… 这是怎么回事 变量类型会更改吗,还是变量内的对象会更改?很迷茫。 问题答案: 看一下这个样本:

  • 如何将hashmap转换或强制转换为Java中的JSON对象,并再次将JSON对象转换为JSON字符串?

  • 最近,我开始使用OpenCSV的CSVReader将数据从CSV文件获取到java中的JTable,但我不断收到错误。DaTroop在这里给出了如何从CSV获取数据的答案:将CSV导入JTable 到Netbeans IDE 7.4,但我一直得到错误“不兼容的类型-对象无法转换为TableModel”。在……里 有什么想法吗? 谢谢 正如在评论中一样,请找到我的整个代码片段。JFrame可以工作和