Peter Seibel peter@gigamonkeys.com writes:
Other than keeping my own integer->object mapping and passing the integer to C and translating it back to the object when I get it back, is there some easier way to do this in CFFI?
AFAIK, that is the best way to do it. You could also serialize Lisp objects into a foreign array, though the situations where that would be a better approach seem limited and you loose object identity.
SBCL and some other Lisps will let you get pointers to vectors of some types. With Allegro you apparently can get pointers to arbitrary Lisp objects with EXCL:LISPVAL-TO-ADDRESS and then get it back with SYS:MEMREF (though you have to get the offset right, and AFAICT handle immediate values yourself); it seems easier to do your own mapping instead. You have to worry about the GC moving objects around too.
Hmm, since you're asking whether this is doable in *CFFI*, the answer is no then. :-) You can, however, make good use of CFFI's type system!
(use-package :cffi)
(define-foreign-type lisp-object-type () ((weakp :initarg :weakp)) (:actual-type :unsigned-int))
(define-parse-method lisp-object (&key weak-mapping) (make-instance 'lisp-object-type :weakp weak-mapping))
(defvar *regular-hashtable* (make-hash-table))
(defvar *weak-hashtable* (trivial-garbage:make-weak-hash-table :weakness :value))
(defvar *regular-counter* 0) (defvar *weak-counter* 0)
(defun increment-counter (value) (mod (1+ value) (expt 2 (* 8 (foreign-type-size :unsigned-int)))))
(define-modify-macro incf-counter () increment-counter)
(defmethod translate-to-foreign (value (type lisp-object-type)) (with-slots (weakp) type (let ((id (if weakp (incf-counter *weak-counter*) (incf-counter *regular-counter*))) (ht (if weakp *weak-hashtable* *regular-hashtable*))) (setf (gethash id ht) value) id)))
(defmethod translate-from-foreign (int (type lisp-object-type)) (with-slots (weakp) type ;; If isn't a weak hashtable, we should probably remhash. (gethash int (if weakp *weak-hashtable* *regular-hashtable*))))
;;;; Silly example
CL-USER> (defctype weak-mapping (lisp-object :weak-mapping t)) WEAK-MAPPING
CL-USER> (foreign-funcall "abs" weak-mapping (lambda (x) x) weak-mapping) #<FUNCTION (LAMBDA (X)) {11AB46F5}> T
This type has obvious shortcomings. In order to be generally useful it should probably take different arguments such as an hashtable so that different CFFI users don't share the same hashtable, have a smarter (possibly user-provided) counter mechanism, etc... I hope it's a useful example nevertheless.