2016-09-27 2 views
5

Ich erweitere ein Julia-Paket, das eine C-Bibliothek verwendet. Ich muss einige C-Funktionen von Julia anrufen. Sie sehen etwas wie folgt aus:Julia - C-Schnittstelle mit nicht-fundamentalen Typen

struct contained { 
    int x; 
    int y; 
    int z; 
}; 
struct mystruct { 
    int n; 
    contained* arr; 
}; 
mystruct* mk_mystruct(int n, contained* arr); 
void use_mystruct(mystruct* foo); 

Ich habe auch die entsprechenden Typen in Julia erklärt:

type contained 
    x::Int64 
    y::Int64 
    z::Int64 
end 
type mystruct 
    n::Int64 
    arr::Array{contained, 1} 
end 

Um ccall Funktionen, die eine contained* als Argument nehmen, funktioniert alles einwandfrei Behandlung der contained* als Ptr{Int64}:

con = fill(0, 5, 3); 
mys = ccall((:mk_mystruct, "mylib"), Ptr{mystruct}, (Int64, Ptr{Int64}), n, con) 

ich nehme an, dies funktioniert, weil contained den gleichen m hat Emory Layout als ein Array von Int64s. So wird es auch anderswo im Julia-Paket gemacht. Aber der einzige Weg, den ich kenne, um den Wert des zurückgegebenen mystruct zu überprüfen, ist, es mit unsafe_load zu dereferenzieren, an diesem Punkt stürzt Julia von einem segfault ab. Was ist der richtige Weg, um einen Zeiger in Julia zu dereferenzieren?

Die C-Bibliothek enthält auch recht-Druckfunktionen, so dass anstelle von dereferencing den Zeiger in Julia ich den Zeiger als undurchsichtig behandeln könnte und es zurück zu dieser C-Funktion übergeben:

void print_mystruct(mystruct* foo, FILE* outputfile) 

Im C-Code, dies wird mit outputfile=stdout aufgerufen. Wie würde ich dies mit ccall einrichten? Das funktioniert natürlich nicht:

ccall((:print_mystruct, "mylib"), Void, (Ptr{mystruct}, Ptr{Void}), mys, stdout) 

Was sollte ich statt Ptr{Void} und stdout? Wie implementiert Julia I/O in der C-Schnittstelle?

+0

Warum 'Int64' als ein entsprechender Typ zu C's' int' verwenden? "int" ist gewöhnlich 32 Bit, vielleicht so schmal wie 16 Bit und kann viele andere größere Größen haben. – chux

+0

Eigentlich habe ich 'Int' benutzt, aber da' Int' 'Int64' auf meiner Maschine bedeutet, habe ich' Int64' in den obigen Code geschrieben, so dass es keine Verwirrung geben würde. Mein Verständnis ist, dass Julia die passende Größe von "Int" erkennt, so dass auf dieser Maschine Cs "int" auch 64 Bit ist. Ist das korrekt? –

+1

Die Größe eines 'int' in C wird vom Compiler und den Optionen gesteuert, nicht vom Prozessor. Sowohl 32-Bit als auch 64-Bit "int" werden aus verschiedenen Gründen mit Compilern für 64-Bit-Maschinen gefunden. – chux

Antwort

3

Wenn Sie den Typ in Julia deklarieren, müssen Sie die gleichen Typen wie C erklären:

type contained 
    x::Cint 
    y::Cint 
    z::Cint 
end 
type mystruct 
    n::Cint 
    arr::Ptr{contained} 
end 

Der Typ Julia Array{contained, 1} würde jl_value_t* in C entsprechen und die Julia Typ Int würde intptr_t in C entsprechen

Ich kenne keine Plattform-agnostische Weise, um ein Handle zu stdout zu bekommen, da die meisten Plattformen ein C-Header-Makro erweitern müssen, um den echten Symbolnamen herauszufinden. Zum Beispiel auf macOS, wird es zu __stdoutp umbenannt:

julia> unsafe_load(cglobal(:__stdoutp, Ptr{Void})) 
Ptr{Void} @0x00007fff751f7348 

julia> ccall(:fprintf, Csize_t, (Ptr{Void}, Cstring, Cint...), ans, "hi\n") 
hi 
0x0000000000000003 

Sie interessiert sein, die Clang.jl package bei der Prüfung, die automatisch diese Definitionen aus Parsen der Header-Dateien erzeugen kann.

+0

Ok, ich ersetzte "Int64" durch "Cint" überall und änderte 'Array {enthielt, 1}' in 'Ptr {enthielt}'. Jetzt möchte ich überprüfen, dass 'mk_mystruct' das Richtige getan hat, also definiere ich' Base.show (m :: mystruct) '. Wie kann ich das mit einem 'Ptr {contained}' anstatt einem 'Array {contained, 1}' machen? Gibt es eine Beziehung zwischen Zeigern und Arrays in Julia? –

Verwandte Themen