Reputation: 159
I have the following exception defining a JNA structure inside a Structure :
Exception in thread "main" java.lang.Error: Structure.getFieldOrder() on class com.MyInterface$mine$ByReference returns names ([color, data, hello, rice, str, wild]) which do not match declared field names
See my Cpp Structures:
typedef struct s_mine
{
e_color color; //e_color is enum type made of int values
his data;
int str;
unsigned int wild;
unsigned int hello;
float rice;
} mine;
typedef struct s_his
{
unsigned char * data;
unsigned int size;
} his;
See my sample below i.e MyInterface.java:
public interface MyInterface extends Library {
public static class his extends Structure {
public static class ByReference extends his implements Structure.ByReference {} // Need the stucture address as it a parameter of a particular wrapped method
public static class ByValue extends his implements Structure.ByValue {} // Need the structure value inside "mine" Structure
public Pointer data;
public int size;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "data", "size"});
}
}
public class mine extends Structure {
public static class ByReference extends mine implements Structure.ByReference {}
int color;
his.ByValue data;
int str;
int wild;
int hello;
float rice;
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] {"color","data","str","wild","hello","rice"});
}
}
}
The main class using MyInterface contains the following line on which I have the exception regarding the order of "mine" Structure's fields:
final MyInterface.mine.ByReference mine_ref = new MyInterface.mine.ByReference();
Thus I debug the code in order to step into JNA following Structure.class into the following getFields method (in which I get the exception (second one below):
protected List<Field> getFields(boolean force) {
List<Field> flist = getFieldList();
Set<String> names = new HashSet<String>();
for (Field f : flist) {
names.add(f.getName());
}
List<String> fieldOrder = fieldOrder();
if (fieldOrder.size() != flist.size() && flist.size() > 1) {
if (force) {
throw new Error("Structure.getFieldOrder() on " + getClass()
+ (fieldOrder.size() < flist.size()
? " does not provide enough"
: " provides too many")
+ " names [" + fieldOrder.size()
+ "] ("
+ sort(fieldOrder)
+ ") to match declared fields [" + flist.size()
+ "] ("
+ sort(names)
+ ")");
}
return null;
}
Set<String> orderedNames = new HashSet<String>(fieldOrder);
if (!orderedNames.equals(names)) {
throw new Error("Structure.getFieldOrder() on " + getClass()
+ " returns names ("
+ sort(fieldOrder)
+ ") which do not match declared field names ("
+ sort(names) + ")");
}
sortFields(flist, fieldOrder);
return flist;
}
This is what I get exploring the following fields value as the exception is raised in getFields method
fieldOrder = [color, data, str, wild, hello, rice]
orderedNames = [str, color, data, hello, rice, wild]
The exception is raised because names is empty. Anyway, as HashSet java.util class does not guarantee that the order will remain constant over time, I definitely think that there is bug in JNA Structure.java.
Because my fieldOrder's order is sorted in a random order by new HashSet<String>(fieldOrder)
instruction and thus different from orderedNames's order.
Does anyone share my point of view or did I miss something regarding the way to declare and use my JNA structures?
If there is no bug (i.e HashSet is used on purpose...) did I make something wrong declaring my structures? How can I fix the issue?
Upvotes: 1
Views: 3198
Reputation: 9130
Your error is in the mine
structure: You need to declare the fields as public
. JNA uses reflection and relies on the public
modifier for fields that are intended to be mapped to native; otherwise they're just considered helper fields in the class.
Otherwise, the mappings would work as you have them, but consider the following improvements:
ByValue
version of the structure when used as a nested structure. That's the default. The only time you need to take special action with a nested structure is if it's ByReference
.
ByReference
is the default when used as a function argument. Only if the function requires a ByValue
argument does it require special treatment.getFieldOrder()
override was replaced in JNA 5.x with the @FieldOrder
annotation. While what you have works, your code is more readable if you just put that annotation before your structure, e.g.,@FieldOrder({"data", "size"})
class his extends Structure {
// etc...
}
public
, static
, and final
so use of those modifiers is redundant.Upvotes: 3