Nếu đây là chỉ là một một lần, bạn có thể luôn luôn chỉ cần viết một macro mà kết thúc tốt đẹp xung quanh dynamic-wind
, làm việc thiết lập và teardown trong trước và sau khi thunks (Tôi giả định rằng allocate-vertex-buffer-object
và free-vertex-buffer-object
là constructor và destructor của bạn ở đây):
(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)))))))
Nếu đây là mẫu mà bạn sử dụng rất nhiều, đối với các loại đối tượng khác nhau, bạn có thể viết macro để tạo loại macro này; và có khả năng bạn sẽ muốn phân bổ một loạt những điều này tại một thời điểm, vì vậy bạn có thể muốn có một danh sách các ràng buộc ngay từ đầu, thay vì chỉ một danh sách duy nhất.
Đây là phiên bản tổng quát, phiên bản chung hơn; Tôi không thực sự chắc chắn về tên, nhưng nó thể hiện ý tưởng cơ bản (chỉnh sửa để sửa chữa vòng lặp vô hạn trong phiên bản gốc):
(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 ...))))
Và bạn sẽ sử dụng điều này như sau:
(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 ...
)
Dưới đây là ví dụ minh họa nó hoạt động, bao gồm thoát và nhập lại phạm vi bằng cách tiếp tục (đây là ví dụ khá giả tạo, xin lỗi nếu luồng điều khiển hơi khó thực hiện):
(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")))
này nên in:
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
chỉ đơn giản là viết tắt của call-with-current-continuation
; sử dụng biểu mẫu dài hơn nếu Lược đồ của bạn không có lược đồ ngắn hơn.
Cập nhật: Khi bạn làm rõ trong nhận xét của mình, bạn đang tìm cách quản lý tài nguyên có thể được trả về từ ngữ cảnh động cụ thể. Trong trường hợp này, bạn sẽ phải sử dụng một finalizer; một finalizer là một hàm sẽ được gọi với đối tượng của bạn khi GC đã chứng minh rằng nó không thể đạt được từ bất cứ nơi nào khác. Finalizers không phải là tiêu chuẩn, nhưng hầu hết các hệ thống Scheme trưởng thành có chúng, đôi khi dưới các tên khác nhau. Ví dụ, trong PLT Scheme, xem Wills and Executors.
Bạn nên nhớ rằng trong Đề án, một ngữ cảnh động có thể được nhập lại; điều này khác với hầu hết các ngôn ngữ khác, trong đó bạn có thể thoát khỏi ngữ cảnh động tại bất kỳ điểm tùy ý nào bằng cách sử dụng ngoại lệ, nhưng bạn không thể nhập lại.Trong ví dụ trên, tôi đã chứng minh một cách tiếp cận ngây thơ khi sử dụng dynamic-wind
để deallocate tài nguyên khi bạn rời khỏi ngữ cảnh động và phân bổ lại chúng nếu bạn nhập lại. Điều này có thể thích hợp đối với một số tài nguyên, nhưng đối với nhiều tài nguyên, nó sẽ không thích hợp (ví dụ, mở lại một tệp, bây giờ bạn sẽ ở đầu tệp khi bạn nhập lại ngữ cảnh động) và có thể có chi phí đáng kể.
Taylor Campbell (có, có quan hệ) có an article in his blag (mục nhập 2009-03-28) giải quyết vấn đề này và trình bày một vài lựa chọn thay thế dựa trên ngữ nghĩa chính xác mà bạn muốn. Ví dụ, anh ta cung cấp một biểu mẫu unwind-protext
sẽ không gọi thủ tục dọn dẹp cho đến khi không thể nhập lại ngữ cảnh động mà tài nguyên có thể truy cập được.
Vì vậy, bao gồm rất nhiều tùy chọn khác nhau có sẵn. Không có đối sánh chính xác với RAII, vì Scheme là một ngôn ngữ rất khác và có những ràng buộc rất khác nhau. Nếu bạn có trường hợp sử dụng cụ thể hơn hoặc nhiều thông tin chi tiết hơn về trường hợp sử dụng mà bạn đã đề cập một thời gian ngắn, tôi có thể cung cấp cho bạn một số lời khuyên cụ thể hơn.
Xin chào, anon. Tôi tự hỏi nếu câu trả lời của tôi đã làm bạn hài lòng, hoặc nếu bạn đang tìm kiếm một cái gì đó khác. –
Tôi nghĩ rằng phản ứng của bạn là tốt như nó được đưa ra chương trình của nó. Chúng tôi ở một mức độ nào đó, chúng tôi phải biết khi nào mô hình "chết" và từ bỏ nó là vbo. Tuy nhiên, trong RAII + GC, tôi không cần phải biết điều này trước, chúng tôi có thể nói "Mô hình, tôi không biết khi nào bạn sẽ chết, nhưng tôi biết rằng khi bạn làm, bạn sẽ từ bỏ VBO ". Chúng tôi không thể thực hiện sau vì chương trình là gc-ed; những gì tôi đã được ban đầu hy vọng cho ... là một số loại mack macro thông minh tự động xen kẽ một số loại ref-đếm, mà họ sẽ cung cấp loại RAII + Refcounting. – anon
Để tiếp tục thêm vào điều này, hãy xem xét tình huống sau: chúng tôi tạo một Mô hình, chúng tôi không biết khi nào nó bị xóa, nhưng chúng tôi biết nó được hiển thị rất nhiều; vì vậy chúng tôi cung cấp cho nó một VBO; vượt qua nó xung quanh rất nhiều; ... và khi không ai sử dụng nó, nó sẽ giải phóng VBO. Không có một nơi duy nhất trong mã mà tôi biết "Bây giờ tôi có thể giải phóng mô hình." – anon