[cffi-devel] First usages of CFFI
Hi, so far i have compiled some programs, that use CFFI, but now id like to create my own. As usual i counter my limits of knowledge and hope to find some answers here. I'm using libevent library (later doing same with libev), that has a simple event based server application, which i want to use from ccl / sbcl. I'll put two c functions first, and then some header information and finally some lisp code i have created. Im particularry interested getting some comments, how the work should be done. Yet there re two parts of the code, where i have stuck and they are: void evhttp_set_gencb(struct evhttp *http, void (*cb)(struct evhttp_request *, void *), void *cbarg) {} (defcfun ("evhttp_set_gencb" %evhttp-set-gencb) :void (http evhttp) ???) Actually same part occurs on evbuffer: void (*cb)(struct evbuffer *, size_t, size_t, void *); Regards, -Marko -------------------------------------------------------------------------------------------------------------------------------------- // ACTUAL APPLICATION void generic_handler(struct evhttp_request *req, void *arg) { struct evbuffer *buf; buf = evbuffer_new(); if (buf == NULL) err(1, "failed to create response buffer"); evbuffer_add_printf(buf, "Requested: %sn", evhttp_request_uri(req)); evhttp_send_reply(req, HTTP_OK, "OK", buf); } int main(int argc, char **argv) { struct evhttp *httpd; event_init(); httpd = evhttp_start("0.0.0.0", 5007); /* Set a callback for requests to "/specific". */ /* evhttp_set_cb(httpd, "/specific", another_handler, NULL); */ /* Set a callback for all other requests. */ evhttp_set_gencb(httpd, generic_handler, NULL); event_dispatch(); /* Not reached in this code as it is now. */ evhttp_free(httpd); return 0; } // SOME C CODE TO DEFINE #define HTTP_OK 200 struct evhttp; struct evhttp_request; struct evbuffer { u_char *buffer; u_char *orig_buffer; size_t misalign; size_t totallen; size_t off; void (*cb)(struct evbuffer *, size_t, size_t, void *); void *cbarg; }; struct event_base * event_init(void) {} struct evhttp * evhttp_start(const char *address, u_short port) {} void evhttp_free(struct evhttp* http) {} void evhttp_set_gencb(struct evhttp *http, void (*cb)(struct evhttp_request *, void *), void *cbarg) {} // LISP PART (cffi:define-foreign-library libevent (:darwin "libevent.dylib") (t (:default "libevent"))) (cffi:use-foreign-library libevent) (defconstant HTTP_OK 200) (defconstant HTTP_NOCONTENT 204) (defconstant HTTP_MOVEPERM 301) (defconstant HTTP_MOVETEMP 302) (defconstant HTTP_NOTMODIFIED 304) (defconstant HTTP_BADREQUEST 400) (defconstant HTTP_NOTFOUND 404) (defconstant HTTP_SERVUNAVAIL 503) (defctype size-t :unsigned-int) (defcstruct evbuffer (*buffer :unsigned-char) (*orig_buffer :unsigned-char) (misalign size-t) (totallen size-t) (off size-t) (*cb :pointer) ; struct? (*cbarg :pointer)) (defcstruct event-base) (defcstruct evhttp-request) (defcstruct evhttp) (defcfun ("evbuffer_new" %evbuffer-new) :pointer) (defcfun ("evbuffer_add_printf" %evbuffer-add-printf) :void (buf evbuffer) (msg :string) (uri :string)) (defcfun ("evhttp_request_uri" %evhttp-request-uri) :string (req evhttp-request)) (defcfun ("evhttp_send_reply" %evhttp-send-reply) :void (req evhttp-request) (flag :int) (msg :string) (buf evbuffer)) ;;; unwind-protect to create evbuffer-new? (defun generic-handler (req &optional (arg nil)) (let ((buf (%evbuffer-new))) (if (null buf) (error "Failed to create response buffer") (progn (%evbuffer-add-printf buf "Requested: %sn" (%evhttp-request-uri req)) (%evhttp-send-reply req HTTP_OK "OK" buf))))) (defcfun ("event_init" %event-init) :event-base) (defcfun ("evhttp_start" %evhttp-start) :evhttp (address :char) (port :unsigned-short)) (defcfun ("evhttp_set_gencb" %evhttp-set-gencb) :void (http evhttp) ???) (defcfun ("event_dispatch" %event-dispatch) :int) (defcfun ("evhttp_free" %evhttp-free) :void (http evhttp)) ;;; MAIN SERVER START UP FUNCTION ;(server "0.0.0.0" 5009) (defun server (host port) (%event-init) (let ((httpd %evhttp-start host port) (%evhttp-set-gencb httpd #'generic-handler nil) (%event-dispatch) (%evhttp-free httpd))))
Marko Tapio Manninen <mmstud@gmail.com> writes:
void (*cb)(struct evbuffer *, size_t, size_t, void *);
This is explained in tutorial format in §3.9 `Calling Lisp from C' in the official manual, and reference documentation is in §11 `Callbacks'. The official manual may be found on the website, but I strongly recommend that you use the one you should have received with CFFI.
(defconstant HTTP_OK 200) (defconstant HTTP_NOCONTENT 204) (defconstant HTTP_MOVEPERM 301) ...
`defcenum' would be good for this.
(defcstruct evbuffer (*buffer :unsigned-char) (*orig_buffer :unsigned-char) (misalign size-t) (totallen size-t) (off size-t) (*cb :pointer) ; struct? (*cbarg :pointer))
Interestingly, this arg is called "closure" in libraries that know what it hacks around. You may wish to call it that to make its nature clear to Lisp-readers.
(defcfun ("evbuffer_add_printf" %evbuffer-add-printf) :void (buf evbuffer) (msg :string) (uri :string))
I believe this was mentioned on #lisp, but those should not be indented a full tab-stop. The communities of Lisp indenters are not nearly as divergent as with some languages, and I don't think you'll find one that would encourage that.
(defcfun ("evhttp_send_reply" %evhttp-send-reply) :void (req evhttp-request) (flag :int) (msg :string) (buf evbuffer))
;;; unwind-protect to create evbuffer-new?
Yes, but it's for freeing, not allocating. See §3.8 `Memory management' for some tips.
(defun generic-handler (req &optional (arg nil)) (let ((buf (%evbuffer-new))) (if (null buf) (error "Failed to create response buffer")
Unfortunately, you can't signal ERROR here after you convert it to a real callback; see the aforementioned §3.9 for why. For generalized conditions, think of this as a game of volleyball, where the call to whatever C function calls GENERIC-HANDLER is the net. -- But you know how reluctant paranormal phenomena are to reveal themselves when skeptics are present. --Robert Sheaffer, SkI 9/2003
participants (2)
-
Marko Tapio Manninen
-
Stephen Compall