2016-03-07 23 views
5

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?

+0

Sarebbe molto interessante sapere perché hanno scelto quella firma per 'CVodeFree'. Sembra "over-engineering" e cerca di essere più intelligente. –

+0

@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

risposta

2

Sì, il tuo approccio sembra giusto, ecco un piccolo test per mostrare il concetto che può essere eseguito direttamente dal repl.

(let* (;; a float 
     (v0 32s0) 

     ;; a pointer to a float foreign memory 
     (p0 (cffi:foreign-alloc :float :initial-element v0))) 

    ;; a new pointer 
    (cffi:with-foreign-object (p1 :pointer) 

    ;; make the new pointer point to the first pointer 
    (setf (cffi:mem-aref p1 :pointer) p0) 

    ;; dereferencing twice should give you the original number 
    (cffi:mem-aref (cffi:mem-aref p1 :pointer) :float))) 

p.s.Sono sicuro che lo sapevi ormai, mi dispiace che ci sia voluto così tanto tempo per darti una risposta. Speriamo che questo possa aiutare gli altri