[racket] FFI question again - how to get a string back from C

From: Thomas Chust (chust at web.de)
Date: Sat Jun 11 09:48:05 EDT 2011

2011/6/11 keydana at gmx.de <keydana at gmx.de>:
> [...]
> I have a problem getting an output string from the C side...

Hello Sigrid,

it would be helpful if you mentioned the signature of the original C
function, otherwise it is hard to decide whether your binding's
signature is correct.

I will assume that the function in question is declared as follows in
C (a declaration I copied from the online reference documentation of
a C library for Oracle database access):

  OCI_EXPORT boolean OCI_API OCI_DateToText(
    OCI_Date *    date,
    const mtext * fmt,
    int           size,
    mtext *       str
  );

> [...]
> E.g. in one case, in my first attempt
>
> (def-ocilib datetotext OCI_DateToText : (date_ptr : _pointer) (fmt : _string) (size : _int) (strval : (_ptr o _string)) -> (result : _bool) -> (values strval result))
>
> I simply tried using (strval : (_ptr o _string)) for the return
> string (the argument size indicates the desired size for the output
> string).
> [...]

When the C function expects a char *, a (_ptr o _string) is wrong
because it will map to a char **. What you need is a buffer that can
be filled with the resulting string.

Something like this may do the job:

  (def-ocilib date->text OCI_DateToText
    (date fmt) ::
    (date : _pointer) (fmt : _string)
    (size : _int = (+ (string-length fmt) 127))
    (str : _pointer = (malloc 'atomic (add1 size))) ->
    (ok? : _bool) ->
    (and ok? (cast str _pointer _string)))

This wrapper function takes the date object and a format string as
arguments, allocates a buffer for the formatted result that is 128
bytes larger than the format string and returns the formatted result
in case of success or #f otherwise.

The buffer is allocated in Racket garbage collected memory, so it is
automatically reclaimed at some time after the function call has
completed. The 'atomic flag to the allocation call tells the Racket
garbage collector that it doesn't have to scan the block of memory for
pointers to other live objects.

Also note that, for safety reasons, I allocate a buffer that is one
byte larger than the size information I pass to the C function, since
I couldn't find any documentation on whether the function will assume
the size information to include space allocated for the terminating
zero byte of the returned C string or not — both assumptions are
equally common in C libraries.

I hope this helps :-)

Ciao,
Thomas


-- 
When C++ is your hammer, every problem looks like your thumb.



Posted on the users mailing list.