[racket] FFI
In `scm-to-blah`, you're trying to use `mkblah` to produce a C-level
blah, because that's what `define-cpointer` needs. But you've declared
`mkblah` to return a Racket-level `_blah` the same as `doublah`. It
doesn't work to `mkblah` and `_blah` in terms of each other.
I think you should think of `mkblah` as a function to generate a
C-level thing, so you can use it to implement `scm-to-blah`:
(define-blah mkblah (_fun _long -> _pointer))
(define (scm-to-blah x)
....
(cond ....
[(fixnum? x)
(printf " x is a fixnum~%")
(define p (mkblah x))
(cpointer-push-tag! p 'blah)
p]
....))
If you really want `mkblah` to produce a Racket-level `_blah`, then
define a separate "raw" view off mkblah() for the implementation of
`scm-to-blah`:
(define-blah mkblah/raw (_fun _long -> _pointer)
#:c-id mkblah)
(define (scm-to-blah x)
....
(cond [(fixnum? x)
(printf " x is a fixnum~%")
(define p (mkblah/raw x))
(cpointer-push-tag! p 'blah)
p]
....))
....
(define-blah mkblah (_fun _long -> _blah))
In this latter case, `mkblah` depends on `_blah`, `_blah` depends on
`scm-to-blah`, `scm-to-blah` depends on `mkblah/raw`, and `mkblah/raw`
depends only on primitives.
At Tue, 13 May 2014 22:33:31 +0200, Hamish Ivey-Law wrote:
> Dear Racket users,
>
> I am writing an FFI binding for the C library we develop at work as a
> "my first Racket project". I have encountered a problem that I
> haven't been able to resolve regarding the functions SCHEME-TO-C and
> C-TO-SCHEME which are parameters to DEFINE-CPOINTER-TYPE. The
> simplified version is as follows. Consider the following C library:
>
> ---START "blah.c"---
> #include <stdio.h>
> #include <stdlib.h>
>
> typedef long *blah;
>
> /* Make a new blah */
> blah mkblah(long x) {
> blah z = malloc(sizeof(long));
> printf(" In mkblah(x = %ld); z = %p\n", x, z);
> *z = x;
> return z;
> }
>
> /* Return double the argument */
> blah doublah(blah x) {
> printf(" In doublah(x = %p); *x = %ld\n", x, *x);
> return mkblah(*x * 2);
> }
> ---END---
>
> Compile with
> $ gcc -shared -fPIC -o blah.so blah.c
>
>
> Then my FFI bindings are as follows:
>
> ---START "blah.rkt"---
> #lang racket/base
> (require ffi/unsafe
> ffi/unsafe/define)
>
> (define *path* "./blah.so")
> (define libblah (ffi-lib *path*))
> (define-ffi-definer define-blah libblah)
>
> (define (scm-to-blah x)
> (printf "SCM --> BLAH with x = ~a" x)
> (cond [(blah? x)
> (printf " (x is already a blah)~%")
> x]
> [(fixnum? x)
> (printf " (x is a fixnum)~%")
> (mkblah x)]
> [else (error x "has unexpected type")]))
>
> (define (blah-to-scm x)
> (printf "BLAH --> SCM with x = ~a~%" x)
> x)
>
> (define-cpointer-type _blah #f scm-to-blah blah-to-scm)
>
> (define-blah mkblah (_fun _long -> _blah))
> (define-blah doublah (_fun _blah -> _blah))
> ---END---
>
> Running this gives:
>
> Welcome to Racket v6.0.
> -> ,en "blah.rkt"
> "blah.rkt"> (doublah 234)
> SCM --> BLAH with x = 234 (x is a fixnum)
> In mkblah(x = 234); z = 0x843310
> BLAH --> SCM with x = #<cpointer:blah>
> In doublah(x = 0x843310); *x = 234
> In mkblah(x = 468); z = 0x830020
> BLAH --> SCM with x = #<cpointer:blah>
> #<cpointer:blah>
>
> This is *almost* what I would expect. We see that 234 is converted
> from a Scheme fixnum to a blah via mkblah, then back to a
> #<cpointer:blah>. But SCM-TO-BLAH is not called again before DOUBLAH
> is called. I would have expected one of the following two outputs:
>
> "blah.rkt"> (doublah 234)
> SCM --> BLAH with x = 234 (x is a fixnum)
> In mkblah(x = 234); z = 0x843310
> BLAH --> SCM with x = #<cpointer:blah>
> SCM --> BLAH with x = #<cpointer:blah> (x is already a blah)
> In doublah(x = 0x843310); *x = 234
> In mkblah(x = 468); z = 0x830020
> BLAH --> SCM with x = #<cpointer:blah>
> #<cpointer:blah>
>
> (note the extra SCM --> BLAH line before the call to DOUBLAH) or
>
> "blah.rkt"> (doublah 234)
> SCM --> BLAH with x = 234 (x is a fixnum)
> In mkblah(x = 234); z = 0x843310
> In doublah(x = 0x843310); *x = 234
> In mkblah(x = 468); z = 0x830020
> BLAH --> SCM with x = #<cpointer:blah>
> #<cpointer:blah>
>
> where the result of SCM-TO-BLAH is passed directly to DOUBLAH.
>
> Here is why that's a problem. What I really want to do is provide a
> printer extension for blah values, and it seems to me that the only
> way to do that is to wrap the blah type in a struct and supply the
> appropriate method with #:methods gen:custom-write. So I wrap blah in
> a blah-hdl struct and adapt the surrounding code like so:
>
> ---START "blah2.rkt"---
> #lang racket/base
> (require ffi/unsafe
> ffi/unsafe/define)
>
> (define *path* "./blah.so")
> (define libblah (ffi-lib *path*))
> (define-ffi-definer define-blah libblah)
>
> (struct blah-hdl (ref))
> ;; #:methods gen:custom-write
> ;; [(define write-proc blah-print)])
>
> (define (scm-to-blah x)
> (printf "SCM --> BLAH with x = ~a~%" x)
> (cond [(blah-hdl? x)
> (printf " x is a blah-hdl~%")
> (blah-hdl-ref x)]
> [(fixnum? x)
> (printf " x is a fixnum~%")
> (mkblah x)]
> [else (error x "has unexpected type")]))
>
> (define (blah-to-scm x)
> (printf "BLAH --> SCM with x = ~a~%" x)
> (blah-hdl x))
>
> (define-cpointer-type _blah #f scm-to-blah blah-to-scm)
>
> (define-blah mkblah (_fun _long -> _blah))
> (define-blah doublah (_fun _blah -> _blah))
> ---END---
>
> This should wrap and unwrap the #<cpointer:blah> objects in blah-hdl
> structs in the passage from Racket to C and back. But:
>
> -> ,en "blah2.rkt"
> "blah2.rkt"> (doublah 234)
> SCM --> BLAH with x = 234
> x is a fixnum
> In mkblah(x = 234); z = 0x944140
> BLAH --> SCM with x = #<cpointer:blah>
> ; blah->C: argument is not non-null `blah' pointer
> ; argument: #<blah-hdl>
> ; [,bt for context]
> "blah2.rkt"> ,bt
> ; blah->C: argument is not non-null `blah' pointer
> ; argument: #<blah-hdl>
> ; context...:
> ; /usr/share/racket/pkgs/xrepl-lib/xrepl/xrepl.rkt:1346:0
> ; /usr/share/racket/collects/racket/private/misc.rkt:87:7
>
> I do not understand this error and the error message (minus the
> 'blah's) doesn't get any hits on Google. It seems to result from the
> fact that a blah-hdl is being given directly to doublah somehow. What
> am I doing wrong?
>
> Thanks in advance, and sorry for such a long first post!
>
> Kind regards,
> Hamish.
> ____________________
> Racket Users list:
> http://lists.racket-lang.org/users