Как завершить lisp:struct, содержащую указатель?
Я портирую Lightweight Communications и Marshalling с julia на lisp, так как он имеет лучший API. Я использовал Swig для генерации вызовов функции C.
Я хочу знать, является ли это безопасным использованием указателя C или нет. вот функция создания:
(defun create-lcm (&optional (provider (null-pointer)))
(let* ((ptr (lcm_create provider))
(addr (cffi:pointer-address ptr)))
(tg:finalize ptr (lambda () (lcm_destroy (cffi:make-pointer addr))))
(if (NULL-POINTER-P ptr)
(error "lcm creation error"))
(%create-lcm :pointer ptr :provider provider
:file-descriptor (lcm_get_fileno ptr))))
Вопрос:
- Как правильно завершить указатель C?
- Как сделать тест для этого?
Любые другие заметки / советы приветствуются.
Заранее спасибо.
2 ответа
Вот несколько вещей, которые были не правы:
- Присоединение финализатора к указателю, который может быть нулевым
- Я не уверен, что вам разрешено прикреплять финализатор к внешнему указателю. Может быть ты.
- Вы должны быть осторожны с финализаторами и gc. Если финализатор ссылается на объект, который он завершает, то этот объект и его финализатор поддерживают друг друга живыми (их нельзя собрать сразу, поскольку финализатор может где-то хранить ссылку на объект, и тогда этот объект будет жив и поэтому не должен '). т были завершены.
Я не знаю, правильно ли это, но лучше
(defun create-lcm (&optional (provider (null-pointer))
(let ((ptr (lcm_create provider)))
(when (null-pointer-p ptr)
(error “lcm creation error”))
(flet ((finaliser () (lcm_destroy ptr)))
(let ((result (%create-lcm :pointer ptr :provider provider
:file-descriptor (lcm_get_fileno ptr))))
(tg:finalize result #'finaliser)
result))))
Вот некоторые вещи, которые не так:
- Если есть ошибка от
%create-lcm
или жеlcm_get_fileno
тогда финализатор не запустится
Вы можете прочитать о cl-autowrap
, который используется, прежде всего, для упаковки SDL 2 в cl-sdl2
, Библиотека предоставляет тонкие обертки вокруг указателей, которые уже освобождают память при завершении.
Я также думаю, что рекомендуемый способ использования финализаторов - это использовать их только для устранения возможных утечек, учитывая, что у вас мало контроля над тем, когда и как выполняется функция очистки (например, какой поток, какая динамическая среда).
Один из способов управления памятью - это заранее выделить свои структуры и очистить их, когда они вам больше не нужны (пул). Или вы можете определить функцию или макрос, который определяет область видимости, такую, что память выделяется при входе в нее и освобождается при выходе, используя unwind-protect:
(defmacro with-lcm ((context &rest options) &body body)
(let ((internal (gensym)))
`(let* ((,internal (create-lcm ,@options))
(,context ,internal))
(unwind-protect (progn ,@body)
(destroy-lcm ,internal)))))