Monday, June 15, 2009

FFI: C function taking pointer to array

Say you want to call the following C function from Haskell:
#include <sys/times.h>
#include <time.h>
float etime_(float *tarray)
{
struct tms buf;
times(&buf);
tarray[0] = 1.0 * buf.tms_utime / CLOCKS_PER_SEC;
tarray[1] = 1.0 * buf.tms_stime / CLOCKS_PER_SEC;
return tarray[0] + tarray[1];
}
view raw etime.c hosted with ❤ by GitHub
The parameter tarray is a pointer to an array of floats. From C, you'd use it along the following lines:
    float times[2];
    etime_(times);
    printf("user time=%f, system time=%f\n", times[0], times[1]);
But in the Haskell world, even though such destructive updates are anathema, we can still talk back and forth.

First, we enable the Foreign Function Interface language pragma:

> {-# LANGUAGE ForeignFunctionInterface #-}
Then some front matter:
> module Main where
> import Foreign (Ptr)
> import Foreign.Marshal.Array (allocaArray,peekArray)
> import Control.Monad (mapM_)
We let Haskell know about the C function we want to call with an import declaration:
> foreign import ccall etime_ :: Ptr Float -> IO Float
To prepare for the call to the C function, allocaArray creates a new buffer and passes a handle to it (ta in the example below) to an action that calls etime_, pulls the data with peekArray, and returns these values along with the value returned from etime_ in a tuple:
> etime :: IO (Float, Float, Float)
> etime = do
>   allocaArray 2 $ \ta -> do
>     t <- etime_ ta
>     [user,sys] <- peekArray 2 ta
>     return (t,user,sys)
Use the etime action as in the following example:
> main :: IO ()
> main = do
>   (t,user,sys) <- etime
>   putStrLn $ "user time:    " ++ show user
>   putStrLn $ "system time:  " ++ show sys
>   putStrLn $ "process time: " ++ show t

No comments: