javac++jna

Access a C++ struct from Java using JNA


I'm new to JNA and trying to put together a very minimal proof-of-concept for accessing structs from a C++ shared library. Unfortunately, it doesn't run and I cannot figure it out.

Here's my code:

Main.java:

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

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

        @FieldOrder({"x", "y"})
        class MyStruct extends Structure {
            public int x;
            public int y;
        }

        MyStruct getStruct();
    }

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

        System.out.println("x: " + myStruct.x);
        System.out.println("y: " + myStruct.y);
    }
}

MyStruct.cpp:

#include <iostream>

struct MyStruct {
    int x;
    double y;
};

extern "C" {
    MyStruct* getStruct() {
        return new MyStruct{10, 20.5};
    }
}

I compile the C++ code into a shared library (using WSL2 Ubuntu 20.04):

g++ -v -shared -o libmycpplib.so my_struct.cpp

But when I run the Java class (of course including a recent JNA jar, jna-5.15.0.jar, to my CLASSPATH) I get the following error:

Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'getStruct': /home/h3007/.vscode-server/data/User/workspaceStorage/af05c638282256cbf9a3f960fefbba02/redhat.java/jdt_ws/hassan-jna-struct_c38993c4/bin/libmycpplib.so: undefined symbol: getStruct
        at com.sun.jna.Function.<init>(Function.java:255)
        at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:618)
        at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:594)
        at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:580)
        at com.sun.jna.Library$Handler.invoke(Library.java:248)
        at jdk.proxy1/jdk.proxy1.$Proxy0.getStruct(Unknown Source)
        at Main.main(Main.java:21)

I'm assuming I've done something silly. Apologies, but pointers to my error would be appreciated.


Solution

  • I managed to make your proposal work with command line with two modifications.

    First, I had to compile MyStruct.cpp with the -fPIC option:

    g++  -shared -o libmycpplib.so MyStruct.cpp -fPIC
    

    Second, you have to be careful in the Main.java file since the type of y should be coherent with the type double in the cpp file:

    import com.sun.jna.Structure;
    import com.sun.jna.Native;
    import com.sun.jna.Library;
    import com.sun.jna.Structure.FieldOrder;
    
    public class Main 
    {
        public interface MyCppLibrary extends Library 
        {
            MyCppLibrary INSTANCE = (MyCppLibrary) Native.load("mycpplib", MyCppLibrary.class);
    
            @FieldOrder({"x", "y"})
            class MyStruct extends Structure 
            {
                public int x;
                public double y;
            }
    
            MyStruct getStruct();
        }
    
        public static void main(String[] args) 
        {
            MyCppLibrary lib = MyCppLibrary.INSTANCE;
            MyCppLibrary.MyStruct myStruct = lib.getStruct();
    
            System.out.println("x: " + myStruct.x);
            System.out.println("y: " + myStruct.y);
        }
    }
    

    Then compile and launch with:

    javac -classpath /usr/share/java/jna.jar  Main.java
    java  -classpath /usr/lib/java/jna.jar:.  Main
    

    Possible output:

    x: 10
    y: 20.5