Let's get this finished

Marcin 'Qrczak' Kowalczyk mk167280 at students.mimuw.edu.pl
Fri Jan 5 11:48:02 EST 2001

On Sat, 6 Jan 2001, Manuel M. T. Chakravarty wrote:

> I think that this is a good idea.  Especially, because of
> the encoding business, Strings are special and it is better
> to make this explicit in the types.

I'm not sure if this is a good idea. I haven't seen convincing arguments
yet. Let's keep it simpler.

> Actually, given this unicode mess, how are we supposed to
> handle individual `Char's?

Depending what do you want to do. You may pass it as Char and get HsChar
in C. You may convert [ch] to a C string (which may be longer than one
character) and get char * in C. You may need it in an ASCII context (e.g.
a commandline flag) and cast it to CChar (there is castCharToCChar in
QForeign); "cast" in the name suggests that it preserves the physical
contents rather than the meaning.

> A Haskell `Char' may expand to a sequence of 8bit `char's.  That's a
> problem when the C side only expects a `char' and not a `*char'.

No library will be able to represent Char or wchar_t in a single char.

> > Could you explain the motivation for these?  I can see the need for some
> > way to convert Bool<=>CInt (which is defined by ISO C), but can't the
> > others be just fromIntegral?
> One reason for extra functions is that we might want to
> handle overflows differently.

We would need to use a different interface to signal the overflow. Some
exception monad (Maybe or IO). These additional functions don't provide it
anyway and I will stick with fromIntegral - the most natural Haskell's way
of converting integers.

> Furthermore, I have 
>   instance IntConv Char where
>     cToInt   = ord
>     cFromInt = chr

No external C library will want either HsChar or HsInt. When we are
writing the C part ourselves, we may choose the type on the C side without
conversion in Haskell. For working internally to Haskell ord and chr are

> Now, the current routines in MarshalUtils are overloaded via
> `Storable', which works fine for the primitives (in fact C2HS uses a
> default method for the primitives).  It becomes interesting for more
> complex types.

When there is more than one way to pass a complex type, I would just use
separate functions if a Storable instance does not fit.

I would represent a pointer to a C string as Ptr CChar in Haskell and use
different functions for distinguishing what we want to do with it. Passing
the string to C functions is not the only thing we might want to do with
it - e.g. we could wish to realloc it, or work on [CChar] in Haskell, or
convert it to an aray of bytes, or pass a pointer to the middle to a C
function. IMHO it's better and less constraining to be explicit, keep the
type and make approprate functions as needed. withCString is not that
harder than toAddr. There are too many possible operations to express them
in one overloaded function on different types.

> For example, remember the case where Marcin had to store a nullFunPtr
> on the C side and explicitly test for it before calling
> freeHaskellFunPtr.  I think, it was Malcolm who argued that Marcin
> should better use `Maybe (FunPtr a)' on the Haskell side.  One reason
> that Marcin didn't do that might be that he didn't have
> ToAddr/FromAddr, but only functions overloaded via `Storable'.

ToAddr/FromAddr would not help here, unless they provided some
appropriately overloaded variant of peek.

> This transparently handles the `Maybe' representation for
> NULL pointers, without cluttering the marshalling code with
> special code for `Maybe'.

Passing Maybe as a maybe-null pointer is easy already:
    maybe ($nullPtr) withCString maybeString $ \ptr -> ...

For the other direction a single function could be added:
    maybeNull :: (Ptr a -> IO b) -> Ptr a -> IO (Maybe b)
    maybeNull f ptr
        | ptr == nullPtr = return Nothing
        | otherwise      = liftM Just (f ptr)

Use it as such:
        maybeString <- maybeNull peekCString ptr

Marcin 'Qrczak' Kowalczyk

More information about the FFI mailing list