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

时间:2016-11-07 20:17:17

标签: c pointers struct julia

我正在使用一个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;
}

以下是朱莉娅的相应类型:

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个字节。 / p>

但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中创建的,unsafe_load上的Ptr{B}不起作用,我一直在使用显式偏移来构建兼容的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相同内存布局的数据的指针?有没有办法告诉Julia按值存储B.aB.b

1 个答案:

答案 0 :(得分:4)

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

根据manual

  

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