无法在java中实例化结构内部结构

时间:2015-09-30 21:55:24

标签: java struct jna

我的目标是创建一个数据结构,它应该在内存中具有以下表示:

    int    countOfElements; // 4 bytes
    int    time_1;          // 4 bytes
    double price_1;         // 8 bytes
    int    time_2;          // 4 bytes
    double price_2;         // 8 bytes
    int    time_3;          // 4 bytes
    double price_3;         // 8 bytes
     . . . . .
    int    time_n;          // 4 bytes, n = countOfElements
    double price_n;         // 8 bytes, n = countOfElements

不幸的是,我无法在内存中选择此结构的表示(字段顺序和类型),因为它与操作系统上的另一个进程相关。数据结构旨在通过使用WinAPI函数放置在共享内存上,此数据结构的目的是数据交换。因此,我还需要在java中获取指向此数据结构的指针...    因此,我努力实现这种数据结构如下:

import java.util.List;
import java.util.Arrays;
import com.sun.jna.Structure;

public class TQuote extends Structure {
    public int    time;
    public double price;

    @Override
    protected List getFieldOrder() {
        return Arrays.asList(new String[]{"time", "price"});
    }
}

public class TSharedArray extends Structure {
    public int      countOfElements;
    public TQuote[] elements;

    public TSharedArray(int size) {
        super();
        countOfElements = size;
        elements = new TQuote[size];
        for (int i = 0; i < size; i++) elements[i] = new TQuote();
    }

    @Override
    protected List getFieldOrder() {
        return Arrays.asList(new String[]{"countOfElements", "elements"});
    }
}

尽管如此,我尝试实例化结构TSharedArray structure = new TSharedArray(10);会引发异常:

/*
 Invalid Structure field in class xxx$TSharedArray, field name 'elements' (class xxx$TQuote): Can't instantiate class xxx$TQuote (java.lang.InstantiationException: xxx$TQuote)
    java.lang.IllegalArgumentException: Invalid Structure field in class xxx$TSharedArray, field name 'elements' (class xxx$TQuote): Can't instantiate class xxx$TQuote (java.lang.InstantiationException: xxx$TQuote)
        at com.sun.jna.Structure.validateField(Structure.java:962)
        at com.sun.jna.Structure.validateField(Structure.java:954)
        at com.sun.jna.Structure.validateFields(Structure.java:972)
        at com.sun.jna.Structure.<init>(Structure.java:186)
        at com.sun.jna.Structure.<init>(Structure.java:180)
        at com.sun.jna.Structure.<init>(Structure.java:167)
        at com.sun.jna.Structure.<init>(Structure.java:159)
        at xxx$TSharedArray.<init>(xxx.java:35)
        at xxx.onStart(xxx.java:57)
        at java.lang.Thread.run(Unknown Source)
*/

xxx.java第35行中的Java代码是super();任何想法,如何修复它?

此外,我注意到一件奇怪的事情......让我们假设,我们有三个结构,每个结构都包含两个字段:

public class TIntInt extends Structure {
    public int int1;
    public int int2;

    @Override
    protected List getFieldOrder() {
      return Arrays.asList(new String[]{"int1", "int2"});
    }
}

public class TDblDbl extends Structure {
    public double dbl1;
    public double dbl2;

    @Override
    protected List getFieldOrder() {
      return Arrays.asList(new String[]{"dbl1", "dbl2"});
    }
}

public class TIntDbl extends Structure {
    public int    int1;
    public double dbl1;

    @Override
    protected List getFieldOrder() {
      return Arrays.asList(new String[]{"int1", "dbl1"});
    }
}

第一个(TIntInt)的大小与预期的2 * 4 = 8个字节相同,第二个(TDblDbl)的大小与预期的2 * 8 = 16个字节相同,但第三个(TIntDbl - it&#39; s与TQuote相同)的大小为16字节而不是预期的4 + 8 = 12。为什么:

System.out.println("TIntInt.size()=" + new TIntInt().size()); //  8 bytes, Ok
System.out.println("TDblDbl.size()=" + new TDblDbl().size()); // 16 bytes, Ok
System.out.println("TIntDbl.size()=" + new TIntDbl().size()); // 16 bytes!!! Why?

3 个答案:

答案 0 :(得分:0)

使用的内存量以8字节块增加。有关详细信息,请参阅https://stackoverflow.com/a/321436/3915166

答案 1 :(得分:0)

必须初始化Structure中的任何数组字段。请注意,不支持数组大小为零。

public class TSharedArray extends Structure {
    public int      countOfElements;
    public TQuote[] elements = new TQuote[1];

    public TSharedArray() { 
        this(1); 
    }
    public TSharedArray(Pointer p) { 
        super(p);
        countOfElements = (int)readField("countOfElements");        
        elements = new TQuote[countOfElements];
        read();
    }
    public TSharedArray(int size) {
        countOfElements = size;
        elements = (TQuote[])new TQuote().toArray(size);
    }

    @Override
    protected List getFieldOrder() {
        return Arrays.asList(new String[]{"countOfElements", "elements"});
    }
}

至于你的第二个问题,大小的差异是由于结构填充。通常字段将在与其大小相等的内存边界上对齐,因此在int字段后将有4个字节填充,以便在8字节边界上对齐double字段。

修改

用作字段的任何结构必须具有public,no-args构造函数(并且拥有基于指针的构造函数也是一个好主意)。您看到的错误是因为JNA无法通过反射创建TQuote的实例。您需要解决该问题或明确为该字段创建预填充数组:

public TQuote[] elements = (TQuote[])new TQuote().toArray(1);

<强>对齐

如果您想避免默认的平台对齐,可以使用Structure.ALIGN_NONE(请参阅Structure.setAlignType())。

答案 2 :(得分:0)

经过多次尝试和实验后,我发现在没有Exception的情况下运行的唯一代码是使用Structure.ByReference

public class TQuote extends Structure {
    public class ByReference extends TQuote implements Structure.ByReference {}
    public int    time;
    public double price;

    @Override
    protected List getFieldOrder() {
      return Arrays.asList("time", "price");
    }
}

public class TSharedArray extends Structure {
  public int                  countOfElements;
  public TQuote.ByReference[] elements;

  public TSharedArray(int size) {
    super();
    countOfElements = size;
    elements = new TQuote.ByReference[size];
  }

  @Override
  protected List getFieldOrder() {
    return Arrays.asList("countOfElements", "elements");
  }
}

尽管如此,上面的代码并没有达到我的目标,因为它形成了一个引用数组,而不是一个值数组。因此,结构的内存表示不合适。

我通过尝试解决结构问题而放弃......现在我有另一个不使用结构的解决方案:我在Pointers的帮助下创建了所需的内存分配。