Common Lisp CFFI: указатель на указатель

Я пытаюсь написать оболочку CFFI для библиотеки Sundials CVODE. SWIG задыхался от заголовков солнечных часов, так как они довольно взаимосвязаны, и SWIG не мог найти правильные заголовки, поэтому я сделал это вручную: немного трудоемко, но я справился.

Теперь я пытаюсь проверить, правильно ли он работает. А пока просто создайте «проблемный объект» и удалите его. Вот где проблема начинается. Итак, "проблемный объект" выделяется через функцию

SUNDIALS_EXPORT void *CVodeCreate(int lmm, int iter);

Для чего я создал обертку:

(cffi:defcfun "CVodeCreate" :pointer
  (lmm :int)
  (iter :int))

PS. SUNDIALS_EXPORT (по крайней мере, в Unix) ничего не значит.

Теперь для уничтожения объекта Солнечные часы используют собственную функцию:

SUNDIALS_EXPORT void CVodeFree(void **cvode_mem);

Итак, мне нужно передать ему ссылку на объект, созданный CVodeCreate. В C, если моя память не виновата, я бы сделал что-то вроде CVodeFree(&problem_object). В CL я написал эту оболочку для функции:

(cffi:defcfun "CVodeFree" :void
  (cvode-mem :pointer))

Итак, здесь COVDE-MEM — это указатель на указатель. Вопрос как получить указатель указателя в CL/CFFI? Вот начало кода:

(defvar *p* (cvodecreate 1 2))

(PS. Не беспокойтесь о числах, переданных в CVODECREATE, они просто говорят, какие методы использовать, все еще нужно определить константы, чтобы сделать его более читаемым)

Так что *P* это что-то вроде

#.(SB-SYS:INT-SAP #X7FFFE0007060)

Если я передам его напрямую CVODEFREE, это закончится ошибкой:

CL-USER> (cvodefree *p*)
; Evaluation aborted on #<SIMPLE-ERROR "bus error at #X~X" {1005EC9BD3}>.

Я пытался передать (CFFI:POINTER-ADDRESS *P*), но это приводит к похожей "ошибке шины..." (даже не уверен, возвращает ли эта функция то, что мне нужно). Я также пытался сделать (CFFI:MAKE-POINTER (CFFI:POINTER-ADDRESS *P*)), но снова безуспешно.

Этот вопрос предлагает такой подход:

(cffi:with-foreign-object (p :pointer)
           (setf (cffi:mem-ref p :pointer) (cvodecreate 1 2))
           (cvodefree p))

Это работает (по крайней мере, это не выдает ошибку). Я думаю, что понимаю, как это работает: он создает (выделяет память) указатель на указатель P, чей MEM-REF (или в терминах C будет разыменование *p) заполняется результатом CVODECREATE. Наконец, я передаю этот указатель на указатель CVODEFREE, который ожидает именно этого. Наконец, память, выделенная для P, освобождается после завершения формы. Это правильный подход? И это единственное, что я могу взять?


person mobiuseng    schedule 07.03.2016    source источник
comment
Было бы очень интересно узнать, почему они выбрали именно эту подпись для CVodeFree. Похоже на чрезмерную инженерию и попытку быть очень умным.   -  person Daniel Jour    schedule 07.03.2016
comment
@DanielJour Насколько я помню, в этом мире нет ничего необычного. Возможно, они освобождают там память и NULL указатель. Но соглашусь, совершенно бесполезно.   -  person mobiuseng    schedule 08.03.2016


Ответы (1)


Да, ваш подход выглядит правильным, вот небольшой тест, чтобы показать концепцию, которую можно запустить прямо из 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. Я уверен, что вы знали это к настоящему времени, извините, что так долго, чтобы получить вам ответ. Надеюсь, это может помочь другим

person Baggers    schedule 26.07.2016
comment
mem-aref действительно помогло. Спасибо! - person mihai; 27.09.2019