javac++ffiproject-panamajava-21

How to get a C++ struct return value from Java using the Foreign Function & Memory API


When I try to get the struct return value in C++, I cannot resolve the value in the struct.

This is my structure.

struct Point {
    int x;
    int y;
};

This is my C++ method

Point test_point(Point points[],long count)
{
    if (count <= 0) {
        // Return a default Point with x and y set to 0
        Point defaultPoint = { 0, 0 };
        return defaultPoint;
    }

    Point maxPoint = points[0];
    int maxSum = maxPoint.x + maxPoint.y;

    for (int i = 1; i < count; ++i) {
        int currentSum = points[i].x + points[i].y;
        if (currentSum > maxSum) {
            maxSum = currentSum;
            maxPoint = points[i];
        }
    }

    int x = maxPoint.x;
    int y = maxPoint.y;
    std::cout << "x = " << x << ", y = " << y << std::endl;
    return maxPoint;
}

This is my Java method

public static void dereferenceSegmentsStruct() throws Throwable {
    Linker linker = Linker.nativeLinker();
    SymbolLookup symbolLookup = SymbolLookup.loaderLookup();
    MethodHandle test_point = linker.downcallHandle(
            symbolLookup.find("test_point").orElseThrow(),
            FunctionDescriptor.of(ADDRESS, ADDRESS, JAVA_LONG)
    );

    StructLayout structLayout = MemoryLayout.structLayout(
            JAVA_INT.withName("x"),
            JAVA_INT.withName("y"));

    SequenceLayout ptsLayout = MemoryLayout.sequenceLayout(10, structLayout);

    VarHandle xHandle    // (MemorySegment, long) -> int
            = ptsLayout.varHandle(PathElement.sequenceElement(),
            PathElement.groupElement("x"));
    VarHandle yHandle    // (MemorySegment, long) -> int
            = ptsLayout.varHandle(PathElement.sequenceElement(),
            PathElement.groupElement("y"));

    MemorySegment segment = Arena.ofAuto().allocate(ptsLayout);
    for (int i = 0; i < ptsLayout.elementCount(); i++) {
        xHandle.set(segment,
                /* index */ (long) i,
                /* value to write */ i); // x
        yHandle.set(segment,
                /* index */ (long) i,
                /* value to write */ i); // y
    }

    MemorySegment result = (MemorySegment) test_point.invoke(segment, ptsLayout.elementCount());
    result = result.reinterpret(structLayout.byteSize());
    System.out.println(result.getAtIndex(JAVA_INT, 0));
}

I see zero length, I try to use a method to modify the zero length.

image

I still can't get it, it gives the following error.

Exception in thread "main" java.lang.IllegalArgumentException: Misaligned access at address: 38654705673
    at java.base/java.lang.invoke.VarHandleSegmentViewBase.newIllegalArgumentExceptionForMisalignedAccess(VarHandleSegmentViewBase.java:57)
    at java.base/java.lang.invoke.VarHandleSegmentAsInts.offsetNoVMAlignCheck(VarHandleSegmentAsInts.java:100)
    at java.base/java.lang.invoke.VarHandleSegmentAsInts.get(VarHandleSegmentAsInts.java:111)
    at java.base/java.lang.foreign.MemorySegment.getAtIndex(MemorySegment.java:1921)
    at org.example/org.example.TestNative.dereferenceSegmentsStruct(TestNative.java:109)
    at org.example/org.example.TestNative.main(TestNative.java:33)

What do I need to do to read the x and y of the Point returned in C++?


Solution

  • Fortunately, with the help of Holger and Jorn Vernee, I modified the code and successfully ran it. The following modifications have been made

    extern "C" MATHLIBRARY_API Point test_point(Point points[], long count);
    
    public static void dereferenceSegmentsStruct() throws Throwable {
        StructLayout structLayout = MemoryLayout.structLayout(
                JAVA_INT.withName("x"),
                JAVA_INT.withName("y"));
    
        MethodHandle test_point = linker.downcallHandle(
                symbolLookup.find("test_point").orElseThrow(),
                FunctionDescriptor.of(structLayout, ADDRESS, JAVA_LONG)
        );
    
        SequenceLayout ptsLayout = MemoryLayout.sequenceLayout(10, structLayout);
    
        VarHandle xHandle    // (MemorySegment, long) -> int
                = ptsLayout.varHandle(PathElement.sequenceElement(),
                PathElement.groupElement("x"));
        VarHandle yHandle    // (MemorySegment, long) -> int
                = ptsLayout.varHandle(PathElement.sequenceElement(),
                PathElement.groupElement("y"));
    
        MemorySegment segment = Arena.ofAuto().allocate(ptsLayout);
        for (int i = 0; i < ptsLayout.elementCount(); i++) {
            xHandle.set(segment,
                    /* index */ (long) i,
                    /* value to write */ i); // x
            yHandle.set(segment,
                    /* index */ (long) i,
                    /* value to write */ i); // y
        }
        SegmentAllocator allocator = SegmentAllocator.slicingAllocator(Arena.ofAuto().allocate(100));
        MemorySegment result = (MemorySegment) test_point.invoke(allocator, segment, ptsLayout.elementCount());
        System.out.println(result);
        result = result.reinterpret(structLayout.byteSize());
        VarHandle resultX    // (MemorySegment, long) -> int
                = structLayout.varHandle(PathElement.groupElement("x"));
        VarHandle resultY    // (MemorySegment, long) -> int
                = structLayout.varHandle(PathElement.groupElement("y"));
        System.out.println(StringTemplate.STR. "\{ resultX.get(result) }:\{ resultY.get(result) }" );
    }