Управление внешними ресурсами (аналогично RAII в С++?)

В общем lisp, каков предпочтительный способ управления внешними ресурсами (сокеты, дескрипторы файловой системы и т.д.)?

Я пытаюсь сделать простой opengl 2d платформер общим lisp. Проблема в том, что я не совсем уверен, как отслеживать текстуры OpenGL (необходимо удалить с помощью glDeleteTextures, когда они больше не нужны).

В С++ я предпочел использовать следующую схему:

  • Сделать класс текстур
  • Сделать умные/слабые указатели для этого класса текстур
  • Сохранять текстуры в карте (словарь/хэш-таблицу), которая сопоставляет имена файлов слабым указателям на текстуры.
  • Когда запрашиваются новые текстуры, посмотрите на карту и посмотрите, есть ли нулевой (нулевой) слабый указатель. Если он доступен, верните существующий объект, иначе загрузите новую текстуру.

Однако я не совсем уверен, как перенести эту схему на общий lisp, потому что:

  • Нет деструкторов.
  • Там сборщик мусора, и кажется, что моя реализация (clozureCL на платформе Windows) поддерживает финализаторы, но, насколько я могу судить, не рекомендуется использовать финализаторы в общем lisp, потому что они не детерминированы.
  • Предпочтительный способ управления ресурсами с помощью (with-* выглядит непригоден, потому что ресурсы могут использоваться совместно и загружаться/выгружаться в середине вызова функции.

Насколько я могу судить, существует несколько доступных подходов:

  • Откажитесь от автоматического управления ресурсами и сделайте это вручную.
  • Внедрите что-то похожее на С++ RAII, слабый указатель и smartpointer с помощью макросов (этот код, вероятно, не работает):

    (defclass destructible () ())
    
    (defmethod destroy ((obj destructible)) (print (format nil "destroy: ~A" obj)))
    
    (defparameter *destructible-classes-list* nil)
    
    (defmethod initialize-instance :after ((obj destructible) &key)
      (progn
          (push *destructible-classes-list* obj)
          (print (format nil "init-instance: ~A" obj))))
    
    (defmacro with-cleanup (&rest body)
      `(let ((*destructible-classes-list* nil))
        (progn ,@body (mapcar (lambda (x) (destroy x)) *destructible-classes-list*))))
    
    (defclass refdata (destructible)
      ((value :accessor refdata-value :initform nil)
       (ref :accessor refdata-ref :initform 0)
       (weakrefcount :accessor refdata-weakref :initform 0)))
    
    (defmethod incref ((ref refdata))
      (if ref (incf (refdata-ref ref))))
    
    (defmethod decref ((ref refdata))
      (if ref
        (progn (decf (refdata-ref ref))
         (if (<= (refdata-ref ref) 0) 
           (progn (destroy (refdata-value ref))
              (setf (refdata-value ref) nil))))))
    
    (defmethod incweakref ((ref refdata))
      (if ref (incf (refdata-weakref ref))))
    
    (defmethod decweakref ((ref refdata))
      (if ref (decf (refdata-weakref ref))))
    
    (defclass refbase (destructible) ((data :accessor refbase-data :initform nil)))
    
    (defmethod deref ((ref refbase))
      (if (and (refbase-data ref) (refdata-value (refbase-data ref)))
        (refdata-value (refbase-data ref))
        nil))
    
    (defclass strongref (refbase) ())
    
    (defclass weakref (refbase) ())
    
    (defmethod destroy :before ((obj strongref))
      (decref (refbase-data obj)))
    
    (defmethod destroy :before ((obj weakref))
      (decweakref (refbase-data obj)))
    
    (defmethod initialize-instance :after ((obj strongref) &key)
      (incref (refbase-data obj)))
    
    (defmethod initialize-instance :after ((obj weakref) &key)
      (incweakref (refbase-data obj)))
    

Есть ли лучший способ сделать это?

Концепции С++ Объяснение: Что такое умный указатель и когда я должен его использовать?

Ответ 1

Если вы хотите обрабатывать область динамической протяженности, используйте UNWIND-PROTECT. Если программа выходит из этой области - обычно или при ошибке - вызывается форма очистки. Просто освободите там или сделайте все, что захотите.

Иногда Lisps использует какой-то механизм "ресурса" для отслеживания используемых и неиспользуемых объектов. Такая библиотека обеспечивает быстрое выделение из пула, выделение, инициализацию, освобождение, отображение ресурсного ресурса. CLIM определяет примитивную версию: Ресурсы. CCL имеет аналогичную примитивную версию.

В Lisp с "таймерами" вы можете периодически запускать функцию, которая ищет "объекты" для освобождения. В CCL вы можете использовать поток, который спит в течение определенного времени, используя PROCESS-WAIT.

Немного о вашем стиле кодирования:

  • FORMAT может напрямую выводиться в поток, не требуется PRINT
  • (defmethod foo ((e bar)) (if e ...)): IF не имеет смысла. e всегда будет объектом.
  • many PROGN не нужны. При необходимости его можно удалить, если IF заменяется на WHEN.