[cffi-devel] Cannot call function with char** arg
Hi all, I'm trying to develop a CFFI binding for librrd, but I'm stuck right at the begining. The function signature I'm trying to call is int rrd_create(int, char **); My test C code look's like this #include <stdio.h> #include <stdlib.h> #include <string.h> #include <rrd.h> int main(int argc, char** argv) { char* c[6]; c[0] = "create"; c[1] = "test.rrd"; c[2] = "--step=300"; c[3] = "DS:a:GAUGE:600:U:U"; c[4] = "DS:b:GAUGE:600:U:U"; c[5] = "RRA:AVERAGE:0.5:1:300"; if (rrd_create(6, c) < 0) (void) fprintf(stderr, "librrd call failed: %s\n", rrd_get_error()); return (0); } Using SBCL 0.9.9 and CFFI 0.9.0 on x86 I came up with the following LISP code (after doing the necessary housekeeping to load librrd) (defvar *cmd* (list "test.rrd" "--step=300" "DS:a:GAUGE:600:U:U" "DS:b:GAUGE:600:U:U" "RRA:AVERAGE:0.5:1:300")) (cffi:defcfun "rrd_create" :int (argcount :int) (args :pointer)) (cffi:with-foreign-pointer (ptr (+ (reduce #'+ (map 'vector #'length *cmd*)) (length *cmd*) 6)) (cffi:lisp-string-to-foreign "dummy" ptr 6) ; librrd needs a dummy fst arg (let ((curptr (cffi:inc-pointer ptr 6))) (dolist (param *cmd*) (let ((size (1+ (length param)))) (cffi:lisp-string-to-foreign param curptr size) (setf curptr (cffi:inc-pointer curptr size))))) (rrd-create 6 ptr)) But I get this error memory fault [Condition of type SB-KERNEL::MEMORY-FAULT-ERROR] Restarts: 0: [ABORT-REQUEST] Abort handling SLIME request. 1: [ABORT] Exit debugger, returning to top level. Backtrace: 0: (SB-KERNEL::MEMORY-FAULT-ERROR) 1: ("foreign function: call_into_lisp") 2: ("foreign function: post_signal_tramp") 3: ("foreign function: getsubopt") 4: ("foreign function: getopt_long") 5: ("foreign function: rrd_create") 6: (RRD-CREATE 6 #.(SB-SYS:INT-SAP #X08269100)) 7: (NIL) 8: (SB-INT:EVAL-IN-LEXENV (CFFI-SYS:WITH-FOREIGN-POINTER (PTR (+ # # 6)) (CFFI:LISP-STRING-TO-FOREIGN "dummy" PTR 6) (LET (#) (DOLIST # #)) (RRD-CREATE 6 PTR)) #<NULL-LEXENV>) --more-- I tried various other ways but did not suceed and now I'm stuck. TIA Nikolai -- Ich verwalte sie. Ich zähle sie und zähle sie wieder. Das ist nicht leicht. Aber ich bin ein ernsthafter Mann. \\ ---> Antoine de Saint-Exupery, "Der kleine Prinz"
Nikolai Nespor <nikolai.nespor@utanet.at> writes:
I'm trying to develop a CFFI binding for librrd, but I'm stuck right at the begining.
The function signature I'm trying to call is
int rrd_create(int, char **);
This is probably a tricky enough case that CFFI should provide some utility functions for creating these argc/argv style string vectors. Does something like the following work for you? It'd be nice to massage this into something worth including in src/strings.lisp... ;;; Allocate a pointer containing COUNT pointers to the strings in the ;;; Lisp SEQUENCE. (defun foreign-string-vector (sequence &optional (count (length sequence))) (let ((ptr (foreign-alloc :pointer :count count)) (i 0)) (map nil (lambda (s) (setf (mem-aref ptr :pointer i) (foreign-string-alloc s)) (incf i)) sequence) ptr)) ;;; Free a string vector at PTR containing COUNT elements. (defun foreign-string-vector-free (ptr count) (loop for i below count do (foreign-string-free (mem-aref ptr :pointer i))) (foreign-free ptr)) ;;; Bind ARGC and ARGV to the count and pointer of a C-style string ;;; vector, taking strings from SEQUENCE. The vector is automatically ;;; freed upon exit of BODY. (defmacro with-foreign-string-vector ((argc argv sequence) &body body) (cffi-utils:once-only (sequence) `(let* ((,argc (length ,sequence)) (,argv (foreign-string-vector ,sequence ,argc))) (unwind-protect (progn ,@body) (foreign-string-vector-free ,argv ,argc))))) ;;; Simple test of the string vector macro: (with-foreign-string-vector (argc argv '("hello" "world")) (loop for i below argc do (format t "~&;; ~D: ~A~%" i (mem-aref argv :string i)))) Thanks, James
In reply to James Bielman: I just managed to do a quick test, I'll have some more time tomorrow.
Does something like the following work for you? It'd be nice to massage this into something worth including in src/strings.lisp...
[snip]
;;; Bind ARGC and ARGV to the count and pointer of a C-style string ;;; vector, taking strings from SEQUENCE. The vector is automatically ;;; freed upon exit of BODY. (defmacro with-foreign-string-vector ((argc argv sequence) &body body) (cffi-utils:once-only (sequence) `(let* ((,argc (length ,sequence)) (,argv (foreign-string-vector ,sequence ,argc))) (unwind-protect (progn ,@body) (foreign-string-vector-free ,argv ,argc)))))
If I call it like this (which I hope is right, since I'm just learing lisp) rrd> (with-foreign-string-vector (argc argv '("create" "test.rrd" "--step=300" "DS:a:GAUGE:600:U:U" "DS:b:GAUGE:600:U:U" "RRA:AVERAGE:0.5:1:300")) (rrd-create argc argv)) I get this error: arithmetic error FLOATING-POINT-INVALID-OPERATION signalled [Condition of type FLOATING-POINT-INVALID-OPERATION] Restarts: 0: [ABORT-REQUEST] Abort handling SLIME request. 1: [ABORT] Exit debugger, returning to top level. Backtrace: 0: (SB-VM:SIGFPE-HANDLER #<unavailable argument> #.(SB-SYS:INT-SAP #X283822C0) #.(SB-SYS:INT-SAP #X28382000)) 1: (SB-SYS:INVOKE-INTERRUPTION #<CLOSURE (LAMBDA NIL) {49C44A75}>) 2: ("foreign function: call_into_lisp") 3: ("foreign function: funcall3") 4: ("foreign function: interrupt_handle_now") 5: ("foreign function: interrupt_handle_now_handler") 6: ("bogus stack frame") 7: ("foreign function: rrd_create_r") 8: ("foreign function: rrd_create") 9: (RRD-CREATE 6 #.(SB-SYS:INT-SAP #X082661C0)) 10: (NIL) 11: (SB-INT:EVAL-IN-LEXENV (WITH-FOREIGN-STRING-VECTOR (ARGC ARGV (QUOTE #)) (RRD-CREATE ARGC ARGV)) #<NULL-LEXENV>) --more-- which is further than I ever got before. It would usually bail out before calling into rrd_create_r - the function where the real work is done. I'm using SBCL 0.9.9 with SLIME CVS from a few weeks ago on x86 FreeBSD. CFFI is 0.9.0. I'll dig deeper tomorrow, thanks for the quick answer Nikolai -- Ich verwalte sie. Ich zähle sie und zähle sie wieder. Das ist nicht leicht. Aber ich bin ein ernsthafter Mann. \\ ---> Antoine de Saint-Exupery, "Der kleine Prinz"
Nikolai Nespor <nikolai.nespor@utanet.at> writes:
rrd> (with-foreign-string-vector (argc argv '("create" "test.rrd" "--step=300" "DS:a:GAUGE:600:U:U" "DS:b:GAUGE:600:U:U" "RRA:AVERAGE:0.5:1:300")) (rrd-create argc argv))
I get this error:
arithmetic error FLOATING-POINT-INVALID-OPERATION signalled [Condition of type FLOATING-POINT-INVALID-OPERATION]
I've seen this happen in the glX libraries before. As I understand what's happening, SBCL enables floating point traps that are not normally enabled in C programs, and this can mess things up when calling foreign code. What I used to get around this was: ;;; Execute BODY with floating-point traps disabled. This seems to be ;;; necessary on (at least) Linux/x86-64 where SIGFPEs are signalled ;;; when creating making a GLX context active. #+sbcl (defmacro without-fp-traps (&body body) `(sb-int:with-float-traps-masked (:invalid :divide-by-zero) ,@body)) ;;; Do nothing on Lisps that don't need traps disabled. #-sbcl (defmacro without-fp-traps (&body body) `(progn ,@body)) and a WITHOUT-FP-TRAPS form around the offending foreign function calls. I'm not enough of a floating-point wizard to know why you'd be getting that particular trap though... James
In reply to James Bielman:
I've seen this happen in the glX libraries before. As I understand what's happening, SBCL enables floating point traps that are not normally enabled in C programs, and this can mess things up when calling foreign code. What I used to get around this was:
;;; Execute BODY with floating-point traps disabled. This seems to be ;;; necessary on (at least) Linux/x86-64 where SIGFPEs are signalled ;;; when creating making a GLX context active. #+sbcl (defmacro without-fp-traps (&body body) `(sb-int:with-float-traps-masked (:invalid :divide-by-zero) ,@body))
;;; Do nothing on Lisps that don't need traps disabled. #-sbcl (defmacro without-fp-traps (&body body) `(progn ,@body))
Which does work perfectly well form me! Thanks a lot.
and a WITHOUT-FP-TRAPS form around the offending foreign function calls. I'm not enough of a floating-point wizard to know why you'd be getting that particular trap though...
Neither am I (unfortunately). Thanks again, Nikolai -- Ich verwalte sie. Ich zähle sie und zähle sie wieder. Das ist nicht leicht. Aber ich bin ein ernsthafter Mann. \\ ---> Antoine de Saint-Exupery, "Der kleine Prinz"
Nikolai Nespor <nikolai.nespor@utanet.at> writes:
(defvar *cmd* (list "test.rrd" "--step=300" "DS:a:GAUGE:600:U:U" "DS:b:GAUGE:600:U:U" "RRA:AVERAGE:0.5:1:300"))
(cffi:defcfun "rrd_create" :int (argcount :int) (args :pointer))
(cffi:with-foreign-pointer (ptr (+ (reduce #'+ (map 'vector #'length *cmd*))(length *cmd*) 6)) (cffi:lisp-string-to-foreign "dummy" ptr 6) ; librrd needs a dummy fst arg (let ((curptr (cffi:inc-pointer ptr 6))) (dolist (param *cmd*) (let ((size (1+ (length param)))) (cffi:lisp-string-to-foreign param curptr size) (setf curptr (cffi:inc-pointer curptr size))))) (rrd-create 6 ptr))
IIUC, you're allocating a bunch of memory of stuffing the 6 strings sequentially. This is not equivalent to what your C test code is doing. Translated to C, the above Lisp code would look something like this: char *ptr = malloc(<length of *cmd* strings + 6>); char *p = ptr; strncpy(p, "dummy", 6); p += 6; for (<each string>) { strncpy(p, <nth string>, <size of nth string>); p += <size of nth string>; } rrd_create(6, ptr); Which is not what rdd_create is expecting. Your C code should look something like this translating to CFFI: (with-foreign-pointer (ptr (1+ (reduce #'+ *cmd* :key #'length))) (setf (mem-ref ptr :pointer) (foreign-string-alloc "dummy")) (loop for i from 1 and param in *cmd* do (setf (mem-aref ptr :pointer i) (foreign-string-alloc (nth i *cmd*)))) (rdd-create 6 ptr) ;; free the strings here ) Hmm, we really need an array type in CFFI. -- Luís Oliveira luismbo (@) gmail (.) com Equipa Portuguesa do Translation Project http://www.iro.umontreal.ca/translation/registry.cgi?team=pt
participants (3)
-
James Bielman
-
Luís Oliveira
-
Nikolai Nespor