首页 文章

Julia - 结构内部的C struct存储为Julia中的指针

提问于
浏览
3

我正在使用一个C库,其中一个结构包含另一个结构(不是指针):

typedef struct {
    int a;
    double b;
} A;

typedef struct {
    A a;
    A b;
    int c;
} B;

示例初始化:

B* mkB() {
    A a = {2, 3.0};
    A b = {4, 5.0};
    B* rv = (B*)malloc(sizeof(B));
    rv->a = a;
    rv->b = b;
    rv->c = 6;
    return rv;
}

以下是Julia中的相应类型:

type A
    a::Cint
    b::Cdouble
end

type B
    a::A
    b::A
    c::Cint
end

现在 sizeof(A) = 16 ,这是有意义的: Cint 为4个字节,填充为4个字节,因此 Cdouble 对齐, Cdouble 为8个字节 .

但Julia说 sizeof(B) = 24fieldoffset 只为字段 ab 设置了8个字节,只有将它们存储为引用而不是值时才有意义:

julia> ofA = [Int(fieldoffset(A, i)) for i in 1:nfields(A)]'
1x2 Array{Int64,2}:
 0  8    

julia> ofB = [Int(fieldoffset(B, i)) for i in 1:nfields(B)]'
1x2 Array{Int64,2}:
 0  8  16

这是一个问题,因为如果不进行一些更改,就无法加载从C函数返回的这种结构的指针:

julia> x = A(2, 3.0); y = A(4, 5.0); z = B(x, y, 6);
julia> pz = pointer_from_objref(z);            #from Julia
julia> pb = ccall(("mkB", mylib), Ptr{B}, ()); #from C

julia> unsafe_load(reinterpret(Ptr{B}, pz))    #works as expected
B(A(2,3.0),A(4,5.0),6)

julia> unsafe_load(reinterpret(Ptr{B}, pb))    #segfaults

但是,给定C偏移量,可以单独提取每个元素 . 这里很明显,Julia将类型 A 类型存储在 B 类型中作为指针,而整个 A 存储在C中内联:

julia> unsafe_load(reinterpret(Ptr{A}, pb))        #from C
A(2,3.0)                                           #correct

julia> unsafe_load(reinterpret(Ptr{A}, pz))        #from Julia
A(1274099440,6.9455678017566e-310)                 #incorrect

julia> unsafe_load(unsafe_load(reinterpret(Ptr{Ptr{A}}, pz)))
A(2,3.0)                                           #correct

julia> unsafe_load(reinterpret(Ptr{Cint}, pb+32))  #from C
6                                                  #B.c offset 32 bytes

julia> unsafe_load(reinterpret(Ptr{Cint}, pz+16))  #from Julia
6                                                  #B.c offset 16 bytes

由于如果B是在C中创建的, Ptr{B} 上的 unsafe_load 不起作用,我一直使用显式偏移来构造兼容的Julia类型:

function B(p::Ptr{B})           #inner constructor for B
    of = [0, 16, 32]            #offsets in C
    jB = new()
    for i in 1:nfields(B)
        v = unsafe_load(reinterpret(Ptr{fieldtype(B,i)}, p + of[i]))
        setfield!(jB, fieldname(B, i), v)
    end
end

这可以从指向C分配内存的指针构建Julia类型,但是我需要更改一些字段值(在Julia中)并将指针传递回C函数 . pointer_from_objref 不适用于此,因为C期望struct元素作为值,但Julia将它们存储为指针 . 结构后面的每个成员都有错误的偏移量 .

问题:如何获得指向具有与C相同内存布局的数据的指针?有没有办法告诉朱莉娅按值存储 B.aB.b

1 回答

  • 3

    而不是 type ,将这些声明为 immutable ,用于C兼容的内联布局 . immutable B ... 给出 sizeof(B) == 40 .

    manual

    递归使用时,isbits类型以内联方式存储 . 所有其他类型都存储为指向数据的指针 . 当镜像在C中的另一个结构体内使用的值的结构时,必须不要尝试手动复制字段,因为这不会保留正确的字段对齐 . 相反,声明一个不可变的isbits类型并使用它 . 在Julia的翻译中无法使用未命名的结构 .

相关问题