FFI Report, CVS Id 1.11

Marcin 'Qrczak' Kowalczyk qrczak at knm.org.pl
Tue Aug 21 08:43:33 EDT 2001

Sun, 19 Aug 2001 23:14:59 +1000, Manuel M. T. Chakravarty <chak at cse.unsw.edu.au> pisze:

> * I am still not convinced that we need
>   `Storable.destruct'.  For deallocating special purpose
>   structures that need a deep traversal, shouldn't we just
>   use a custom function?

Without destruct code like

    with haskellValue $ \ptr -> do
        just use ptr, no allocation nor deallocation

would be correct in 99% of cases, except that for some types it
leaks memory (when the only reasonable implementation of poke is not
idempotent wrt. allocated memory).

These cases include char* inside a struct, assumed to point to
a memory private to this struct.

Yes, it can be called by hand, just as C++ destructors could be
called by hand. But places where it should be called are automatically
predictable, as long as it is well defined which memory is initialized
and which is not. Since with* functions automatically call poke, they
should automatically call something which undoes side effects of poke.

You might avoid Storable instances for such types and only use
opaque ForeignPtrs to heap-allocated objects. In this case, if all
the allocation and deallocation is handled by foreign code, destruct
is not needed.

It makes sense however for more complex types which are made instances
of Storable, i.e. where memory allocation is done by Haskell. I
imagine that allocation in Haskell will be especially good idea now
when Simon Marlow made alloca faster than malloc.

A Haskell-C++ interfacing tool would hook destructors to destruct.

> * I am also not really convinced about
>   `MarshalUtils.withMany'.  There may be situations, where
>   such a function is handy, but should it really be in the
>   standard libraries?

It's not so easily written by hand. Explicit recursion is needed,
AFAIK it can't be simply written in terms of foldr, mapM etc. So it
requires a variable definition, and thus would be written in this
very form where needed, instead of "inlined".

It's the mapM of the continuation monad, where the monad is
    /\a. (a -> res) -> res
and thus can't be made instance of class Monad without wrapping in
a newtype. FFI uses the "(a -> IO b) -> IO b" type pattern much.

>   Moreover, it isn't really marshalling specific - it is JAFL
>   (Just Another Function on Lists).

Yes, but it's not available elsewhere.

> PtrDiff
> ~~~~~~~
> Maybe after all, `PtrDiff' wasn't that bad an idea.  To
> assume that a pointer difference fits into an `Int' (what we
> do at the moment) is pretty dodgy.  Remember that all that
> H98 requires of an `Int' is that it has >=30 bits.  IMHO,
> this is pretty weak for a general representation of a
> pointer difference.

Haskell already uses Int for these kinds of lengths: array indices
are mapped to Int, default implementations of list functions use Int
for measuring lengths.

On 64-bit processors Int is 64-bit (or 63-bit when targeting OCaml
etc.), so the need of handling objects larger than a gigabyte in 32-bit
architectures is temporary, if at all. Haskell has the advantage over
C that there is no temptation to express all sized integer types as
{char,short,int,long}, so there is no reason for Int to be too small.

On 32-bit processors C implementations generally don't allow objects
larger than 32 bits anyway.

> The annoying thing about our old use of `PtrDiff' was that
> it made `plusPtr' a pain to use (as we had to cast the
> offset to a `PtrDiff' first).  A simple solution would be
>   plusPtr :: Integral i => Ptr a -> i -> Ptr b

It would make "ptr `plusPtr` 1" use Integer by default (with a warning
with -Wall).

What about peekArray etc.? It's not clear where to stop.

 __("<  Marcin Kowalczyk * qrczak at knm.org.pl http://qrczak.ids.net.pl/
  ^^                      SYGNATURA ZASTÊPCZA

More information about the FFI mailing list