[racket] Freeing FFI resources
On Sun, Oct 10, 2010 at 5:40 PM, Eric Dobson <endobson at cs.brown.edu> wrote:
> I am dealing with a foreign library that has functions that return
> error messages in a char** input argument. These need to be explicitly
> freed by calling another function. I figured out how to get them into
> racket strings by using the _ptr and the _string ctype. But I didn't
> see a way to capture the value before conversion to a racket string so
> that I could free the original string, and continue to use the _string
> conversion process. Is there an easy way to do these two thing
> together?
There's a section of the FFI manual (Pointer Functions -> Memory
Management) that uses this scenario (or one very similar) as an
example:
http://docs.racket-lang.org/foreign/foreign_pointer-funcs.html?q=ffi&q=syntax-rules&q=define-syntax#(part._.Memory_.Management)
See the definition of the bytes/free ctype -- as well as the
discussion about why the code there is subtly incorrect.
Or, rather than using a finalizer (and assuming you want a character
string as output, not a byte string), you could use
bytes->string/[encoding] to produce a character string, then free the
original byte string and return the character string.
So let's say you have the following, contrived C functions:
int fail();
void get_err_msg(int, char**);
void free_err_msg(char *);
fail() just returns an error code, which you can pass to get_err_msg()
to get the corresponding string message. And you're supposed to free
the messages by passing them to free_err_msg().
Then, you can create bindings like so:
======================
#lang racket
(require ffi/unsafe)
;; create a byte string from a pointer
;;
;; make-byte-string : _pointer -> _bytes
(define (make-byte-string ptr)
(let loop ((i 0))
(cond ((zero? (ptr-ref ptr _byte i))
(make-sized-byte-string ptr i))
(else
(loop (add1 i))))))
;; A ctype meant to be used as an out param for strings
;; that are allocated by the library
;;
;; _out-string : _ctype
(define _out-string
(make-ctype _pointer
#f
(lambda (x)
(let* ([b (make-byte-string x)]
[s (bytes->string/latin-1 b)])
(free-err-msg x)
s))))
(define libtest (ffi-lib "/path/to/shared/lib"))
(define fail (get-ffi-obj "fail" libtest
(_fun -> _int)))
(define get-err-msg (get-ffi-obj "get_err_msg" libtest
(_fun _int (s : (_ptr o _out-string))
-> _void -> s)))
(define free-err-msg (get-ffi-obj "free_err_msg" libtest
(_fun _pointer -> _void)))
>
> -Eric
> _________________________________________________
> For list-related administrative tasks:
> http://lists.racket-lang.org/listinfo/users
>