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.