From: Alaric Snell-Pym <[email protected]>
Subject: Re: [r6rs-discuss] Thing2
Date: Mon, 28 Sep 2009 11:30:52 +0100

> On 28 Sep 2009, at 10:59 am, David Rush wrote:
> 
> > I've been doing a lot of FFI work in Scheme lately, and it's finally
> > gotten me to wondering: Is there any good reason to *not* have a
> > standard FFI in Thing2?
> 
> I think there's a good cause for standardising a basic minimal-
> assumptions FFI and having implementation-dependent extensions beyond
> that, yes.

I also agree that minimum Scheme-side interface may be
defined portably.   (The withdrawn srfi-50 tried to define
C-side API as well, but that opened huge can of worms.)

Whether it needs to be in the standard or not is another
question.  I feel SRFI can do the job, but it may worth
to discuss the possibility here.

> 1) How to get at the foreign functions in the first place. I suggest
> dlopen+libffi as that is fairly universal across implementation
> techniques, compared to statically linking code in (which may be
> painful in an interpreter).

How it is implemented can be left to each implementations.
What matters is API.   Although mainstream systems
support dlopen-like runtime dynamic liniking, some systems
may require to know external libraries at compilation
time.  To support both, declarative API, which allows an
implementation to know the linked library and functions at
the compile time, may be preferred as the base API.
An implementation may provide procedural API as an extension.

Some systems expose foreign function names differently
(e.g. whether prepending '_' or not).  We might need to
standardize the interpretation of the foreign function
name.  (This leads to the question of C++ mangled function
names, which I don't want to think about...)

> 2) What to do abut the heap and call/cc
> 
> 3) How to handle callbacks back into Scheme (which is really the
> tricky case for call/cc :-)

I'll discuss heap management later.

Thinking about the minimum interface, we can simply put
restrictions here: a Scheme function called back from
foreign functions must return exactly once.

In effect, continuation's extent is restricted if the
control passes over C stack boundary.   A continuation
captured in the callback cannot be invoked once the
callback returned.  A continuation captured outside of
foreign function's extent cannot be invoked from the
callback.   (Some implementation may loosen these
restrictions, but portable code can't rely on that).

As a collorary, a Scheme exception thrown in the
callbacks should be captured before returning to the
foreign function by the FFI, since we may not be
able to jump over foreign stack boundary.   How to
propagate the captured exception information is an
open question.

Simiarly, exceptions thrown in the foreign function
must be captured by FFI and converted to host Scheme's
exceptions.

> 4) How to handle C++ classes and the like, and access to C-compiler
> features like macros (some libraries aren't very amenable to dlopen()
> as they really expose a set of macros that expand to funnily-named
> symbols, or to dereferences through a struct of pointers, or
> whatever). This usually requires writing a stub in C and linking that
> in, or in Chicken, taking advantage of it being a Scheme->C compiler
> by writing inline C in your Scheme.

Or parse C header file and automatically generates stubs for
C macros; Gauche's ffi extension (c-wrapper) does that.
But again, as the minimum interface, we may just left them
unsupported.  The library can still provide a portable stub,
which is just a plain C function and agnostic to any particular
Scheme implementation.


What can't be left out from "minimum interface" is heap
management, since not all implementations are possible
to manage it completely automatically by the host GC.

(1) Passing a Scheme object to a foreign entity (including
callback closures): For portability, we should assume we
can't know when the foreign entity drops the reference to the
Scheme object.   A pragmatic approach is that the FFI
implicitly protects all objects that passed to the foreign
functions, and requires application code to unprotect them
explicitly.

(2) Bringing a boxed pointer of foreign-allocated object
to the Scheme world: The reference to the foreign object
can be dropped in finalizers if the implementation has the
capability, but we may not be able to assume that.
It'll be safer to ask portable application code
to explicitly destroy the foreign object when it's no
longer needed.  It's the same situation as file ports,
for example---the implementation may still provide a
safety-net by finalizers, but portable code shouldn't
assume that.

(3) Object identity of boxed foreign-object reference:
Suppose you have a foreign function that returns
a reference of the foreign object.  The function may
return a reference of the same foreign object it has
returned before.   In the Scheme side, how can we compare
those boxed references?  Should they be eq? if two
boxed references point to the same foreign object?
Or should we have a separate function to check the
identity of these foreign references?

--shiro


_______________________________________________
r6rs-discuss mailing list
[email protected]
http://lists.r6rs.org/cgi-bin/mailman/listinfo/r6rs-discuss

Reply via email to