Sto provando a scrivere il wrapper CFFI per la libreria Sundials CVODE. SWIG stava soffocando sulle intestazioni delle Sundials poiché erano abbastanza interconnesse e SWIG non riusciva a trovare le intestazioni giuste, quindi l'ho fatto a mano: un po 'laborioso ma sono riuscito.Common Lisp CFFI: puntatore al puntatore
Ora sto provando a verificare se funziona correttamente. Per ora, semplicemente creando "l'oggetto problematico" e cancellandolo. È qui che inizia il problema. Così, il "problema oggetto" è assegnato tramite funzione
SUNDIALS_EXPORT void *CVodeCreate(int lmm, int iter);
Per che ho creato l'involucro:
(cffi:defcfun "CVodeCreate" :pointer
(lmm :int)
(iter :int))
PS. SUNDIALS_EXPORT
(almeno su Unix) è praticamente nulla.
Ora, per distruggere l'oggetto, meridiane utilizza la propria funzione:
SUNDIALS_EXPORT void CVodeFree(void **cvode_mem);
Così, ho bisogno di passare il riferimento all'oggetto creato da CVodeCreate
. In C, se la mia memoria non è colpa, avrei fatto qualcosa come CVodeFree(&problem_object)
. In CL ho scritto questo wrapper per la funzione:
(cffi:defcfun "CVodeFree" :void
(cvode-mem :pointer))
Così, qui COVDE-MEM
è un puntatore a un puntatore. La domanda è come ottenere il puntatore del puntatore in CL/CFFI? Ecco l'inizio del codice:
(defvar *p* (cvodecreate 1 2))
(. PS Non preoccupatevi dei numeri passati a CVODECREATE
, hanno solo dire che i metodi da utilizzare, ancora bisogno di costanti definite per renderlo più leggibile)
Così *P*
è qualcosa di simile
#.(SB-SYS:INT-SAP #X7FFFE0007060)
Se mi passa direttamente al CVODEFREE
, finisce per errore:
CL-USER> (cvodefree *p*)
; Evaluation aborted on #<SIMPLE-ERROR "bus error at #X~X" {1005EC9BD3}>.
Ho provato a passare (CFFI:POINTER-ADDRESS *P*)
ma risulta in un simile "errore del bus ..." (non sono nemmeno sicuro che questa funzione restituisca ciò di cui ho bisogno). Ho anche provato a fare (CFFI:MAKE-POINTER (CFFI:POINTER-ADDRESS *P*))
, ancora una volta senza alcun successo.
This question suggerisce questo approccio:
(cffi:with-foreign-object (p :pointer)
(setf (cffi:mem-ref p :pointer) (cvodecreate 1 2))
(cvodefree p))
Questo funziona (almeno che non si genera un errore). Penso di capire come funziona: crea (assegna la memoria per) un puntatore a un puntatore P
, il cui valore MEM-REF
(o in termini C sta per dereferenziare *p
) viene riempito dal risultato su CVODECREATE
. Infine, sto passando questo puntatore-a-un-puntatore a CVODEFREE
, che si aspetta esattamente questo. Infine, la memoria allocata per P
viene liberata al termine del modulo. È questo l'approccio corretto? Ed è l'unico che posso prendere?
Sarebbe molto interessante sapere perché hanno scelto quella firma per 'CVodeFree'. Sembra "over-engineering" e cerca di essere più intelligente. –
@DanielJour Per quanto mi ricordi, questo modo non è insolito in C-world. Forse, liberano la memoria e NULL il puntatore lì. Ma sono d'accordo, assolutamente inutile. – mobiuseng