From: Bruno D. <bru...@gm...> - 2007-09-18 11:52:35
|
Dear developers, I see that the function alien-lambda used by CFFI in the file http://common-lisp.net/project/cffi/darcs/cffi/src/cffi-sbcl.lisp is undocumented in both the CMUCL and the SBCL manuals. Is it safe to use this function with closures, say, in the following way? (defmacro alien-lambda-with-cffi-types (rettype arg-names-and-types &body body) (let ((arg-names (mapcar #'first arg-names-and-types)) (arg-types (mapcar #'second arg-names-and-types))) `(alien-sap (sb-alien::alien-lambda ,(cffi::convert-foreign-type rettype) ,(mapcar (lambda (sym type) (list sym (cffi::convert-foreign-type type))) arg-names arg-types) ,@body))) (let ((ptr nil)) (let ((a 3)) (setq ptr (cffi-like-alien-lambda :int ((b :int)) (+ a b)))) (some-c-function ptr)) Best regards Bruno Daniel |
From: Bruno D. <bru...@gm...> - 2007-09-18 18:59:18
|
Dear developers, I just tried it out and it seems to work. Here's the code with some corrections (CFFI has to be installed and loaded): library-test.cc: ------------------------------------------------------------ // compile with // g++ -fPIC -c library-test.cc && g++ -shared -Wl,-soname,library-test.so -o library-test.so library-test.o #include <iostream> using namespace std; extern "C" { int caller_func(int a, int (*f)(int)) { return (*f)(a) + 5; } } ------------------------------------------------------------ in Lisp: ------------------------------------------------------------ (let () (define-foreign-library library-test (:unix "/home/daniel/pj/lisp/library-test.so")) (use-foreign-library library-test)) (defcfun "caller_func" :int (a :int) (f :pointer)) (defmacro alien-lambda-with-cffi-types (rettype arg-names-and-types &body body) (let ((arg-names (mapcar #'first arg-names-and-types)) (arg-types (mapcar #'second arg-names-and-types))) `(sb-alien::alien-sap (sb-alien::alien-lambda ,(cffi-sys::convert-foreign-type rettype) ,(mapcar (lambda (sym type) (list sym (cffi-sys::convert-foreign-type type))) arg-names arg-types) ,@body)))) (let ((ptr nil)) (let ((b 3)) (setq ptr (alien-lambda-with-cffi-types :int ((a :int)) (+ a b)))) (caller-func 200 ptr)) ------------------------------------------------------------ The result is 208 as it should be. It doesn't crash with intermediate garbage collections: (let ((ptr nil)) (dotimes (bb 100) (let ((b bb)) (setq ptr (alien-lambda-with-cffi-types :int ((a :int)) (+ a b)))) (sb-ext:gc) (sb-ext:gc :full t) (print (caller-func 200 ptr)))) So the only thing I have to know is whether this as yet undocumented feature will continue to work in future versions of SBCL. Thank you very much for your answer. I need this feature for efficient subclassing of C++ classes in Lisp. Many libraries such as those used for GUIs require the programmer to subclass their classes. Best regards Bruno Daniel |
From: Bruno D. <bru...@gm...> - 2007-09-18 21:07:06
|
> Dear developers, > I just tried it out and it seems to work. Here's the code with some > corrections (CFFI has to be installed and loaded): I thought it over and now I understand why this function is undocumented: Using closures as callbacks will result in a serious memory leak: Since the C code might have stored the function pointer anywhere, Lisp will never be able to garbage collect the closure. I must find another solution for my application. Best regards Bruno Daniel |
From: Nikodemus S. <nik...@ra...> - 2007-09-19 11:17:41
|
On 9/18/07, Bruno Daniel <bru...@gm...> wrote: > I thought it over and now I understand why this function is undocumented: > Using closures as callbacks will result in a serious memory leak: Since the > C code might have stored the function pointer anywhere, Lisp will never be > able to garbage collect the closure. Quite so. Constructing the callback for a closure is also horrendously expensive compared to non-closures: (defun foo (x) (alien-lambda () ... x ...)) (defun bar () (alien-lambda () ...)) FOO needs to round-trip through the compiler every time, whereas BAR does that only on the first call. ...but the reason for this being undocumented is just a feature of me having run out of steam & time while working on this. > I must find another solution for my application. Most C libraries using callbacks have a separate "data" argument to the callback you can use. Cheers, -- Nikodemus |
From: Bruno D. <bru...@gm...> - 2007-09-19 12:06:15
|
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 |
From: Tobias C. R. <tc...@fr...> - 2007-09-19 13:42:58
|
Bruno Daniel <bru...@gm...> writes: > (defmacro with-dslots (vars (obj class) &body body) > " ... " > (with-gensyms (obj-evaluated) > (labels > ((slot-ref (obj1 slot class) > `(,(intern (concatenate 'string (string class) "-" (string slot))) > ,obj1)) > ...)))) This doesn't work for structs that are defined with :conc-name. Better use something like SWANK::WITH-STRUCT where you have to explicitely pass the conc-name. > ;; 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)))) (with-struct (test-struct- a b) ..) For (defstruct (matching :conc-name matching.) prefix infix suffix) (with-struct (matching. prefix infix suffix) ...) You may find a syntax like (with-struct-slots matching. (prefix infix suffix) ...) more pleasing. (I'm still undecided.) -T. |
From: Bruno D. <bru...@gm...> - 2007-09-19 15:00:34
|
Dear Tobias, thanks for your advice! You convinced me and I changed it everywhere in my code. > This doesn't work for structs that are defined with :conc-name. Better > use something like SWANK::WITH-STRUCT where you have to explicitely pass > the conc-name. > > (with-struct (test-struct- a b) > ..) > > For (defstruct (matching :conc-name matching.) > prefix infix suffix) > > (with-struct (matching. prefix infix suffix) > ...) > > You may find a syntax like > > (with-struct-slots matching. (prefix infix suffix) > ...) > > more pleasing. (I'm still undecided.) Best regards Bruno Daniel |