Simon Marlow <simonmar@microsoft.com> wrote,
- MarshalString: Most people seem to be of the opinion that we have to handle strings specially due to Unicode conversion requirements. So, this is were string marshalling goes.
I've been wondering whether we should have something like
newtype CString = CString (Ptr CChar)
which would allow GHC to replace the representation with a ByteArray leading to a much more efficient implementation of withCString. There are other reasons why having a special CString type might be a good idea: a C string really is more than just a pointer to char, it is an array with a zero terminator and is specified in some kind of encoding. Having a special CString type would provide more type safety.
One problem with the optimisation is that GHC can't handle ByteArrays being *returned* from a foreign function.
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. Actually, given this unicode mess, how are we supposed to handle individual `Char's? 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'.
- MarshalConv: The C2HS IntConv, BoolConv, etc classes. Marcin just uses `fromIntegral' here, but I think that this is too limited - eg, it doesn't handle Bool.
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. I think, the Haskell report doesn't specify what happens in case of an overflow. In a binding, I can imagine that I want to abort in case of overflows during testing and in production code truncate the values. Furthermore, I have instance IntConv Char where cToInt = ord cFromInt = chr
Moreover, soemthing along the C2HS ToAddr/FromAddr classes, but it probably makes to have `stdArrayPtr' etc as additional member functions (this is similar to the newXXX routines that are in MarshalUtils now, but with additional overloading). Furthermore, Marcin-syle withXXX routines would fo here.
I'm not sure how ToAddr/FromAddr would work. From looking at C2HS, they seem to provide a consistent interface to marshalling that is specific to a given Haskell type. I haven't found a need for something like this in my own code yet, so again could you motivate?
The member function `stdAddr' from `ToAddr' is similar to MarshalUtils' `new' and `addrStdKeep' (and the derived `addrStd') are the matching deallocation routine (which MarshalUtils doesn't have) - the withXXX routines combine the two. 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. 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'. I use instance ToAddr a => ToAddr (Maybe a) where stdAddr Nothing = nullAddr stdAddr (Just x) = stdAddr x instance FromAddr a => FromAddr (Maybe a) where addrStdKeep addr | addr == nullAddr = return Nothing | otherwise = do x <- addrStdKeep addr return $ Just x This transparently handles the `Maybe' representation for NULL pointers, without cluttering the marshalling code with special code for `Maybe'. I also have a special case for `String's. Moreover, a binding author can add other instances for binding specific types. This happens, for example, in the GDK binding - however, there arguably overloading `Storable' would have been sufficient. It might be sufficient to have one additional class instead of two (at least, I don't see anymore why I originally used two). Cheers, Manuel