Search code examples
javac++matlabdlljna

Why does my JNA Structure mapping produce strange field values?


I am trying to call functions from a dll generated by Mathlab. It appears to work ok in C, but it returns weird results when i try to use jna.

I am trying to call the function with the following signature :

emxArray_real32_T *emxCreate_real32_T(int rows, int cols);

with the structure:

struct emxArray_real32_T
{
    float *data;
    int *size;
    int allocatedSize;
    int numDimensions;
    boolean_T canFreeData;
};

The structure is mapped in java to:

public interface LibSoftEdge extends StdCallLibrary {
        public static class emxArray_real32_T extends Structure{


            public Pointer data ;
            public Pointer size;
            public int numDimensions;
            public int allocatedSize;
            public boolean canFreeData;

            @Override
            protected List getFieldOrder() {
                return Arrays.asList(new String[]{"allocatedSize","canFreeData",

"data","numDimensions","size"});
            }


            @Override
            public String toString() {
                return "emxArray_real32_T{" +
                        "data=" + data +
                        ", size=" + size +
                        ", allocatedSize=" + allocatedSize +
                        ", numDimensions=" + numDimensions +
                        ", canFreeData=" + canFreeData +
                        '}';
            }
        }

        emxArray_real32_T emxCreate_real32_T(int rows, int cols);
    }

and i call it with :

LibSoftEdge libM = (LibSoftEdge) Native.loadLibrary("libsoftedge", LibSoftEdge.class);
LibSoftEdge.emxArray_real32_T  test; 
test =     libM.emxCreate_real32_T(3,3);

In the test object I get weird values and null pointers.

Any suggestion is much apreciated.


Solution

  • When you return your field order like this:

    @Override
    protected List getFieldOrder() {
        return Arrays.asList("allocatedSize","canFreeData", "data","numDimensions","size");
    }
    

    You are telling JNA that your native struct looks like this:

    struct emxArray_real32_T
    {
        int allocatedSize; // actually 'data'
        boolean_T canFreeData; // actually 'size'
        float *data; // actually 'allocatedSize'
        int numDimensions; 
        int *size; // actually 'canFreeData'
    };
    

    So when JNA populates the fields on the Java side, your values will be incorrect because you're essentially scrambling your fields.

    In addition, depending on the size of boolean_T, you might be reading misaligned data as well, and might even end up crashing.

    Your getFieldOrder() should look like this:

    @Override
    protected List getFieldOrder() {
        return Arrays.asList("data", "size", "allocatedSize", "numDimensions", "canFreeData");
    }
    

    Finally, if you want to use Java boolean for boolean_T and boolean_T is any size other than four bytes, then you'll need to use a TypeMapper to ensure the types are converted properly.