简体   繁体   中英

JNA how to pass String[] from Java to C code

I have an issue. I first need to understand how to pass a 'String[] contentsStatic'. I can see in the JNA documentation that the String[] should map to Char**. I am not sure how to map this on the Java side. In my particular implementation I am not even sure if this would actually work, because the C code I am trying to use is MatLab, so in the C code it is expecting a 'const emxArray_char_T *contentsStatic. I do not know what this is.

This is the C side:

Initialise(const emxArray_char_T *contentsStatic, const
                    emxArray_char_T *contentsDynamic, int b_phoneAngleDeg, double
                            b_stepCalibrationOffset, int b_initialFloorNumber, int
                            b_initialPointingAngleDeg, int b_initialAlignmentMode, bool *mapStatus, bool
  *paramStatus);

The Java Side is:

initialise(String[] contentsStatic,
                                String[] contentsDynamic,
                                int phoneRelativeToBodyDegree,
                                double initialStepCalibrationOffset, 
                                int startingFloorID,
                                LatLng startingLatLong,
                                double startingAccuracy,
                                boolean _CDontActuallyUse,
                                int phoneOrientation,
                                int phoneOrientationUse,
                                boolean magntometerValid
                                )

Mapping method for reference:

private struct MatWrap
{
    var MatString: MatString
    private var string_size_array_to_pass: [Int32]
    private var string_bytes_int8: [Int8]

    init(string: String)
    {
        let string_size = MatWrap.getSize(string: string)

        self.string_bytes_int8 = MatWrap.getInt8Array(string: string)
        self.string_size_array_to_pass = MatWrap.getSizeArray(size: string_size)

        self.MatString = MatWrap.makeMatString(
            data: &self.string_bytes_int8,
            size: &self.string_size_array_to_pass,
            allocatedSize: string_size
        )
    }

    private static func getInt8Array(string: String) -> [Int8] {
        let data = string.data(using: .ascii, allowLossyConversion: false)!
        let bytes = [UInt8](data)
        let bytes_int8 = bytes.map{Int8($0)}
        return bytes_int8
    }

    private static func getSize(string: String) -> Int32 {
        return Int32(string.lengthOfBytes(using: .ascii))
    }

    private static func getSizeArray(size: Int32) -> [Int32] {
        return [1, size]
    }

    private static func makeMatString(data: UnsafeMutablePointer<Int8>, size: UnsafeMutablePointer<Int32>, allocatedSize: Int32) -> MatString {
        return MatString(data: data, size: size, allocatedSize: allocatedSize, numDimensions: 1, canFreeData: false)
    }
}

The Method used to prepare the joined String[] to pass:

allFloorsDynamic_wrappedMatString =  MatWrap(string: contentsDynamic.joined())

You're asking how to pass a String[] but that's not what the native function is expecting.

Looking at the Matlab docs , emxArray_char_T is a structure:

struct emxArray_char_T
{
   char * str;
   int size;
};

Further, reading the documents, you can see this description of the API:

The code generator produces C/C++ array definitions that depend on the array element type and whether the array uses static or dynamic memory allocation. The two kinds of memory allocation for an array require two different implementations:

  • For an array whose size is bounded within a predefined threshold, the generated C/C++ definition consists of a pointer to memory and an integer that stores the total number of array elements, the array size. The memory for this array comes from the program stack and is statically allocated.

  • For an array whose size is unknown and unbounded at compile time, or whose bound exceeds a predefined threshold, the generated C/C++ definition consists of a data structure called an emxArray. When an emxArray is created, intermediate storage bounds are set based on the current array size. During program execution, as intermediate storage bounds are exceeded, the generated code appropriates additional memory space from the heap and adds it to the emxArray storage. The memory for this array is dynamically allocated.

For the case where you are passing const values to the native side, you can use the statically allocated version. However, here you run into a limitation of JNA: the size of arrays must be known when you define the structure. So to use a structure you'd have to do it like this:

@FieldOrder ({"str", "size"})
class emxArray_char_T_3 extends Structure {
    public String[] strArray = new String[3];
    public int size = 3;
}

You would define this and then set the 3 strArray elements. You could do a similar array for different sizes.

However, it looks like MATLAB has helper functions for creating this array, such as emxCreateWrapper_char_T . You may want to figure out how to use those.

Further digging into the docs, they say :

If you are not using dynamic memory allocation, arrays in structures translate into single-dimension arrays, not pointers.

This is a challenge for strings, which are variable length arrays, and a typical String[] mapping for other language would be an array of pointers to strings elsewhere. This implies that to directly use the existing structures with the const type you would have to have constant-length character arrays defined for your strings.

Looking at the syntax for the string arrays that you've added with the edit, it still looks like you have to pass a single string, but the notation joined implies that it expects some sort of delimited notation. Again, this is something you'll have to search the API to find exactly what the delimiter is!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM