Ho un'API C che sto cercando di utilizzare in Clojure, tramite l'API JNA. Il mio problema può essere meglio dimostrato con il seguente esempio. Dire che ho questo codice C in una libreria:Ottenere e passare le strutture in base al valore in Clojure con JNA
typedef struct {
int foo;
int bar;
double baz;
} MyStruct;
MyStruct createStruct() {
MyStruct myStruct;
myStruct.foo = 3;
myStruct.bar = 4;
myStruct.baz = 3.14;
return myStruct;
}
double addStruct(MyStruct myStruct) {
return myStruct.foo + myStruct.bar + myStruct.baz;
}
In questo esempio, mi piacerebbe chiamare createStruct
, e quindi passare tale risultato addStruct
. Il punto importante qui è che MyStruct
è passato al valore sia come tipo di ritorno che come argomento. In nessun momento ho bisogno di leggere effettivamente i valori dei campi in MyStruct
.
Inoltre, nel mio sistema, funzioni native sono avvolti in questo modo:
; `quux` is defined in `some-lib` and returns an `int`
(let [fn- (com.sun.jna.Function/getFunction "some-lib" "quux")]
(fn [& args]
(.invoke fn- Integer (to-array args))))
L'obiettivo è quello di ottenere un tipo per sostituire Integer
sopra che vi avvolgerà MyStruct
come un valore.
L'unica risorsa che ho trovato che copre questo argomento è this article, ma si parla solo di come passare le strutture per riferimento.
Dato che, qui ci sono i diversi approcci che ho provato a prendere per risolvere questo problema:
creare una classe che eredita da
Structure
, che è JNA di built-in mechanism for creating and using structs. Date le informazioni su quella pagina, ho cercato di creare la seguente classe utilizzando solo Clojure:class MyStruct extends Structure implements Structure.ByValue { int foo; int bar; double baz; }
deftype
non funziona per questo scenario, dal momento che la classe deve ereditare dalla classe astrattaStructure
, egen-class
doesn' t lavorare perché la classe deve avere i campi pubblici non staticifoo
,bar
ebaz
.Da quello che posso dire, nessuno dei servizi di interoperabilità Clojure Java standard può creare la classe precedente.
Creare una classe che eredita da
Structure
e sovrascrivere i metodi getter/setter del campo struct. Poichégen-class
è (credo) l'unico costrutto Clojure che consente l'ereditarietà diretta e non supporta più campi pubblici non statici, l'alternativa successiva è semplicemente non utilizzare affatto i campi. Looking at theStructure
abstract class documentation, sembra che ci sia una combinazione di sostituzioni possibile per utilizzare i campi della struttura "virtuale", in modo tale da ottenere e impostare i dati da un'origine diversa (such as thestate
field fromgen-class
). Guardando attraverso la documentazione, sembra sovrascriverereadField
,writeField
, e altri metodi possono avere l'effetto desiderato, ma non sono riuscito a capire come farlo leggendo la documentazione, e non sono riuscito a trovare esempi simili online.Utilizzare una classe di memoria diversa. JNA ha una miriade di classi per il wrapping di tipi nativi.Mi chiedo se, piuttosto che definire e utilizzare una classe
Structure
, potrei usare un'altra classe generica che può prendere un numero arbitrario di bit (come il modo in cui loInteger
può contenere qualsiasi cosa di 4 bit di larghezza, indipendentemente dal tipo di sorgente "effettivamente" è). È possibile, ad esempio, dire che una funzione restituisce una matrice di byte di lunghezza 16 (poichésizeof(MyStruct)
è 16)? Che dire del wrapping di una matrice a dimensione fissa in una classe contenitore che implementaNativeMapped
? Non sono riuscito a trovare esempi di come fare neanche.
JNA fornisce allocazione di memoria di blocco in forma di 'com.sun.jna.Memory'. Da lì, puoi estrarre tipi arbitrari da offset arbitrari in memoria usando i metodi 'Pointer.getXXX()'. – technomage
@technomage L'uso di 'Memory' come tipo di ritorno per' createStruct' restituisce un 'Pointer', il cui indirizzo è' 0x0000000400000003'. In altre parole, considera il valore della memoria grezza della struttura come un puntatore. Quando si dereferenzia (tramite uno dei metodi 'read' o' getXXX'), si blocca la VM. –
Il problema è che il parametro 'struct' e la semantica del valore di ritorno variano a seconda del compilatore e il layout' Structure' viene effettivamente utilizzato dal codice nativo per determinare come impostare correttamente lo stack. Stavo suggerendo che potresti essere in grado di usare 'Memoria' per assegnare un blocco arbitrario di memoria a un'istanza' Structure.ByValue' esistente. – technomage