2016-11-07 5 views
2

ich eine C-Bibliothek, in der ein struct Verwendung enthält eine weitere (nicht einen Zeiger):Julia - C struct innen struct als Zeiger in Julia gespeichert

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

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

Beispiel Initialisierung:

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; 
} 

Hier die entsprechenden Typen in Julia sind:

type A 
    a::Cint 
    b::Cdouble 
end 

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

Jetzt sizeof(A) = 16, was Sinn macht: für 4 Bytes a Cint, 4 Bytes Padding, so dass die Cdouble ausgerichtet ist, und 8 Bytes für die Cdouble.

Aber Julia sagt sizeof(B) = 24 und fieldoffset setzt nur 8 Byte für Felder a und b, die nur Sinn macht, wenn sie als Referenzen gespeichert werden, statt Werte:

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 

Dies ist ein Problem, da ein Zeiger

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 

jedoch jedes Element einzeln extrahiert gegeben werden kann, die C-Offset: auf eine solche struct aus einer C-Funktion zurückgegeben werden, können nicht ohne einige Änderungen geladen werden. Hier wird klar, dass Julia speichert Typ A innerhalb Typ B als Zeiger, während der gesamte A gespeichert wird inline in 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 

Da unsafe_load auf einem Ptr{B} nicht funktioniert, wenn die B in C erstellt wurde, I mit expliziten Offsets haben eine kompatible Julia Typ zu konstruieren:

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 

Dieses eine Julia-Typ aus einem Zeiger auf C-zugewiesenen Speicher bauen funktioniert, aber dann muss ich (in Julia) einige Feldwerte ändern und übergeben ein Zeiger zurück auf eine C-Funktion. pointer_from_objref funktioniert dafür nicht, da C Strukturelemente als Werte erwartet, aber Julia sie als Zeiger speichert. Jedes Mitglied nach der Struktur hat den falschen Offset.

Fragen: Wie bekomme ich einen Zeiger auf Daten mit dem gleichen Speicherlayout wie in C? Gibt es eine Möglichkeit, Julia zu sagen, B.a und wertmäßig zu speichern?

Antwort

3

Anstelle von type deklarieren Sie diese als immutable für C-kompatibles, inliniertes Layout. immutable B ... ergibt sizeof(B) == 40.

Per der manual:

Wenn rekursiv verwendet, isbits Typen inline gespeichert werden. Alle anderen Typen werden als Zeiger auf die Daten gespeichert. Beim Spiegeln einer Struktur, die von-Wert in einer anderen Struktur in C verwendet wird, ist es unbedingt erforderlich, dass Sie nicht versuchen, die Felder manuell zu kopieren, da dadurch die korrekte Feldausrichtung nicht beibehalten wird. Erklären Sie stattdessen einen unveränderlichen Isbits-Typ und verwenden Sie stattdessen diesen. Unbenannte Strukturen sind in der Übersetzung zu Julia nicht möglich.

+0

Für diese Anwendung muss ich in der Lage sein, die Werte zu ändern, so 'unveränderlich' ist ein Problem. Gibt es einen Weg, um es zu umgehen? –

+1

Nein, aber Sie können immer noch auf einen Zeiger schreiben. Siehe http://stackoverflow.com/questions/40458122/updating-a-field-value-of-ac-struct-from-julia –

+0

(und immutables, die in einem veränderbaren Container gespeichert sind, können ersetzt werden. Erstellen Sie einen neuen und speichern Sie ihn zB in einem Array Ich habe einige Hilfsmakros gesehen, die herumschweben, um nur ein oder zwei Felder zu aktualisieren, obwohl ich sie im Moment nicht finden kann.) –