Dear Gambit enthusiasts,
I think I figured out how to use dlopen to run functions from Gambit that live in dynamically loaded libraries (plugins); my best to date is below.
But I'm not quite happy with it--because it's kind of elaborate. That is, I couldn't figure out how to avoid some of the boiler plate below. Perhaps there is a shorter way?
So my question for Gambit enthusiasts is: Is there a cleaner way to do this--to run functions from plugin libraries? i.e. without having to pre-declare call_plugin_print8 and call_print8. What if you wanted to make such a call after inspecting a new library at runtime?
Many thanks! Gambit rocks! Thanks again for such a great system.
- Jason
;;;;;;;;;;;;;;; file: libex.cpp ;;;;;;;;;;;; ; ; a simple library of one function for ; testing/development of plugin loading ;
#include <stdio.h>
extern "C" int print8() { printf("\n In print8() : returning 8\n"); return 8; }
;;;;;;;;;;;;;;; file: libex.h ;;;;;;;;;;;;
#ifndef LIBEX_H #define LIBEX_H int print8(); #endif
;;;;;;;;;;;;;;; file: dlopen.scm ;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; dlopen.scm : plugins in Gambit ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; compile and link with: ; ; gsc -link dlopen.scm ; gcc -I/usr/local/Gambit-C/include -L/usr/local/Gambit-C/lib dlopen.c dlopen_.c -lgambc -lm -lutil -ldl -o dlopen
(c-declare #<<c-declare-end
#include <stdio.h> #include <stdlib.h> #include <dlfcn.h> #include "libex.h"
/* simple wrapper to do runtime plugin function calls (call into .so library) */ int call_plugin_print8(void* handle) { char *error; int (*my_func)(); // my_func = (int (*)()) dlsym(handle,"print8"); *(void **) (&my_func) = dlsym(handle, "print8");
if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); exit(EXIT_FAILURE); }
return (*my_func)(); }
c-declare-end )
; from dlfcn.h ; see man dlopen ; ; void * dlopen(const char *filename, int flag); ; char * dlerror(void); ; void * dlsym(void *handle, const char *symbol); ; int dlclose(void *handle); ; ; translated into Gambit-C FFI: ; (define dlopen (c-lambda (nonnull-char-string int32) (pointer void) "dlopen")) (define dlerror (c-lambda () char-string "dlerror")) (define dlsym (c-lambda ((pointer void) nonnull-char-string) (pointer void) "dlsym")) (define dlclose (c-lambda ((pointer void)) int32 "dlclose"))
;; hmm is there a defconstant in scheme that we could use for these C header constants? ;; for now generate a function of the same name to get us the constant int value we need. (define RTLD_LAZY (c-lambda () int32 "___result = RTLD_LAZY;"))
; declare the type of the foreign function (define call_print8 (c-lambda ((pointer void)) int32 "call_plugin_print8"))
(let* ((h (dlopen "/home/jaten/dj/gamb/libex.so" (RTLD_LAZY))) (res (dlsym h "print8")))
(if (null? res) (error "could not locate symbol 'print8' in the plugin library!"))
(pretty-print (list "calling print8() gives: " (call_print8 h))) (pretty-print res) (pretty-print h) (pretty-print (list "The value of the constant RTLD_LAZY is: " (RTLD_LAZY))))
;; output:
;;; $ ./dlopen ; ; In print8() : returning 8 ;8 ;#<void* #2 0x7fdd20c1868c> ;#<void* #3 0x22cd7b0> ;("The value of the constant RTLD_LAZY is: " 1)
;;;;;;;;;;;;;;; file: Makefile ;;;;;;;;;;;;
all: gsc -link dlopen.scm gcc -I/usr/local/Gambit-C/include -L/usr/local/Gambit-C/lib dlopen.c dlopen_.c -lgambc -lm -lutil -ldl -o dlopen g++ -c libex.cpp -fpic -o libex.o g++ -shared libex.o -o libex.so @echo "testing ./dlopen" ./dlopen
clean: rm -f dlopen dlopen_.c dlopen.c dlopen.o1 dlopen.o2 *~ *.o *.so