On 06/11/2011 12:54 AM, keyd...@gmx.de wrote:
Hi again,

sorry for again asking such a basic FFI question, but I have a problem getting 
an output string from the C side...

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).


After the experience with the null-terminated strings from my recent post, I 
also tried an input-output-arg, passing a null-terminated string in to C:

(def-ocilib datetotext OCI_DateToText : (date_ptr : _pointer) (fmt : _string) (size : 
_int) (strval : (_ptr io _string) = (gen-output-string 100))->  (result : _bool) 
->  (values strval result))

where gen-output-string pads an empty string to a specified length.

But in both cases, I get the same error:

ptr-ref: expects type<cpointer>  as 1st argument, given: #<bad-value>; other 
arguments were: #<ctype>

Honestly, I have no idea what the problem might be (or how to debug/investigate 
it), and would very much appreciate any hints...  :-)

I'm not sure if it's possible to solve this problem with (_ptr o ???) or (_ptr io ???); I'd be very interested to hear if it's possible.

It looks like the function wants you to pass in a buffer for it to write into. The tricky part seems to be figuring out where the data ends. I assume the end is indicated by a null terminator. (The APIs I've worked with have had the courtesy to put the length of the string in an output parameter, which makes it easier.) Here's how I would do it:

Use _bytes to represent the buffer. A type like _string won't work, because the C function would be writing to a converted copy of the string; it wouldn't modify the string you gave it.

The C function writes the formatted date, followed by a null terminator, to the buffer. Now you need to extract only the formatted date, the part before the null terminator. You could search for the position of the null terminator yourself, or you could use an internal Racket function that makes a byte string ("bytes") from a null-terminated byte array.

(define-ffi-definer define-racket (ffi-lib #f))

(define-racket scheme_make_byte_string
  (_fun _bytes -> _racket))

For the main function, if you want to take the buffer as a parameter (because you calculated the size elsewhere or because you want to reuse the buffer), define the function this way:

;; OCI_DateToText : date-pointer string bytes -> (values boolean bytes)
(define-ocilib OCI_DateToText
  (_fun (date fmt buffer) ::
        (date : _pointer)
        (fmt : _string)
        (size : _int = (bytes-length buffer)
        (buffer : _bytes)
        -> (result : _boolean)
        -> (values result
                   (scheme_make_byte_string buffer))))

I find it useful to specify the parameters explicitly; that's what the part before the '::' is. It's sometimes mandatory, especially when you're calculating some parameter values based on others; I just always write them out unless the signature is trivial. The part after the first '->' is a type-spec that specifies what the foreign function returns. The part after the second '->' is a normal Racket expression that determines what the Racket wrapper returns.

Note that the definition above gives you the result as bytes; you can convert it to a string using bytes->string/???, whatever encoding it is. Or you could use Racket's scheme_make_utf8_string instead of scheme_make_byte_string if that's the right encoding.

If you want to create the buffer locally:

;; OCI_DateToText : date-pointer string -> (values boolean bytes)
(define-ocilib OCI_DateToText
  (_fun (date fmt) ::
        (date : _pointer)
        (fmt : _string)
        (size : _int = 100) ;; FIXME: big enough?
        (buffer : _bytes = (make-bytes size))
        -> (result : _boolean)
        -> (values result
                   (scheme_make_byte_string buffer))))

HTH,
Ryan
_________________________________________________
 For list-related administrative tasks:
 http://lists.racket-lang.org/listinfo/users

Reply via email to