The C strings stuff is still up in the air, so I'll leave that for the time being. The main concern as far as adding functionality to the marshalling libraries seems to be the need to separate the compiler-dependent primitives from the rest of the library. With that in mind, I'll propose the following changes: * Deprecate everything in Storable except the class itself and its instances. * Add the Marshal module below (the names are of course all up for discussion, including the name of the module itself). The implementation of Marshal is compiler independent, except there's a requirement for a way to convert an errno value into an IOError. As noted below, perhaps the error processing, or at least the compiler-dependent portion, should be farmed off into another module. I omitted some of the generic utilities from Marcin's proposal, since while obviously useful they don't seem to belong here. Note that this isn't supposed to be the "higher-level marshalling library" we've been talking about, it's just s small step from what we have now, but an important step in that it puts the FFI into a usable state (at least for interfacing to C libraries, modulo the C strings support). That's important for us in the GHC team at least because it means we can start using this in GHC's libraries. My take on the C strings stuff: we can, and should, provide a portable-ish implementation of poke/peekCString which uses a (fictitious?) conversion library to do the character conversions, and uses standard marshalling to get to/from [CChar]. The point is that we'll probably need to optimise this at least in GHC, both to avoid writing our own conversion code and to speed things up, so it makes sense to place it all behind a specialised set of C string marshalling calls. Cheers, Simon module Marshal ( ------------------------------------------------------------------------ -- Allocation, deallocation and marshalling for objects and arrays malloc, -- :: Storable a => IO (Ptr a) mallocArray, -- :: Storable a => Int -> IO (Ptr a) mallocBytes, -- :: Int -> IO (Ptr a) alloca, -- :: Storable a => (Ptr a -> IO b) -> IO b allocaArray, -- :: Storable a => Int -> (Ptr a -> IO b) -> IO b allocaBytes, -- :: Int -> (Ptr a -> IO b) -> IO b reallocArray, -- :: Storable a => Ptr a -> Int -> IO (Ptr a) reallocBytes, -- :: Ptr a -> Int -> IO (Ptr a) free -- :: Ptr a -> IO () pokeArray, -- :: Storable a => Ptr a -> [a] -> IO () pokeArray0, -- :: Storable a => a -> Ptr a -> [a] -> IO () peekArray, -- :: Storable a => Ptr a -> Int -> IO [a] peekArray0, -- :: (Storable a, Eq a) => a -> Ptr a -> IO [a] withObject, -- :: Storable a => a -> (Ptr a -> IO b) -> IO b withArray, -- :: Storable a => [a] -> (Ptr a -> IO b) -> IO b withArray0, -- :: Storable a => a -> [a] -> (Ptr a -> IO b) -> IO b new, -- :: Storable a => a -> IO (Ptr a) newArray, -- :: Storable a => [a] -> IO (Ptr a) newArray0, -- :: Storable a => a -> [a] -> IO (Ptr a) ---------------------------------------------------------------- -- Haskellish interface to memcpy and memmove. copyArray, -- :: Storable a => Ptr a -> Ptr a -> Int -> IO () copyBytes, -- :: Ptr a -> Ptr a -> Int -> IO () moveArray, -- :: Storable a => Ptr a -> Ptr a -> Int -> IO () moveBytes, -- :: Ptr a -> Ptr a -> Int -> IO () ------------------------------------------------------------------ -- Error handling. -- Perhaps these should be moved to another module (PosixError?) getErrno, -- :: IO Int throwIf, -- :: (a -> Bool) -> String -> IO a -> IO a throwIf_EINTR, -- :: (a -> Bool) -> String -> IO a -> IO a throwIfNull, -- :: String -> IO (Ptr a) -> IO (Ptr a) throwIfNull_EINTR, -- :: String -> IO (Ptr a) -> IO (Ptr a) throwIfMinus1, -- :: Num a => String -> IO a -> IO a throwIfMinus1_EINTR, -- :: Num a => String -> IO a -> IO a throwCError, -- :: String -> Int -> IO a throwErrno, -- :: String -> IO a ------------------------------------------------------------------------ ----- -- Misc indexPtr -- :: Storable a => Ptr a -> Int -> Ptr a )
Simon Marlow <simonmar@microsoft.com> wrote,
The main concern as far as adding functionality to the marshalling libraries seems to be the need to separate the compiler-dependent primitives from the rest of the library. With that in mind, I'll propose the following changes:
* Deprecate everything in Storable except the class itself and its instances.
* Add the Marshal module below (the names are of course all up for discussion, including the name of the module itself).
The implementation of Marshal is compiler independent, except there's a requirement for a way to convert an errno value into an IOError. As noted below, perhaps the error processing, or at least the compiler-dependent portion, should be farmed off into another module.
I omitted some of the generic utilities from Marcin's proposal, since while obviously useful they don't seem to belong here.
------------------------------------------------------------------------ -- Allocation, deallocation and marshalling for objects and arrays
malloc, -- :: Storable a => IO (Ptr a) mallocArray, -- :: Storable a => Int -> IO (Ptr a) mallocBytes, -- :: Int -> IO (Ptr a)
alloca, -- :: Storable a => (Ptr a -> IO b) -> IO b allocaArray, -- :: Storable a => Int -> (Ptr a -> IO b) -> IO b allocaBytes, -- :: Int -> (Ptr a -> IO b) -> IO b
reallocArray, -- :: Storable a => Ptr a -> Int -> IO (Ptr a) reallocBytes, -- :: Ptr a -> Int -> IO (Ptr a)
free -- :: Ptr a -> IO ()
pokeArray, -- :: Storable a => Ptr a -> [a] -> IO () pokeArray0, -- :: Storable a => a -> Ptr a -> [a] -> IO ()
peekArray, -- :: Storable a => Ptr a -> Int -> IO [a] peekArray0, -- :: (Storable a, Eq a) => a -> Ptr a -> IO [a]
withObject, -- :: Storable a => a -> (Ptr a -> IO b) -> IO b withArray, -- :: Storable a => [a] -> (Ptr a -> IO b) -> IO b withArray0, -- :: Storable a => a -> [a] -> (Ptr a -> IO b) -> IO b
new, -- :: Storable a => a -> IO (Ptr a) newArray, -- :: Storable a => [a] -> IO (Ptr a) newArray0, -- :: Storable a => a -> [a] -> IO (Ptr a)
---------------------------------------------------------------- -- Haskellish interface to memcpy and memmove.
copyArray, -- :: Storable a => Ptr a -> Ptr a -> Int -> IO () copyBytes, -- :: Ptr a -> Ptr a -> Int -> IO ()
moveArray, -- :: Storable a => Ptr a -> Ptr a -> Int -> IO () moveBytes, -- :: Ptr a -> Ptr a -> Int -> IO ()
[..]
------------------------------------------------------------------------ ----- -- Misc indexPtr -- :: Storable a => Ptr a -> Int -> Ptr a )
These are fine with me.
------------------------------------------------------------------ -- Error handling.
-- Perhaps these should be moved to another module (PosixError?)
getErrno, -- :: IO Int throwIf, -- :: (a -> Bool) -> String -> IO a -> IO a throwIf_EINTR, -- :: (a -> Bool) -> String -> IO a -> IO a throwIfNull, -- :: String -> IO (Ptr a) -> IO (Ptr a) throwIfNull_EINTR, -- :: String -> IO (Ptr a) -> IO (Ptr a) throwIfMinus1, -- :: Num a => String -> IO a -> IO a throwIfMinus1_EINTR, -- :: Num a => String -> IO a -> IO a throwCError, -- :: String -> Int -> IO a throwErrno, -- :: String -> IO a
IMHO, they should go into another module. `PosixError' seems reasonable. Moreover, the first set of functions should be re-exported by `Foreign' (which re-exports all the language independent low-level FFI). Not so, PosixError, which isn't part of `Foreign' (like CTypes isn't). And because the greatest fun of all is to argue about names, I propose to rename `throwIf_EINTR', `throwIfNull_EINTR', and `throwIfMinus1_EINTR' to `throwIfEINTR', `throwIfNullEINTR', and `throwIfMinus1EINTR', respectively. For GHC, we should specify Qrczak's requirement that errno is thread local. Finally, I am not sure whether we should really call the module `Marshal', because that would have been the name that I would have given the main interface to the higher-level marshalling module. How about `MarshalUtils' (following the subject of this thread)? Cheers, Manuel
Sat, 02 Dec 2000 13:49:52 +1100, Manuel M. T. Chakravarty <chak@cse.unsw.edu.au> pisze:
And because the greatest fun of all is to argue about names, I propose to rename `throwIf_EINTR', `throwIfNull_EINTR', and `throwIfMinus1_EINTR' to `throwIfEINTR', `throwIfNullEINTR', and `throwIfMinus1EINTR', respectively.
Perhaps testing for (== -1) and (== nullPtr) is not worth their own functions and throwIf (== -1) is enough to keep the interface smaller. It must be stressed that these functions translate errno - this fact is not visible in their interface. They can easily be misused if a library function just returns something in case of an error but does not set errno at all. Anyway, I think that the following functions are more important :-) than throwIfMinus1 and throwIfNull (perhaps under better names): sequenceCont:: [(a -> z) -> z] -> ([a] -> z) -> z mapCont:: (a -> (b -> z) -> z) -> [a] -> ([b] -> z) -> z Example of usage: execv:: [String] -> IO a execv args = do mapCont withCString args $ \argPtrs@(arg0Ptr:_) -> withArray0 nullPtr argPtrs $ \argsPtr -> execvC arg0Ptr argsPtr throwErrno "execv" foreign import "execv" unsafe execvC :: Ptr CChar -> Ptr (Ptr CChar) -> IO CInt Without them one has to write a recursive helper function. Unfortunately plain mapM does not work for continuation-style allocations. -- __("< Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/ \__/ ^^ SYGNATURA ZASTÊPCZA QRCZAK
Simon Marlow <simonmar@microsoft.com> wrote,
[...] With that in mind, I'll propose the following changes: [...]
I agree with Manuel here: * Basically a "Make it so!" * Error handling should go into another module, but I'll stay off the naming battlefield this time. :-) * errno *is* a thread local thing, of course. * s/Marshal/MarshalUtils/ Cheers, Sven
participants (4)
-
Manuel M. T. Chakravarty -
qrczak@knm.org.pl -
Simon Marlow -
Sven Panne