Si cela est juste un unique, vous pouvez toujours il suffit d'écrire une macro qui enroule autour dynamic-wind
, faire la mise en place et se démonte avant et après thunks (je suppose que allocate-vertex-buffer-object
et free-vertex-buffer-object
sont votre constructeur et Destructeurs ici):
(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)))))))
Si cela est un modèle que vous utilisez beaucoup, pour différents types d'objets, vous pouvez écrire une macro pour générer ce genre de macro; et vous allez probablement vouloir en allouer une série à la fois, alors vous voudrez peut-être avoir une liste de reliures au début, au lieu d'une seule.
Voici une version plus générale, plus générale; Je ne suis pas sûr du nom, mais il démontre l'idée de base (modifiée pour fixer la boucle infinie en version originale):
(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 ...))))
Et vous utiliseriez cela comme suit:
(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 ...
)
Voici un exemple qui montre comment cela fonctionne, y compris sortir et ré-entrer dans la portée en utilisant des continuations (c'est un exemple plutôt artificiel, des excuses si le flux de contrôle est un peu difficile à suivre):
(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")))
Cela devrait imprimer:
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
est simplement une abréviation de call-with-current-continuation
; Utilisez la forme plus longue si votre régime n'a pas le plus court.
Mise à jour: Comme vous l'avez précisé dans vos commentaires, vous recherchez un moyen de gérer les ressources qui peuvent être renvoyées hors d'un contexte dynamique particulier. Dans ce cas, vous devrez utiliser un finaliseur; un finaliseur est une fonction qui sera appelée avec votre objet une fois que le GC a prouvé qu'il ne peut pas être atteint de n'importe où ailleurs. Les finaliseurs ne sont pas standard, mais les systèmes Scheme les plus matures en ont, parfois sous des noms différents. Par exemple, dans PLT Scheme, voir Wills and Executors.
Vous devez garder à l'esprit que dans Scheme, un contexte dynamique peut être ré-entré; Cela diffère de la plupart des autres langues, dans lesquelles vous pouvez quitter un contexte dynamique à n'importe quel point arbitraire en utilisant des exceptions, mais vous ne pouvez pas entrer de nouveau.Dans mon exemple ci-dessus, j'ai démontré une approche naïve d'utiliser dynamic-wind
pour libérer des ressources lorsque vous quittez le contexte dynamique, et de les réaffecter si vous entrez de nouveau. Cela peut être approprié pour certaines ressources, mais pour de nombreuses ressources, cela ne serait pas approprié (par exemple, en rouvrant un fichier, vous serez maintenant au début du fichier lorsque vous entrerez à nouveau dans le contexte dynamique), et vous pourriez avoir frais généraux significatifs. Taylor Campbell (oui, il y a une relation) a an article in his blag (l'entrée 2009-03-28) abordant ce problème, et présentant quelques alternatives basées sur la sémantique exacte que vous voulez. Par exemple, il fournit un formulaire unwind-protext
qui n'appelle pas la procédure de nettoyage tant qu'il n'est plus possible de revenir dans le contexte dynamique dans lequel la ressource est accessible.
Donc, cela couvre beaucoup d'options différentes qui sont disponibles. Il n'y a pas de correspondance exacte avec RAII, car Scheme est une langue très différente et a des contraintes très différentes. Si vous avez un cas d'utilisation plus spécifique, ou plus de détails sur le cas d'utilisation que vous avez mentionné brièvement, je pourrais être en mesure de vous fournir des conseils plus spécifiques.
Salut, anon. Je me demande si ma réponse vous a satisfait ou si vous cherchez autre chose. –
Je pense que votre réponse est aussi bonne que possible étant donné son régime. Nous à un certain niveau, nous devons savoir quand le modèle "meurt" et abandonne c'est vbo. Cependant, en RAII + GC, je n'ai pas besoin de le savoir à l'avance, on peut dire "Modèle, je ne sais pas quand tu vas mourir, mais je sais que quand tu le feras, tu renonceras au VBO ". Nous ne pouvons pas tout à fait faire plus tard car le schéma est gc-ed; ce que j'espérais initialement ... était un type de macro-macaron intelligent qui entrelacé automatiquement un certain type de ref-counting, qui fourniraient ce type de RAII + Refcounting. – anon
Pour ajouter encore à cela, considérons la situation suivante: nous créons un modèle, nous ne savons pas quand il est supprimé, mais nous savons qu'il est rendu beaucoup; donc on lui donne une VBO; faites-le circuler beaucoup; ... et quand personne ne l'utilise, cela libère le VBO. Il n'y a pas un seul endroit dans le code où je sais "je peux maintenant libérer le modèle". – anon