Julia - 结构内的 C 结构作为指针存储在 Julia 中

Mat*_*ord 8 c struct pointers julia

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

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

typedef struct {
    A a;
    A b;
    int c;
} B;
Run Code Online (Sandbox Code Playgroud)

示例初始化:

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;
}
Run Code Online (Sandbox Code Playgroud)

以下是 Julia 中的相应类型:

type A
    a::Cint
    b::Cdouble
end

type B
    a::A
    b::A
    c::Cint
end
Run Code Online (Sandbox Code Playgroud)

现在sizeof(A) = 16,这是有道理的:a Cint4 个字节,填充 4 个字节以便Cdouble对齐,8 个字节用于Cdouble.

但朱莉娅说sizeof(B) = 24,和fieldoffset看跌期权只有8个字节的领域ab,这才有意义,如果它们被存储作为参考,而不是值:

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
Run Code Online (Sandbox Code Playgroud)

这是一个问题,因为从 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
Run Code Online (Sandbox Code Playgroud)

但是,给定 C 偏移量,可以单独提取每个元素。这里很明显 Julia 将 type 存储A在 type 中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
Run Code Online (Sandbox Code Playgroud)

由于如果 B 是在 C 中创建的,则unsafe_loadaPtr{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
Run Code Online (Sandbox Code Playgroud)

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

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

Isa*_*ton 6

而不是type,将它们声明为immutable与 C 兼容的内联布局。immutable B ...sizeof(B) == 40.

根据手册

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