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
