[plt-scheme] Problem with FFI functions returning pointers to 'C' functions

From: Eli Barzilay (eli at barzilay.org)
Date: Wed Sep 12 10:57:05 EDT 2007

On Sep 12, Tim Brown wrote:
> I've revisited my problem, and I have got a little further
> (in fact a solution I can work with).
> 
> On 12/09/07 10:10, Tim Brown wrote:
> > A more concrete example is setting a signal(3C) call:
> > 
> > (require (lib "foreign.ss"))
> > (unsafe!)
> > (define c-signal
> >   (get-ffi-obj "signal" #f (_fun _int (_fun _int -> _void) ->
> >                                (_fun _int -> _void))))
> > (c-signal 15 (lambda (f) #f))
> > 
> > However, when I run this on: MzScheme v370 [3m]
> > SunOS 5.10 x86 or even Linux
> > 
> > I get the following error:
> > ffi-call: expects type <non-null-cpointer> as 1st argument, given: #f;
> > other arguments were: (#<ctype>) #<ctype>
> 
> This error is caused by the signal function returning a NULL
> pointer.  Since no signal handler is initially installed for 15 in
> this case, a NULL is returned.

Right.  With pointers, #f stands for NULL, but not with functions.


> Why is the error reported by ffi-call, and not whatever it is that
> objected to the #f in the first place?

The `first place' is inside the implementation of `_cprocedure' which
just doesn't expect to deal with NULLs.  It's easy to change the
library to do that, but I'm not sure that it's a good idea.  If anyone
has a clear argument in favor then I'll do that, if not then at some
point in the future I'll make it possible to pass flags to `_fun' to
customize its behavior including allowing NULLs.  (I'll need to add
such flags anyway, it's needed also to control the ABI that gets
used.)

But in the meanwhile, here is a solution that works for now:

* Your (_fun ...) type for signal gets expanded to:

    (_cprocedure (list _int (_cprocedure (list _int) _void))
                 (_cprocedure (list _int) _void))

* You need a version of `_cprocedure' that can deal with NULLs, but
  you can't do that with "foreign.ss" since it doesn't give you the
  necessary primitives -- so grab them straight from the C level:

    (require (only #%foreign ffi-callback ffi-call))

* Once you have that, you can implement the variant of `_cprocedure'
  that just passes NULLs over (in both directrions):

    (define (_cprocedure* itypes otype)
      (make-ctype _fpointer
        (lambda (x) (and x (ffi-callback x itypes otype)))
        (lambda (x) (and x (ffi-call x itypes otype)))))

* Using this:

    (define c-signal
      (get-ffi-obj "signal" #f
        (_cprocedure* (list _int (_cprocedure* (list _int) _void))
                      (_cprocedure* (list _int) _void))))


> Anyway, I am returning a _fpointer, and only casting it (with ptr-set)
> to an (_fun _int -> _void) if it is (not #f).

I'm pretty sure that doing such a cast will not work.

-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                  http://www.barzilay.org/                 Maze is Life!


Posted on the users mailing list.