如何在 jnr ffi 中使用结构体和结构体

kmp*_*kmp 1 c java struct ffi jnr

我有以下c代码:

#include <stdio.h>

struct Second {
    int a_number;
};

struct Top {
    struct Second second;
};

void lets_go(struct Top *top) {
    printf("The number is %d\n", top->second.a_number);
}
Run Code Online (Sandbox Code Playgroud)

我想从 Java 中执行此操作:

int main(void) {
    struct Top top = {{8}};
    lets_go(&top);
}
Run Code Online (Sandbox Code Playgroud)

我也想使用jnr-ffi,所以我查看了测试并最终得到以下结果:

package structs.playing;

import structs.playing.Program.Test.Top;
import structs.playing.Program.Test.Second;
import jnr.ffi.LibraryLoader;
import jnr.ffi.Runtime;
import jnr.ffi.Struct;

public final class Program {

    public static interface Test {

        void lets_go(Top top);

        public static final class Second extends Struct {               
            public final Signed32 a_number = new Signed32();                
            public Second(final Runtime runtime) {
                super(runtime);
            }
        }

        public static final class Top extends Struct {              
            public Second second;                           
            public Top(final Runtime runtime) {
                super(runtime);
            }
        }
    }

    public static void main(final String[] args) {

        Test test = LibraryLoader.create(Test.class).load("test");
        Runtime runtime = Runtime.getRuntime(test);         
        Top top = new Top(runtime);
        Second second = new Second(runtime);
        top.second = second;
        second.a_number.set(7);         
        test.lets_go(top);
    }    
}
Run Code Online (Sandbox Code Playgroud)

问题是 的值a_number根本没有设置,所以我在输出中得到一个垃圾值,例如:

The number is 46645760
Run Code Online (Sandbox Code Playgroud)

那么我怎样才能得到与我的 C 代码相同的结果呢?

Kyl*_*man 5

编辑:我花了更多的时间查看代码,我对创建结构的理解略有改变。

我相信您应该声明有关结构的所有内容并将其定为最终的,因为每次您声明一个新成员时,它都会向它所属的结构注册自身。

struct 中有一些适用于每个用例的辅助函数。重载的 array() 方法允许您注册成员或结构数组。inner() 方法允许您注册单个结构。否则,您只需定义新的成员对象,它们就会自动注册。

例如:

struct Second {
    int a_number;
};

struct Top {
    struct Second second;
    struct Second seconds[5];
    int another_number;
    int more_numbers[5];
};
Run Code Online (Sandbox Code Playgroud)

表示为:

public final class Second extends Struct {
    public final Signed32 a_number = new Signed32();
    public Second(final Runtime runtime) {
        super(runtime);
    }
}

public final class Top extends Struct {              
    public final Second second = inner(new Second(getRuntime()));  
    public final Second[] seconds = array(new Second[5]);
    public final Signed32 another_number = new Signed32();
    public final Signed32[] more_numbers = array(new Signed32[5]);
    public Top(final Runtime runtime) {
        super(runtime);
    }
}
Run Code Online (Sandbox Code Playgroud)

原文:我相信执行此操作的正确方法是使用接受 (Runtime, Struct) 的重载 Struct 构造函数。 https://github.com/jnr/jnr-ffi/blob/master/src/main/java/jnr/ffi/Struct.java#L129

protected Struct(Runtime runtime, Struct enclosing) {
    this(runtime);
    __info.alignment = enclosing.__info.alignment;
}
Run Code Online (Sandbox Code Playgroud)

此构造函数强制封闭结构共享其内存。所以在你的例子中我认为它看起来像这样:

package structs.playing;

import structs.playing.Program.Test.Top;
import structs.playing.Program.Test.Second;
import jnr.ffi.LibraryLoader;
import jnr.ffi.Runtime;
import jnr.ffi.Struct;

public final class Program {

    public static interface Test {

        void lets_go(Top top);

        public static final class Second extends Struct {               
            public final Signed32 a_number = new Signed32();                
            public Second(final Runtime runtime, final Struct enclosing) {
                super(runtime, enclosing);
            }
        }

        public static final class Top extends Struct {              
            public Second second;                           
            public Top(final Runtime runtime) {
                super(runtime);
            }
        }
    }

    public static void main(final String[] args) {

        Test test = LibraryLoader.create(Test.class).load("test");
        Runtime runtime = Runtime.getRuntime(test);         
        Top top = new Top(runtime);
        Second second = new Second(runtime, top);
        top.second = second;
        second.a_number.set(7);         
        test.lets_go(top);
    }    
}
Run Code Online (Sandbox Code Playgroud)

请注意对 Second 构造函数的更改,并注意我将 Top 对象传递给了 Second 对象,因此它知道 top 正在包围它。这未经测试,只是分享我在尝试理解代码时发现的内容。

我认为你的例子中发生的事情是第二个对象正在分配它自己的内存,而 Top 对此一无所知。

如果这不起作用,我建议您考虑这样做:

public static final class Top extends Struct {              
    public Second second = new Second(getRuntime(), this);                           
    public Top(final Runtime runtime) {
        super(runtime);
    }
}
Run Code Online (Sandbox Code Playgroud)