Если это всего лишь один раз, вы всегда можете просто написать макрос, который будет заключен в dynamic-wind
, выполняя настройку и демонтаж до и после thunks (я предполагаю, что allocate-vertex-buffer-object
и free-vertex-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-with-current-continuation
; используйте более длинную форму, если ваша Схема не имеет более короткой формы.
Обновление : Как вы пояснили в своих комментариях, вы ищете способ управления ресурсами, которые могут быть возвращены из определенного динамического контекста. В этом случае вам придется использовать финализатор; Финализатор - это функция, которая будет вызываться с вашим объектом, как только GC докажет, что он недоступен из других источников. Финализаторы не являются стандартными, но большинство зрелых систем Scheme имеют их, иногда под разными именами. Например, в схеме PLT см. Завещания и исполнители .
Следует помнить, что в Схеме динамический контекст может быть введен повторно; это отличается от большинства других языков, в которых вы можете выйти из динамического контекста в любой произвольной точке, используя исключения, но вы не можете войти снова. В моем примере выше я продемонстрировал наивный подход использования dynamic-wind
для освобождения ресурсов при выходе из динамического контекста и перераспределения их при повторном входе. Это может быть подходящим для некоторых ресурсов, но для многих ресурсов это не будет подходящим (например, при повторном открытии файла вы теперь будете в начале файла при повторном входе в динамический контекст), и может иметь значительные накладные расходы.
У Тейлора Кэмпбелла (да, есть связь) есть статья в его блоге (запись 2009-03-28), посвященная этой проблеме, и несколько вариантов, основанных на точной семантике хочу. Например, он предоставляет форму unwind-protext
, которая не будет вызывать процедуру очистки, пока больше не будет возможности повторно войти в динамический контекст, в котором доступен ресурс.
Итак, это охватывает множество различных доступных опций. Точного совпадения с RAII нет, так как Scheme - это совсем другой язык и имеет очень разные ограничения. Если у вас есть более конкретный вариант использования или более подробная информация о сценарии использования, который вы кратко упомянули, возможно, я смогу дать вам более конкретный совет.