Search code examples
javacjna

How to pass a struct including a array of primitive type in JNA?


I'm a novice JNA developer so please forgive me if this question is trivial, but I'm struggling against dealing with C-lang struct object including an array of float with JNA. Currently I got a compile error.

I think the problem is the wrong way to initialize and assign FloatByReference instance (mys.Weights) to mystruct class instance (mys), and believe that Java code below should not be modified because JNAerator automatically generated it.

My C Code:

typedef struct
{
  int n;
  float *Weights;
} mystruct;

void testfunc(mystruct* mys){
  printf(mys->Weights[0]);
}

public interface CLibrary extends Library {
    public static final String JNA_LIBRARY_NAME = "test";
    public static final NativeLibrary JNA_NATIVE_LIB = NativeLibrary.getInstance(CLibrary.JNA_LIBRARY_NAME);
    public static final CLibrary INSTANCE = (CLibrary)Native.loadLibrary(CLibrary.JNA_LIBRARY_NAME, CLibrary.class);

and Java:

public interface CLibrary extends Library {
    public static final String JNA_LIBRARY_NAME = "test";
    public static final NativeLibrary JNA_NATIVE_LIB = NativeLibrary.getInstance(CLibrary.JNA_LIBRARY_NAME);
    public static final CLibrary INSTANCE = (CLibrary)Native.loadLibrary(CLibrary.JNA_LIBRARY_NAME, CLibrary.class);


    public static class mystruct extends Structure {
        public int n;
        public FloatByReference Weights;
        protected List<? > getFieldOrder() {
            return Arrays.asList("n", "Weights");
        }
        public mystruct(int n, JnaeratorTestLibrary.feature_t.ByReference Features, FloatByReference Weights) {
            super();
            this.n = n;
            this.Features = Features;
            this.Weights = Weights;
        }
        public mystruct(Pointer peer) {
            super(peer);
        }
        public static class ByReference extends mystruct implements Structure.ByReference {

        };
        public static class ByValue extends mystruct implements Structure.ByValue {

        };
    };
    void testfunc(CLibrary.mystruct mys);
}

public static void main(String[] args) {
    mystruct mys = new mystruct();
    mys.n = 10;
    mys.Weights = new FloatByReference();
    // Compile error raises.
    mys.Weights = new float[4]; mys.Weights[0] = 1.0;

    testfunc(mys);
}

Compile Error message:

Type mismatch: cannot convert from float[] to FloatByReference


Solution

  • Use Pointer, then use Pointer.getFloatArray(0, n) to extract the data you want.

    You'll need to allocate Memory and use Memory.write() to write a Java primitive float array to native memory before assigning that memory to your structure field.

    FloatByReference is basically a pointer to one allocated float. JNAerator simply can't tell the difference between a pointer to a single float and a pointer to a buffer of floats (nor can a human without some additional context).