[Sbcl-help] Closures as callbacks

0 views
Skip to first unread message

Bruno Daniel

unread,
Oct 11, 2007, 10:10:18 AM10/11/07
to sbcl...@lists.sourceforge.net
Dear developers,

I found a very simple way to use callbacks in CFFI or in alien-lambda without
the performance and memory implications we discussed, at least for the case
that the callback doesn't call C again:

The callback given to C is just a delegator to a closure that is saved in a
the global variable *once-to-c-closure-callback*.

--------------------------------------------------------------------
(defvar *once-to-c-closure-callback* nil)

(defcallback delegating-callback :double ((x :double))
;; (callback-index :int))
(funcall *once-to-c-closure-callback* x))

(defun closure-callback (func)
(setq *once-to-c-closure-callback* func)
(callback delegating-callback))
--------------------------------------------------------------------
Here's an example with a C function executing the callback:
--------------------------------------------------------------------
// Compile with CF=testc && gcc -fPIC -c $CF.c && gcc -shared -Wl,-soname,$CF.so -o $CF.so $CF.o

#include <stdio.h>

double execute_callback (void* callback, double x) {
if (callback != NULL) {
printf("callback: %ld\n", (long)callback);
double (*callback1)();
callback1 = (double (*)())callback;
return (*callback1)(x);
} else {
printf("Callback is 0.\n");
}
}
--------------------------------------------------------------------
>From Lisp, you would use it this way:

(let ()
(define-foreign-library testc
(:unix "/home/daniel/pj/lisp/testc.so"))
(use-foreign-library testc)
(defcfun "execute_callback" :double (callback :pointer) (x :double)))

(defun test-closure-callback (a)
(execute-callback (closure-callback #'(lambda (x) (+ a x))) 10d0))

(test-closure-callback 5d0)
;; which gives 15d0.

(defun test-closure-callback2 (a)
(execute-callback (closure-callback #'(lambda (x) (* a x))) 10d0))

(test-closure-callback2 5d0)
;; which gives 50d0.


For callbacks that are allowed to call C again one would have to use a
self-expanding global array instead of *once-to-c-closure-callback* and pass
the index into the array where the closure is saved to the C function as well.

In order to let the garbage collector reclaim the closure's memory, the
following with-macro could be used instead of closure-callback. Since the
next call will overwrite *once-to-c-closure-callback* anyway, this doesn't
matter much, but in the above-mentioned array case, a similar implementation
would be necessary.

(defmacro with-closure-callback ((closure-var func) &body body)
`(unwind-protect
(let ((,closure-var (callback delegating-callback)))
(setq *once-to-c-closure-callback* ,func)
,@body)
(setq *once-to-c-closure-callback* nil)))

And here's a test for that:

(defun test-closure-callback (a)
(with-closure-callback (closure #'(lambda (x) (+ a x)))
(execute-callback closure 10d0)))

(test-closure-callback 5d0)

Best regards
Bruno Daniel

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems? Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/
_______________________________________________
Sbcl-help mailing list
Sbcl...@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sbcl-help

Bruno Daniel

unread,
Oct 11, 2007, 10:16:19 AM10/11/07
to sbcl...@lists.sourceforge.net
> For callbacks that are allowed to call C again one would have to use a
> self-expanding global array instead of *once-to-c-closure-callback* and pass
> the index into the array where the closure is saved to the C function as
> well.

In case the C code receives another callback and wants to call both of them
interchangingly, one would have to use the array solution as well.

Viele Grüße

Reply all
Reply to author
Forward
0 new messages