2010-01-19 23 views
7

Có cách nào để triển khai Thu thập tài nguyên là Khởi tạo trong Đề án không?RAII trong Đề án?

Tôi biết rằng RAII không hoạt động tốt trong các ngôn ngữ GC-ed (vì chúng tôi không có ý tưởng gì về việc đối tượng bị hủy). Tuy nhiên, Đề án có những điều tốt đẹp như tiếp tục, năng động gió, và đóng cửa - là có một cách để sử dụng một số kết hợp này để thực hiện RAII?

Nếu không, làm thế nào để các nhà thiết kế thiết kế mã của họ để không sử dụng RAII?

[Một ví dụ thông thường tôi chạy vào như sau:

Tôi có một lưới 3D, tôi có một đối tượng Vertex Buffer atached với nó, khi Lưới không còn sử dụng, tôi muốn VBO giải phóng .]

Cảm ơn!

+0

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. –

+0

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

+0

Để 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

Trả lời

14

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-objectfree-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.