-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
c-object.lisp
89 lines (73 loc) · 3.08 KB
/
c-object.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
(in-package #:org.shirakumo.fraf.steamworks)
(defvar *c-object-table* (make-hash-table :test 'eql))
(defun pointer->object (pointer)
(let ((address (etypecase pointer
(cffi:foreign-pointer (cffi:pointer-address pointer))
(integer pointer))))
(gethash address *c-object-table*)))
(defun (setf pointer->object) (object pointer)
(let ((address (etypecase pointer
(cffi:foreign-pointer (cffi:pointer-address pointer))
(integer pointer))))
(if object
(setf (gethash address *c-object-table*) object)
(remhash address *c-object-table*))))
(defclass c-object ()
((handle :initarg :handle :initform NIL :accessor handle)))
(defmethod print-object ((c-object c-object) stream)
(print-unreadable-object (c-object stream :type T)
(format stream "@~d" (handle c-object))))
(defmethod handle (thing)
(etypecase thing
(integer thing)
(cffi:foreign-pointer thing)))
(defgeneric allocate-handle (c-managed-object &key &allow-other-keys))
(defgeneric free-handle-function (c-managed-object handle))
(defclass c-registered-object (c-object)
())
(defmethod initialize-instance :after ((object c-registered-object) &key)
(setf (pointer->object (handle object)) object))
(defmethod free-handle-function :around ((object c-registered-object) handle)
(let ((next (call-next-method)))
(lambda ()
(setf (pointer->object handle) NIL)
(funcall next))))
(defmethod free ((object c-registered-object))
(when (slot-boundp object 'handle)
(setf (pointer->object (handle object)) NIL)))
(defclass c-managed-object (c-object)
())
(defmethod initialize-instance ((object c-managed-object) &rest initargs &key free-on-gc)
(call-next-method)
(unless (handle object)
(setf (handle object) (apply #'allocate-handle object initargs)))
(when free-on-gc
(tg:finalize object (free-handle-function object (handle object)))))
(defmethod initialize-instance :around ((object c-managed-object) &key handle)
(if handle
(call-next-method)
(with-cleanup-on-failure (free object)
(call-next-method))))
(defmethod free ((object c-managed-object))
(let ((handle (when (slot-boundp object 'handle) (handle object))))
(when handle
(tg:cancel-finalization object)
(setf (handle object) NIL)
(funcall (free-handle-function object handle)))))
(defmacro with-c-objects (bindings &body body)
(let ((gensyms (loop for binding in bindings
collect (gensym (string (first binding)))))
(thunk (gensym "THUNK")))
`(let ,gensyms
(unwind-protect
(flet ((,thunk ()
(let ,(loop for binding in bindings
for var in gensyms
collect `(,(first binding) ,var))
,@body)))
,@(loop for binding in bindings
for var in gensyms
collect `(setf ,var ,(second binding)))
(,thunk))
,@(loop for var in gensyms
collect `(when ,var (free ,var)))))))