如何使用JNA在Java对象中转换C ++结构(由Mathlab编译) [英] How to convers C++ Structure(compiled from Mathlab) in Java object using JNA

查看:81
本文介绍了如何使用JNA在Java对象中转换C ++结构(由Mathlab编译)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have this C++ Structure and Method, which it return inside of .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;
}

然后我尝试通过JNA从Java调用它.我的目标是收到此结构"emxArray_real_T"在Java中获取对其字段的访问.我从一个非常简单的代码开始,到此结束,但是仍然有问题-我在"size","data"等

And I try to call it from Java via JNA. My goal is to receive this Struct "emxArray_real_T" in Java to obtain access to its fields. I started from a very simple code and ended with this, but still have problems - I received the wrong data inside in "size", "data" etc.

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本机函数调用和具有双**指针/数组内存分配的结构

How can I fix it? I followed this guide, but it doesn't helped me completely solve the problem: JNA native function call and a Structure with a Double**-Pointer/Array Memory-Allocation

UPD 16.02/21:@MatthiasBläsing,谢谢您提供的出色解决方案-它帮助我识别了自己的错误.与C ++输出相比,我有一些错误的数据输出.也许我应该提供完整的C ++代码:

UPD 16.02/21: @Matthias Bläsing, thank you for this great solution - it helped me recognize my mistakes. I have some wrong data output comparing to the C++ output. Maybe I should provide the full C++ code:

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)此处:

Comparision outputs from C++ and Java for the call with arguments: emxCreate_real_T(1024, 12) here:

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
=============

我认为问题出在这样的分配大小差异上:

I think the problem is in the allocation size differences like this:

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

但是不知道如何解决-使用Byte并转换为UnsignedInt对我没有帮助.:(

But have no idea how to solve it - using Byte and conversions to UnsignedInt doesn't help for me. :(

UPD:20.02.21:@MatthiasBläsing再次感谢您的帮助.如何将Getter和Setters写入此新结构,该结构包含指向工作结构"emxArray_real_T"的Pointer(此指针" * emxData"").C ++结构代码:

UPD: 20.02.21: @Matthias Bläsing Thanks again for your help. How to write Getters and Setters to this new Structure, which contains Pointer("this pointer "*emxData"") to working Structure "emxArray_real_T" C++ Structure code:

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问题):

UPD 11.03.21: double[] <-> byte[] conversions(trying to solve Linux issue):

getData()setData中的更改:

Changes in 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;
    }
}

推荐答案

主要问题是 @ Structure.FieldOrder 错误.你需要匹配本地字段顺序,以便运行时可以用正确的存储位置正确的Java变量相关联.声明后,第一个成员是 allocatedSize (java站点),但是在本机代码中,它是指向 data 的指针.

The primary issue is, that @Structure.FieldOrder is wrong. You need to match the native field order, so that the runtime can associate the right java variable with the right memory location. With your declaration the first member is allocatedSize (java site), but in the native code, it is the pointer to data.

@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.

When you use this, please check if boolean_T is 32bit. If it is 1byte, map to byte.

对于拥有对 emxArray_real_T 的引用的结构,您需要一个结构,该结构实现标记接口 com.sun.jna.Structure.ByReference .具有该标记接口的结构通过引用传递,并且当嵌入到其他结构中时,指向该结构的指针将嵌入到父结构中,而不是子元素本身.

For the Structure that holds a reference to the emxArray_real_T you need a Structure, that implements the marker interface com.sun.jna.Structure.ByReference. Structures with that marker interface are passed by reference and when embedded into other structures, a pointer to the structures is embedded into the parent structure and not the child itself.

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

    public emxArray_real_T.ByReference emxData;
    public double timeStamp;
}

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

To convert from Structure to Structure.ByReference you can run (if this comes from native):

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

如果结构是在Java端创建的,并且数据由Memory对象支持,则建议使用复制构造函数,以使引用保持完整.

If the structure was created on the java side and the data is backed by a Memory object, it is advised to use a copy constructor, so that the reference is kept intact.

这篇关于如何使用JNA在Java对象中转换C ++结构(由Mathlab编译)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆