Suppose you want to call a C function with signature int f(long, double *, float *, short *);
where the first parameter is passed by value, the second by reference, the third is an output and the the last is updated. In IDL terms: int f([in]long, [in,ref]double *, [out]float *, [in,out]short *); Now you could just use HDirect, which will write everything for you, but let's suppose you choose not to for some reason. Then you declare the foreign function foreign import ccall "f" c_f :: CLong -> Ptr CDouble -> Ptr CFloat -> Ptr CShort -> IO CInt and you write a Haskell wrapper that does the parameter marshalling by hand: f :: CLong -> CDouble -> CShort -> IO (CFloat, CShort, CInt) f a b d = with b $ \ pb -> alloca $ \ pc -> with d $ \ pd -> do r <- c_f a pb pc pd c <- peek pc d' <- peek pd return (c, d', r) The last bit could be any computation using c, d' and r, but the rest is boilerplate. The plan is to rewrite that as f :: CLong -> CDouble -> CShort -> IO (CFloat, CShort, CInt) f a b d = call c_f (val a . ref b . out . inout d) $ \ c d' r -> return (c, d', r) for suitable combinators call, val (because "in" is a reserved word), ref, out and inout. (You may be reminded of Danvy's printf.) The call combinator takes 3 arguments: - a worker function, usually a foreign function - a specification, consisting of a composition of parameter descriptions in the same order as the function signature, and - a continuation that takes the values of the outputs (again in the same order as they occur in the signature) and the result. The val, ref and inout combinators take the values to be passed; the out and inout combinators give rise to parameters of the final continuation. When value parameters occur first (as they often do), we can move them into the worker function: f :: CLong -> CDouble -> CShort -> IO (CFloat, CShort, CInt) f a b d = call (c_f a) (ref b . out . inout d) $ \ c d' r -> return (c, d', r) There's also a variant call_ that ignores the result: f_ :: CLong -> CDouble -> CShort -> IO (CFloat, CShort) f_ a b d = call_ c_f (val a . ref b . out . inout d) $ \ c d' -> return (c, d') It remains to define the combinators. The following is Haskell 98. > module Param where > import Foreign > import Foreign.C The parameter specifiers build a function that combines a worker function and a continuation operating on the results of the worker, to produce an IO action. Typically each specifier adds a parameter to the worker, and may add one to the continuation. So in general their types look like (rf -> rk -> IO b) -> (ExtraParam -> rf) -> (ExtraResult -> rk) -> IO b Value and reference parameters also take the input value. There is no extra result, and the combinators are fairly simple: > val :: a -> (rf -> rk -> IO b) -> (a -> rf) -> rk -> IO b > val a spec f k = spec (f a) k > ref :: Storable a => a -> (rf -> rk -> IO b) -> (Ptr a -> rf) -> rk -> IO b > ref a spec f k = with a $ \ p -> spec (f p) k In the case of output parameters, the worker takes an extra pointer, and we need to peek at that pointer to get the extra output, while preserving all the arguments that follow it. For that, we need a class that provides a generalized =<<: > class LiftIO r where > liftIO :: (a -> r) -> IO a -> r > instance LiftIO (IO c) where > liftIO = (=<<) > instance LiftIO r => LiftIO (b -> r) where > liftIO f v b = liftIO (flip f b) v and we use this to peek at the pointer: > out :: (Storable a, LiftIO rk) => > (rf -> rk -> IO b) -> (Ptr a -> rf) -> (a -> rk) -> IO b > out spec f k = alloca $ \ p -> spec (f p) (liftIO k (peek p)) Inout parameters combine reference and output parameters: > inout :: (Storable a, LiftIO rk) => > a -> (rf -> rk -> IO b) -> (Ptr a -> rf) -> (a -> rk) -> IO b > inout a spec f k = with a $ \ p -> spec (f p) (liftIO k (peek p)) Finally, 'call' applies this composition of parameter specifications to the base case, which is simply monadic binding of the result continuation to a worker action: > call :: rf -> ((IO a -> (a -> IO b) -> IO b) -> rf -> rk -> IO b) -> rk -> IO b > call f spec_fn k = spec_fn (>>=) f k Here's the variant that ignores the result: > call_ :: rf -> ((IO a -> IO b -> IO b) -> rf -> rk -> IO b) -> rk -> IO b > call_ f spec_fn k = spec_fn (>>) f k There are also other parameter marshalling combinators not used in the above example. A 'Maybe' value passed as either a null pointer or a reference: > unique :: Storable a => > Maybe a -> (rf -> rk -> IO b) -> (Ptr a -> rf) -> rk -> IO b > unique mb_a spec f k = withMaybe mb_a $ \ p -> spec (f p) k > where > withMaybe :: Storable a => Maybe a -> (Ptr a -> IO b) -> IO b > withMaybe Nothing k = k nullPtr > withMaybe (Just a) k = with a k Null-terminated strings: > string :: String -> (rf -> rk -> IO b) -> (CString -> rf) -> rk -> IO b > string s spec f k = withCString s $ \ p -> spec (f p) k Strings followed by their length: > stringLen :: Integral n => > String -> (rf -> rk -> IO b) -> (Ptr CChar -> n -> rf) -> rk -> IO b > stringLen s spec f k = > withCStringLen s $ \ (p, n) -> spec (f p (fromIntegral n)) k Arrays followed by their length: > arrayLen :: (Storable a, Integral n) => > [a] -> (rf -> rk -> IO b) -> (Ptr a -> n -> rf) -> rk -> IO b > arrayLen arr spec f k = > withArrayLen arr $ \ n p -> spec (f p (fromIntegral n)) k _______________________________________________ FFI mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/ffi