GHC's Addr module is meant to be used in conjunction with the FFI (at least this is what the docs told me :-), but its plethora of similar functions is not very nice and some often needed functionality is missing. Attached is my proposed new version of Addr, being very similar to the things in the "Staying alive" thread. Some changes: * marshal.../unmarshal... are normal functions now and not methods of Marshalable anymore. * marshalList now returns the length of the list, too. * Some convenience functions for in/inout/out parameters are added. * After playing around with this module, I think that it is a Good Thing (tm) that ...OffAddr use element offsets and not byte offsets. It makes instance declarations of the following form much easier: instance Marshalable a => Marshalable (Foo a) where ... Apart from that, it is much more consistent with the old definitions. Alas, almost nobody mailed his/her wishes for a marshaling library, so this proposal is obviously biased towards HOpenGL's (and I think Manuel's) needs. Comments/suggestions? Cheers, Sven -- Sven Panne Tel.: +49/89/2178-2235 LMU, Institut fuer Informatik FAX : +49/89/2178-2211 LFE Programmier- und Modellierungssprachen Oettingenstr. 67 mailto:[EMAIL PROTECTED] D-80538 Muenchen http://www.informatik.uni-muenchen.de/~Sven.Panne
{- Hey Emacs, this is -*- haskell -*- ! @configure_input@ This file was part of HOpenGL - a binding of OpenGL and GLUT for Haskell. Copyright (C) 1999 Sven Panne <[EMAIL PROTECTED]> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library (COPYING.LIB); if not, write to the Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} module Addr ( Addr, nullAddr, -- Addr plusAddr, -- Addr -> Int -> Addr Marshalable( sizeOf, -- a -> Int alignment, -- a -> Int indexOffAddr, -- Addr -> Int -> a readOffAddr, -- Addr -> Int -> IO a writeOffAddr), -- Addr -> Int -> a -> IO () marshal, -- Marshalable a => a -> IO Addr marshalList, -- Marshalable a => [a] -> IO (Int, Addr), marshalListZero, -- Marshalable a => a -> [a] -> IO Addr unmarshal, -- Marshalable a => Addr -> IO a unmarshalList, -- Marshalable a => Int -> Addr -> IO [a] unmarshalListZero, -- (Marshalable a, Eq a) => a -> Addr -> IO [a] inParamWith, -- (a -> IO Addr) -> (Addr -> IO b) -> a -> IO b inParam, -- Marshalable a => (Addr -> IO b) -> a -> IO b inOutParamWith, -- (a -> IO Addr) -> (Addr -> IO a) -> (Addr -> IO b) -> a -> IO a inOutParam, -- Marshalable a => (Addr -> IO b) -> a -> IO a outParamWith, -- (a -> Int) -> (Addr -> IO a) -> (Addr -> IO b) -> IO a outParam, -- Marshalable a => (Addr -> IO b) -> IO a malloc, -- Int -> IO Addr free -- Addr -> IO () ) where import Monad(when, zipWithM_) import Addr import Int import Word ---------------------------------------------------------------------- -- Haskell equivalent of raw pointers {- We get these from Addr data Addr = ... instance Eq Addr where ... instance Ord Addr where ... instance Show Addr where ... nullAddr :: Addr plusAddr :: Addr -> Int -> Addr -} -- replacement for intToAddr/addrToInt instance Enum Addr where toEnum = intToAddr fromEnum = addrToInt ---------------------------------------------------------------------- -- primitive marshaling class Marshalable a where sizeOf :: a -> Int alignment :: a -> Int -- replacement for index-/read-/write???OffAddr indexOffAddr :: Addr -> Int -> a readOffAddr :: Addr -> Int -> IO a writeOffAddr :: Addr -> Int -> a -> IO () -- system-dependent, but rather obvious instances instance Marshalable Char where sizeOf = const @SIZEOF_CHAR@ alignment = const @ALIGNOF_CHAR@ indexOffAddr = indexCharOffAddr readOffAddr = readCharOffAddr writeOffAddr = writeCharOffAddr instance Marshalable Int where sizeOf = const @SIZEOF_INT@ alignment = const @ALIGNOF_INT@ indexOffAddr = indexIntOffAddr readOffAddr = readIntOffAddr writeOffAddr = writeIntOffAddr instance Marshalable Addr where sizeOf = const @SIZEOF_VOID_P@ alignment = const @ALIGNOF_VOID_P@ indexOffAddr = indexAddrOffAddr readOffAddr = readAddrOffAddr writeOffAddr = writeAddrOffAddr instance Marshalable Float where sizeOf = const @SIZEOF_FLOAT@ alignment = const @ALIGNOF_FLOAT@ indexOffAddr = indexFloatOffAddr readOffAddr = readFloatOffAddr writeOffAddr = writeFloatOffAddr instance Marshalable Double where sizeOf = const @SIZEOF_DOUBLE@ alignment = const @ALIGNOF_DOUBLE@ indexOffAddr = indexDoubleOffAddr readOffAddr = readDoubleOffAddr writeOffAddr = writeDoubleOffAddr instance Marshalable Word8 where sizeOf = const 1 alignment = sizeOf -- not sure about this indexOffAddr = indexWord8OffAddr readOffAddr = readWord8OffAddr writeOffAddr = writeWord8OffAddr instance Marshalable Word16 where sizeOf = const 2 alignment = sizeOf -- not sure about this indexOffAddr = indexWord16OffAddr readOffAddr = readWord16OffAddr writeOffAddr = writeWord16OffAddr instance Marshalable Word32 where sizeOf = const 4 alignment = sizeOf -- not sure about this indexOffAddr = indexWord32OffAddr readOffAddr = readWord32OffAddr writeOffAddr = writeWord32OffAddr instance Marshalable Word64 where sizeOf = const 8 alignment = sizeOf -- not sure about this indexOffAddr = indexWord64OffAddr readOffAddr = readWord64OffAddr writeOffAddr = writeWord64OffAddr instance Marshalable Int8 where sizeOf = const 1 alignment = sizeOf -- not sure about this indexOffAddr = indexInt8OffAddr readOffAddr = readInt8OffAddr writeOffAddr = writeInt8OffAddr instance Marshalable Int16 where sizeOf = const 2 alignment = sizeOf -- not sure about this indexOffAddr = indexInt16OffAddr readOffAddr = readInt16OffAddr writeOffAddr = writeInt16OffAddr instance Marshalable Int32 where sizeOf = const 4 alignment = sizeOf -- not sure about this indexOffAddr = indexInt32OffAddr readOffAddr = readInt32OffAddr writeOffAddr = writeInt32OffAddr instance Marshalable Int64 where sizeOf = const 8 alignment = sizeOf -- not sure about this indexOffAddr = indexInt64OffAddr readOffAddr = readInt64OffAddr writeOffAddr = writeInt64OffAddr ---------------------------------------------------------------------- -- convenience functions for (un-)marshaling -- Performance paranoia, one could use: -- marshal x = marshalList [x] marshal :: Marshalable a => a -> IO Addr marshal x = do buf <- malloc (sizeOf x) writeOffAddr buf 0 x return buf marshalList :: Marshalable a => [a] -> IO (Int, Addr) marshalList xs = do let numElements = length xs buf <- malloc (numElements * sizeOf (head xs)) zipWithM_ (writeOffAddr buf) [ 0 .. ] xs return (numElements, buf) -- Performance paranoia, one could use: -- marshalListZero zeroElem xs = marshalList (xs ++ [zeroElem]) marshalListZero :: Marshalable a => a -> [a] -> IO Addr marshalListZero zeroElem xs = do let numElements = length xs buf <- malloc ((numElements+1) * sizeOf (head xs)) zipWithM_ (writeOffAddr buf) [ 0 .. ] xs writeOffAddr buf numElements zeroElem return buf -- Performance paranoia, one could use: -- unmarshal buf = liftM head $ unmarshalList 1 buf unmarshal :: Marshalable a => Addr -> IO a unmarshal buf = readOffAddr buf 0 unmarshalList :: Marshalable a => Int -> Addr -> IO [a] unmarshalList numElements buf = mapM (readOffAddr buf) [ 0 .. numElements-1 ] unmarshalListZero :: (Marshalable a, Eq a) => a -> Addr -> IO [a] unmarshalListZero zeroElem buf = loop 0 [] where loop idx accu = do x <- readOffAddr buf idx if x == zeroElem then return $ reverse accu else loop (idx+1) (x:accu) ---------------------------------------------------------------------- inParamWith :: (a -> IO Addr) -> (Addr -> IO b) -> a -> IO b inParamWith marshal_ act x = do buf <- marshal_ x val <- act buf free buf return val inParam :: Marshalable a => (Addr -> IO b) -> a -> IO b inParam = inParamWith marshal inOutParamWith :: (a -> IO Addr) -> (Addr -> IO a) -> (Addr -> IO b) -> a -> IO a inOutParamWith marshal_ unmarshal_ act x = do buf <- marshal_ x act buf val <- unmarshal_ buf free buf return val inOutParam :: Marshalable a => (Addr -> IO b) -> a -> IO a inOutParam = inOutParamWith marshal unmarshal outParamWith :: (a -> Int) -> (Addr -> IO a) -> (Addr -> IO b) -> IO a outParamWith sizeOf_ unmarshal_ act = do buf <- malloc (sizeOf_ undefined) act buf m <- unmarshal_ buf free buf return m outParam :: Marshalable a => (Addr -> IO b) -> IO a outParam = outParamWith sizeOf unmarshal ---------------------------------------------------------------------- -- (de-)allocation of raw bytes malloc :: Int -> IO Addr malloc numBytes = do buf <- mallocAux numBytes when (buf == nullAddr) (ioError (userError ("malloc(" ++ show numBytes ++ ") failed"))) return buf -- Hmmm, Int is a little bit strange here, C uses size_t foreign import ccall "malloc" unsafe mallocAux :: Int -> IO Addr foreign import ccall "free" unsafe free :: Addr -> IO ()