RAII в схеме?

Там должен так или иначе реализовать Ресурс, Acquisation является Инициализация в Схеме?

Я знаю, что RAII не работает хорошо на языках редактора GC (так как мы понятия не имеем whe, объект уничтожается). Однако Схема имеет хорошие вещи как продолжения, динамический ветер, и закрытия - являются там способом использовать некоторую комбинацию этого для реализации RAII?

В противном случае, как интриганы разрабатывают свой код для не использования RAII?

[Типичный пример, с которым я сталкиваюсь, следующий:

У меня есть 3D сетка, у меня есть Буферный Объект Вершины, присоединенный к ней, когда Сетка больше не используется, я хочу VBO, освобожденный.]

Спасибо!

7
задан anon 19 January 2010 в 02:46
поделиться

1 ответ

[1139443-

Если это всего лишь разовая, вы всегда можете просто написать макрос, который обернутся вокруг динамического ветра , выполняя настройку и разрыв в ранее и после Thunks (Я предполагаю, что выделение-вершина-буферный объект и и Buffer-Object - ваш конструктор и деструкторы здесь):

(define-syntax with-vertex-buffer-object
  (syntax-rules ()
    ((_ (name arg ...) body ...)
     (let ((name #f))
       (dynamic-wind
         (lambda () (set! name (allocate-vertex-buffer-object args ...)))
         (lambda () body ...)
         (lambda () (free-vertex-buffer-object name) (set! name #f)))))))

Если это шаблон, который Вы используете много, для разных типов объектов, вы можете написать макрос для генерации такого рода макроса; И, скорее всего, вы собираетесь выделить серию этих одновременно, поэтому вы можете захотеть иметь список привязки в начале, а не только один.

Вот манжета, большая общая версия; Я не совсем уверен в имени, но он демонстрирует основную идею (, отредактированную для фиксации бесконечного цикла в оригинальной версии ):

(define-syntax with-managed-objects
  (syntax-rules ()
    ((_ ((name constructor destructor)) body ...)
     (let ((name #f))
       (dynamic-wind
         (lambda () (set! name constructor))
         (lambda () body ...)
         (lambda () destructor (set! name #f)))))
    ((_ ((name constructor destructor) rest ...)
      body ...)
     (with-managed-objects ((name constructor destructor))
       (with-managed-objects (rest ...)
         body ...)))
    ((_ () body ...)
     (begin body ...))))

, и вы будете использовать это следующим образом:

(with-managed-objects ((vbo (allocate-vertex-buffer-object 1 2 3)
                            (free-vertext-buffer-object vbo))
                       (frob (create-frobnozzle 'foo 'bar)
                             (destroy-frobnozzle frob)))
  ;; do stuff ...
  )

Вот пример Это демонстрирует его работу, в том числе выходящие и повторные ввози в объем с использованием продолжений (это довольно надуманный пример, извинения, если поток управления немного трудно следовать):

(let ((inner-continuation #f))
  (if (with-managed-objects ((foo (begin (display "entering foo\n") 1) 
                                  (display "exiting foo\n")) 
                             (bar (begin (display "entering bar\n") (+ foo 1)) 
                                  (display "exiting bar\n")))
        (display "inside\n")
        (display "foo: ") (display foo) (newline)
        (display "bar: ") (display bar) (newline)
        (call/cc (lambda (inside) (set! inner-continuation inside) #t)))
    (begin (display "* Let's try that again!\n") 
           (inner-continuation #f))
    (display "* All done\n")))

Это должно распечатать:

entering foo
entering bar
inside
foo: 1
bar: 2
exiting bar
exiting foo
* Let's try that again!
entering foo
entering bar
exiting bar
exiting foo
* All done

Call / CC - это просто аббревиатура call-с текущей продолжением ; Используйте более длинную форму, если ваша схема не имеет более короткой.

Обновление : Как вы уточнили в ваших комментариях, вы ищете способ управлять ресурсами, которые можно вернуть из определенного динамического контекста. В этом случае вам придется использовать финализатор; Финализатор - это функция, которая будет вызываться с вами объектом после того, как GC доказал, что его нельзя добраться из-за чего-либо еще. Учетные финализаторы не являются стандартными, но большинство зрелых систем схемы у них есть, иногда под разными именами. Например, в схеме PLT см. Wills и Executors .

Вы должны помнить, что на схеме, динамический контекст может быть повторно введен; Это отличается от большинства других языков, в которых вы можете выйти из динамического контекста в любой произвольной точке с использованием исключений, но вы не можете войти в систему. В моем примере выше, я продемонстрировал наивный подход к использованию динамического ветра , чтобы освободить ресурсы, когда вы оставляете динамический контекст, и повторно выделите их, если вы вводите снова. Это может быть подходящим для некоторых ресурсов, но для многих ресурсов было бы не подходящим (например, повторно открытие файла, вы теперь будете в начале файла при повторном входе в динамический контекст) и могут иметь значительный накладной расход.

Тейлор Кэмпбелл (да, есть отношение) имеет статью статьи в его BLAG (вступление в 2009-03-28), посвященную этому вопросу и представляя несколько альтернатив на основе точной семантики, которую вы хочу. Например, он обеспечивает форму Warmnind-Protext , которая не будет вызывать процедуру очистки до тех пор, пока не будет невозможно повторно ввести динамический контекст, в котором доступный ресурс.

Итак, это охватывает много разных вариантов, которые доступны. Нет точного совпадения для Raii, поскольку схема является очень другим языком и имеет очень разные ограничения. Если у вас есть более конкретный случай для использования или более подробной информации о случаях использования, который вы кратко упоминали, я могу предоставить вам еще несколько конкретных советов.

14
ответ дан 6 December 2019 в 15:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: