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?
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? –
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 –
(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.) –