FFI and callbacks -- in Scheme?!

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

FFI and callbacks -- in Scheme?!

Christoph Lange
I'm still busy trying to get MQTT (using Mosquitto MQTT) running for me in Scheme. I have the following code, and this works fine when compiling.

Note, that I have defined the `on_message` callback as external, so basically a C function in the end. This forces me to use this code compiled.

Ideally, in a next step, I'd like to put everything into a Chicken module in a way that will also be usable in the interpreter. Therefore my question:

What would I have to do to be able to use pure Scheme functions as callbacks here?

Any hint and help appreciated ...
 
;; csc -I /usr/include -L "-l mosquitto" mqtt.scm

(import (chicken memory))
(import (chicken blob))
(import bind)
(bind-rename/pattern "_" "-")
(bind* "struct mosquitto_message{
          int mid;
          char *topic;
          void *payload;
          int payloadlen;
          int qos;
          ___bool retain;
        };")
(bind* "int mosquitto_lib_init();")
(bind* "int mosquitto_lib_cleanup();")
(bind* "int mosquitto_lib_version(___out int *major,
  ___out int *minor,
  ___out int *revision);")
(bind* "struct mosquitto *mosquitto_new(const char *id,
                                        ___bool clean_session,
void *obj);")
(bind* "void mosquitto_destroy(struct mosquitto *mosq);")
(bind* "int mosquitto_connect(struct mosquitto *mosq,
      const char *host,
      int port,
      int keepalive);")
(bind* "int mosquitto_publish(struct mosquitto *mosq,
                              ___out int *mid,
      const char *topic,
      ___length(payload) int payloadlen,
      ___blob payload /* const void *payload */,
      int qos,
      ___bool retain);")
(bind* "___safe int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets);")
(bind* "___safe int mosquitto_loop_forever(struct mosquitto * mosq, int timeout, int max_packets);")
(bind* "int mosquitto_subscribe(struct mosquitto *mosq,
int *mid,
const char *sub,
int qos);")
(bind* "int mosquitto_unsubscribe(struct mosquitto *mosq,
                                  int *mid,
  const char *sub);")
;; setting callbacks
(bind* "void mosquitto_connect_callback_set(struct mosquitto *mosq,
                                            void (*on_connect)(struct mosquitto *, void *, int));")
;;(bind* "mosquitto_disconnect_callback_set")
;;(bind* "mosquitto_publish_callback_set")
;; NB: the last type of return value in the vallback is 'const struct *' according
;;     to mosquitto.h; however, it seems impossible to encode this in the
;;     Scheme callback function using combinations of 'c-pointer' and 'const'
(bind* "void mosquitto_message_callback_set(struct mosquitto *mosq,
                                            void (*on_message)(struct mosquitto *,
                     void *,
       struct mosquitto_message *));")
;;(bind* "mosquitto_subscribe_callback_set")
;;(bind* "mosquitto_unsubscribe_callback_set")
;;(bind* "mosquitto_log_callback_set")
(mosquitto-lib-init)
(display (mosquitto-lib-version))
;; on-connect callback
(define-external (on_connect ((c-pointer "struct mosquitto") mosq)
             ((c-pointer void) userdata) 
             (int returncode))
         void
(begin
   (display "connected to broker ... returncode: ")
   (display returncode)
   (newline)))
;; on-message callback
(define-external (on_message ((c-pointer "struct mosquitto") mosq)
     ((c-pointer void) userdata)
     ((c-pointer "struct mosquitto_message") msg))
          void
  (let* ((len (mosquitto-message-payloadlen msg))
         (*payload (mosquitto-message-payload msg))
                     (payload (make-blob len))
         (mid (mosquitto-message-mid msg))
         (topic (mosquitto-message-topic msg))
         (qos (mosquitto-message-qos msg))
         (retain (mosquitto-message-retain msg))
         ;; int mid; char* topic; int qos; bool retain
           )
     (if (> len 0)
       (move-memory! *payload payload len))
     (display "Wow! ... there was a message!\n")
     (display (blob->string payload)) (newline)
     (display retain)
       (newline)))
(let-values (((res major minor revision) (mosquitto-lib-version)))
  (display major) (newline)
  (display minor) (newline)
  (display revision) (newline))
;; #f is equivalent to C's NULL
(define mqttc (mosquitto-new #f #t #f))
(display "mqtt client address\n")
(display mqttc) (newline)
(mosquitto-connect-callback-set mqttc #$on_connect)
(mosquitto-message-callback-set mqttc #$on_message)
(display "connect to broker\n")
(display (mosquitto-connect mqttc "localhost" 1883 60))
(newline)
(mosquitto-subscribe mqttc #f "greetings/#" 0)
(define payload (string->blob "gluck, gluck!"))
(mosquitto-publish mqttc "chicken/call" payload 0 #f)
(let loop ()
  (mosquitto-loop mqttc -1 1)
  (loop))
;;(mosquitto-loop-forever mqttc -1 1)
;; (mosquitto-loop mqttc -1 1)
(mosquitto-destroy mqttc)
(mosquitto-lib-cleanup)

 
--
Christoph Lange
Lotsarnas Väg 8
430 83 Vrångö

_______________________________________________
Chicken-users mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/chicken-users
Reply | Threaded
Open this post in threaded view
|

Re: FFI and callbacks -- in Scheme?!

Peter Bex
On Mon, May 13, 2019 at 11:07:07AM +0200, Christoph Lange wrote:

> I'm still busy trying to get MQTT (using Mosquitto MQTT) running for me in
> Scheme. I have the following code, and this works fine when compiling.
>
> Note, that I have defined the `on_message` callback as external, so
> basically a C function in the end. This forces me to use this code compiled.
>
> Ideally, in a next step, I'd like to put everything into a Chicken module
> in a way that will also be usable in the interpreter. Therefore my question:
>
> *What would I have to do to be able to use pure Scheme functions as
> callbacks here?*
Hi Christoph,

A relatively straightforward way to do that is to use the void * user
data that is passed to mosquitto_new to store a vector or alist or
something ctonainng callbacks.  It would have to be a pointer to a GC
root object that stores this vector, because the vector itself will be
moved around by the GC of course.

See http://wiki.call-cc.org/man/5/Embedding#chicken_new_gc_root

Then the callback can simply be a foreign safe lambda or a define-external
procedure that looks up the real callback in this vector.  You could also
store it inside the mosquitto connection record type object, which would
be cleaner because you can more easily change the callback for a given
connection.  You could either store the aforementioned vector in the
object, or store the actual handlers in it and stuff the mosquitto object
inside a GC root.

The GC root should be freed whenever the mosquitto object is freed.

Cheers,
Peter

_______________________________________________
Chicken-users mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/chicken-users

signature.asc (499 bytes) Download Attachment