[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Chicken-users] Re: FFI to libgmp questions
From: |
felix |
Subject: |
Re: [Chicken-users] Re: FFI to libgmp questions |
Date: |
Sat, 29 Jun 2002 12:35:05 +0200 |
Hello, Peter!
>Ok, here is another attempt that finally *appears* to work:
>
>;; convert a pointer known to be a char* into a c-string so I can free the
>;; c pointer later and still have a garbage collected scheme string laying
>;; around.
>(define c-pointer->c-string
> (foreign-callback-lambda* c-string ((c-pointer ptr))
> " char *arena = C_alloc(C_SIZEOF_STRING(strlen(ptr) + 1));
> strcpy(arena, ptr);
> return (arena); /* copied into gc-memory??? */ "))
`C_alloc()' allocates memory on the C-stack. After returning from
this procedure, the memory is invalid! `C_alloc()' should only be called
in CPS-converted code, i.e. that was Chicken produces.
Another thing is that a value of return-type `c-string' is always copied,
so the `arena' buffer above will be copied again, leaking memory again
(if we would have used `mallloc()' instead of `C_alloc()').
>
>;; if there is a return char *, then manually copy it into a c-string, and
>;; free the real c-pointer returned by the function. This should make garbage
>;; collection happy.
>(define mpz_get_str
> (let ( (alloc (foreign-lambda c-pointer "mpz_get_str" c-string int mpz_t))
> (dealloc (foreign-lambda void "free" c-pointer)))
> (lambda (ptr base mpz)
> (let ((c-ptr (alloc ptr base mpz)))
> (if (equal? ptr #f)
> (let ((str (c-pointer->c-string c-ptr))) ;; copy the string...
> (dealloc c-ptr) ;; remove the mem alloced by mpz_get_str
> str)
> ptr)))))
>
>
>Does that look correct? in c-pointer->c-string, is the arena memory
>going to get copied into the garbage collector correctly without leaking
>any memory or using any dangling pointers?
Hm, not quite (see above). I would write it like this:
(define c-pointer->c-string
(foreign-lambda* c-string ((c-pointer ptr)) "return(ptr);") )
(define mpz_get_str
(let ([alloc (foreign-lambda c-pointer "mpz_get_str" pointer int mpz_t)]
[free (foreign-lambda void "free" c-pointer)] )
(lambda (ptr base mpz)
(let ([c-ptr (alloc ptr base mpz)])
(if (not ptr)
(let ([c-str (c-pointer->c-string c-ptr)])
(free c-ptr)
c-str)
c-ptr) ) ) ) )
Here, we use the foreign argument type `pointer', which accepts any
non-immediate (besides #f) Scheme value and passes a (void) pointer to
it's contents, without copying.
Another possibilty would be to never pass a default buffer, just copy
always:
(define mpz_get_str
(let ([buffer (make-string 1024)] ; `1024' might not be sufficient.
[alloc (foreign-lambda c-string "mpz_get_str" pointer int mpz_t)] )
(lambda (int mpz)
(alloc buffer int mpz) ) )
Generally there are three pointer types in the Chicken FFI:
1) c-pointer
argument-value: a machine-pointer object, passed as void*
return-value a freshly allocated machine-pointer object, returned as void*
2) pointer
argument-value: any non-immediate Scheme value or #f, passed as void*
return-value: not allowed as a return-value
byte-vector is similar, but only for byte-vector objects.
3) c-string
argument-value: a string or #f, passed as char*
return-value: a fresh string, copied from the address returned by the call
(char*)
This is a little bit confusing. Sorry.
cheers,
felix
--
P.S. External enums can be quite simply accessed, using
`define-foreign-variable':
(define-macro (define-foreign-enum . items)
`(begin
,@(map
(match-lambda
[(name realname) `(define-foreign-variable ,name int
,realname)]
[name `(define-foreign-variable ,name int)] )
items) ) )
; so for "enum {abc=3, def};" we would have:
(define-foreign-enum abc def)