admin管理员组

文章数量:1335854

This follows on from my earlier question. I now need to access a C++ struct that has multiple members of type std::vector<double> or std::vector<int>. However while I seem to successfully access the first member, the second produces garbage. Code below:

my_struct.cpp:

#include <iostream>
#include <memory>

#include <vector>

struct MyStruct {
    int intVectorSize;
    int doubleVectorSize;
    std::vector<int> intVector;
    std::vector<double> doubleVector;
};


extern "C" {

    MyStruct* create_my_struct() {
        MyStruct* myStruct = new MyStruct();

        myStruct->intVectorSize = 2;
        myStruct->doubleVectorSize = 2;
        myStruct->intVector.push_back(5);
        myStruct->intVector.push_back(6);
        myStruct->doubleVector.push_back(1.1);
        myStruct->doubleVector.push_back(2.3);

        return myStruct;
    }

    void delete_my_struct(MyStruct* myStruct) {
        delete myStruct;
    }
}

BasicStruct.java:

import com.sun.jna.Structure;
import com.sun.jna.Native;
import com.sun.jna.Pointer;

import java.util.Arrays;

import com.sun.jna.Library;
import com.sun.jna.Structure.FieldOrder;

public class BasicStruct {
    public interface MyCppLibrary extends Library {
        MyCppLibrary INSTANCE = (MyCppLibrary) Native.load("mycpp", MyCppLibrary.class);

        @FieldOrder({"intVectorSize", "doubleVectorSize", "intVector", "doubleVector"})
        public class MyStruct extends Structure {
            public int intVectorSize;
            public int doubleVectorSize;

            public Pointer intVector;
            public Pointer doubleVector;
        }

        MyStruct create_my_struct();
        void delete_my_struct(MyStruct struct);

    }


    public static void main(String[] args) {
        MyCppLibrary lib = MyCppLibrary.INSTANCE;

        MyCppLibrary.MyStruct myStruct = lib.create_my_struct();

        System.out.println("intVectorSize: " + myStruct.intVectorSize);
        System.out.println("doubleVectorSize: " + myStruct.doubleVectorSize);
        System.out.println("intVector: " + myStruct.intVector);
        System.out.println("doubleVector: " + myStruct.doubleVector);


        int[] intArray = myStruct.intVector.getIntArray(0, myStruct.intVectorSize);
        double[] doubleArray = myStruct.doubleVector.getDoubleArray(0, myStruct.doubleVectorSize);

        System.out.println("intVector contents: " + Arrays.toString(intArray));
        System.out.println("doubleVector contents: " + Arrays.toString(doubleArray));

        lib.delete_my_struct(myStruct);

    }
}

Compiling the cpp code using g++ in WSL2 running Ubuntu 20.04 into a shared lib whose name matches that to be loaded from the Java code:

g++ -v -shared -fPIC -o libmycpp.so my_struct.cpp

And compiling and executing the Java code (ensuring that the JNA jar file is on the classpath):

 $ javac -classpath ./jars/jna-5.15.0.jar  BasicStruct.java
 $ java  -classpath ./jars/jna-5.15.0.jar:. BasicStruct

I get the following results:

intVectorSize: 2
doubleVectorSize: 2
intVector: native@0x7f0ea8294600
doubleVector: native@0x7f0ea8294608
intVector contents: [5, 6]
doubleVector contents: [6.9469400861921E-310, 0.0]

Note that while the output for the intVector is correct, the output for the doubleVector attribute shown is wrong.

I tried other combinations of ordering the attributes and notice after the first vector is read, the subsequent attributes appear corrupted.

Any suggestions on how to fix this? Thanks in advance.

This follows on from my earlier question. I now need to access a C++ struct that has multiple members of type std::vector<double> or std::vector<int>. However while I seem to successfully access the first member, the second produces garbage. Code below:

my_struct.cpp:

#include <iostream>
#include <memory>

#include <vector>

struct MyStruct {
    int intVectorSize;
    int doubleVectorSize;
    std::vector<int> intVector;
    std::vector<double> doubleVector;
};


extern "C" {

    MyStruct* create_my_struct() {
        MyStruct* myStruct = new MyStruct();

        myStruct->intVectorSize = 2;
        myStruct->doubleVectorSize = 2;
        myStruct->intVector.push_back(5);
        myStruct->intVector.push_back(6);
        myStruct->doubleVector.push_back(1.1);
        myStruct->doubleVector.push_back(2.3);

        return myStruct;
    }

    void delete_my_struct(MyStruct* myStruct) {
        delete myStruct;
    }
}

BasicStruct.java:

import com.sun.jna.Structure;
import com.sun.jna.Native;
import com.sun.jna.Pointer;

import java.util.Arrays;

import com.sun.jna.Library;
import com.sun.jna.Structure.FieldOrder;

public class BasicStruct {
    public interface MyCppLibrary extends Library {
        MyCppLibrary INSTANCE = (MyCppLibrary) Native.load("mycpp", MyCppLibrary.class);

        @FieldOrder({"intVectorSize", "doubleVectorSize", "intVector", "doubleVector"})
        public class MyStruct extends Structure {
            public int intVectorSize;
            public int doubleVectorSize;

            public Pointer intVector;
            public Pointer doubleVector;
        }

        MyStruct create_my_struct();
        void delete_my_struct(MyStruct struct);

    }


    public static void main(String[] args) {
        MyCppLibrary lib = MyCppLibrary.INSTANCE;

        MyCppLibrary.MyStruct myStruct = lib.create_my_struct();

        System.out.println("intVectorSize: " + myStruct.intVectorSize);
        System.out.println("doubleVectorSize: " + myStruct.doubleVectorSize);
        System.out.println("intVector: " + myStruct.intVector);
        System.out.println("doubleVector: " + myStruct.doubleVector);


        int[] intArray = myStruct.intVector.getIntArray(0, myStruct.intVectorSize);
        double[] doubleArray = myStruct.doubleVector.getDoubleArray(0, myStruct.doubleVectorSize);

        System.out.println("intVector contents: " + Arrays.toString(intArray));
        System.out.println("doubleVector contents: " + Arrays.toString(doubleArray));

        lib.delete_my_struct(myStruct);

    }
}

Compiling the cpp code using g++ in WSL2 running Ubuntu 20.04 into a shared lib whose name matches that to be loaded from the Java code:

g++ -v -shared -fPIC -o libmycpp.so my_struct.cpp

And compiling and executing the Java code (ensuring that the JNA jar file is on the classpath):

 $ javac -classpath ./jars/jna-5.15.0.jar  BasicStruct.java
 $ java  -classpath ./jars/jna-5.15.0.jar:. BasicStruct

I get the following results:

intVectorSize: 2
doubleVectorSize: 2
intVector: native@0x7f0ea8294600
doubleVector: native@0x7f0ea8294608
intVector contents: [5, 6]
doubleVector contents: [6.9469400861921E-310, 0.0]

Note that while the output for the intVector is correct, the output for the doubleVector attribute shown is wrong.

I tried other combinations of ordering the attributes and notice after the first vector is read, the subsequent attributes appear corrupted.

Any suggestions on how to fix this? Thanks in advance.

Share Improve this question edited Nov 19, 2024 at 22:56 H3007 asked Nov 19, 2024 at 21:19 H3007H3007 627 bronze badges 5
  • 1 Any suggestions on how to fix this? -- Create sensibly named member functions in MyStruct that accesses the vectors instead of trying to directly access them. I don't know jna (only jni), but at the very least, I would think that vector::data() is what you should be returning to the Pointer type. – PaulMcKenzie Commented Nov 19, 2024 at 21:34
  • Your C++ standard library can use any vector implementation it sees fit. A typical implementation is that a vector instance consists of three pointers and a separately allocated array. The three pointers point to the start of the array, to the end of the actually used part of the array and to the end of the array. – Codo Commented Nov 19, 2024 at 22:11
  • 1 std::vector::data. – PaulMcKenzie Commented Nov 19, 2024 at 22:16
  • 1 I suppose a typo for the java compilation; one should have something like javac -classpath ./jars/jna-5.15.0.jar BasicStruct.java – edrezen Commented Nov 19, 2024 at 22:51
  • Yep that was a cut/paste error. Fixed. – H3007 Commented Nov 19, 2024 at 22:56
Add a comment  | 

1 Answer 1

Reset to default 2

It seems that JNA needs to access the raw array and not the std::vector itself in order to convert it into a java array (see this for getIntArray and getDoubleArray). It means that your C structure has to provide the result of std::vector::data.

So the C library would be for instance:

#include <vector>
#include <cstdint>

struct MyStruct 
{
    int intVectorSize;
    int doubleVectorSize;
    
    const int32_t*  intVectorPtr;
    const double*   doubleVectorPtr;

    std::vector<int32_t> intVector;
    std::vector<double>  doubleVector;
};

extern "C" 
{
    MyStruct* create_my_struct() 
    {
        MyStruct* myStruct = new MyStruct();

        myStruct->intVector.push_back(5);
        myStruct->intVector.push_back(6);
        myStruct->intVectorSize  = myStruct->intVector.size();
        myStruct->intVectorPtr   = myStruct->intVector.data();
        
        myStruct->doubleVector.push_back(1.1);
        myStruct->doubleVector.push_back(2.3);
        myStruct->doubleVectorSize = myStruct->doubleVector.size();
        myStruct->doubleVectorPtr  = myStruct->doubleVector.data();

        return myStruct;
    }

    void delete_my_struct(MyStruct* myStruct) 
    {
        delete myStruct;
    }
}

and the java part:

import com.sun.jna.Structure;
import com.sun.jna.Native;
import com.sun.jna.Pointer;

import java.util.Arrays;

import com.sun.jna.Library;
import com.sun.jna.Structure.FieldOrder;

public class BasicStruct 
{
    public interface MyCppLibrary extends Library 
    {
        MyCppLibrary INSTANCE = (MyCppLibrary) Native.load("mycpp", MyCppLibrary.class);

        @FieldOrder({"intVectorSize", "doubleVectorSize", "intVectorPtr", "doubleVectorPtr"})
        public class MyStruct extends Structure 
        {
            public int intVectorSize;
            public int doubleVectorSize;

            public Pointer intVectorPtr;
            public Pointer doubleVectorPtr;
        }

        MyStruct create_my_struct();
        void delete_my_struct(MyStruct struct);
    }

    public static void main(String[] args) 
    {
        MyCppLibrary lib = MyCppLibrary.INSTANCE;

        MyCppLibrary.MyStruct myStruct = lib.create_my_struct();

        int[]    intArray    = myStruct.intVectorPtr   .getIntArray   (0, myStruct.intVectorSize);
        double[] doubleArray = myStruct.doubleVectorPtr.getDoubleArray(0, myStruct.doubleVectorSize);

        System.out.println("intVector contents   : " + Arrays.toString(intArray));
        System.out.println("doubleVector contents: " + Arrays.toString(doubleArray));

        lib.delete_my_struct(myStruct);
    }
}

Possible output:

intVector contents   : [5, 6]
doubleVector contents: [1.1, 2.3]

本文标签: Access a C struct with two vectors from Java using JNAStack Overflow