Dear Nikodemus,
thank you very much for your clarification.
> Most C libraries using callbacks have a separate "data" argument
> to the callback you can use.
Unfortunately this isn't an option for my application. What I want to do is
to improve Swig's support for Common Lisp (using CFFI). I already fixed the
overloaded functions and methods problem. Now I'm working on enabling Lisp to
subclass C++ classes in a way that makes the resulting objects usable in
C++ libraries just as if they had been suclassed in C++ itself, and the
solution shouldn't be too inefficient.
My solution would be the following: Let Swig add a C++ subclass that receives
some Lisp callback pointers for the methods to override in the constructor.
The subclass will have overriding methods that just delegate to the callbacks
in case the pointers are non-zero and to the upper-class methods otherwise.
This might be fine, but what about additional slots in the subclass? As the
overriding methods, these are to be defined in Lisp, but how do I get the
overriding methods to reach them when they are called? Since the methods must
keep the signatures of the overridden methods, I can't add a data argument, so
only the self-argument ("this") remains. But this is a C++ object and it can't
keep references to Lisp objects since these may be moved around by the garbage
collector. I don't want to switch off the garbage collector, so I turned to
closures which also don't work out as we have seen.
In the meantime I had another idea that is not too inefficient: Instead of a
reference I'll keep an index in the C++ object. It's an index into a
self-expanding Lisp array that references all the structs for additional slots
of the C++-class-subclassing objects. The C++-destructor will remove the
pointer to the struct from the array and make the index value reusable for
later objects. Then Lisp's garbage collector can reclaim the formerly
referenced struct.
Some tricks are necessary in order to avoid too inefficient searching for free
indices upon object creation, but I think I can deal with that. Slot access
for this particular kind of objects will be slowed down a bit by two function
calls and an array access, but this will happen only once per method call
since I intend to use the following convenience macro. It's analogous to
with-slots:
(defmacro with-dslots (vars (obj class) &body body)
"Like WITH-SLOTS but for things of STRUCTURE-CLASS only. Allows efficent
uses of optimized DEFSTRUCT slot accessors outside of method bodies;
inspired by
http://www.koders.com/lisp/fidC34AFB44CE058B8B2706165FF96C9DD4F7434C1C.aspx"
(with-gensyms (obj-evaluated)
(labels
((slot-ref (obj1 slot class)
`(,(intern (concatenate 'string (string class) "-" (string slot)))
,obj1))
(varform (v)
(etypecase v
(symbol
`(,v ,(slot-ref obj-evaluated v class)))
(list
`(,(car v) ,(slot-ref obj-evaluated (cadr v) class))))))
`(let ((,obj-evaluated ,obj))
(symbol-macrolet ,(mapcar #'varform vars)
;;(declare (type ,class ,obj))
,@body)))))
;; Example:
(defstruct test-struct
(a 0 :type mword)
(b 0 :type mword))
(let ((teststr (make-test-struct :a 3 :b 4)))
(with-dslots (a b) (teststr test-struct)
(print (list a b))))
;; This prints (3 4) .
Best regards
Bruno Daniel
|