Next Previous Contents

3.1 Interfacing to C libraries

This section presents a couple of worked out examples where HaskellDirect is used to create bindings to C libraries. If you do want to experiment with these examples (or maybe simply just use), the examples/ directory of the distribution contains the code.

A math library binding

As an initial example of IDL and how HaskellDirect can help you to create bindings to external libraries, here's the IDL specification for ANSI C's math library:

module Math {

  [pure]int  abs([in]int x);
  [pure]long labs([in]long x);
  [pure]double fabs([in]double x);

  [pure]double ceil([in]double x);
  [pure]double floor([in]double x);
  [pure]double fmod([in]double x,[in]double y);

  [pure]double exp([in]double x);
  [pure]double log([in]double x);
  [pure]double log10([in]double x);

  [pure]double frexp([in]double x,[in,out,ref]int* nptr);
  [pure]double ldexp([in]double x,[in]int n);
  [pure]double modf([in]double x,[in,out,ref]double* nptr);

  [pure]double pow([in]double x,[in]double y);
  [pure]double sqrt([in]double x);

  [pure]int rand(void);
  void srand([in]int seed);
  
  [pure]double cos([in]double x);
  [pure]double sin([in]double x);
  [pure]double tan([in]double x);

  [pure]double acos([in]double x);
  [pure]double asin([in]double x);
  [pure]double atan([in]double x);
  [pure]double atan2([in]double x,
                     [in]double y);

  [pure]double cosh([in]double x);
  [pure]double sinh([in]double x);
  [pure]double tanh([in]double x);
};

The functions that make up the math library are all encapsulated in an IDL module declaration, which causes the IDL compiler to generate a Haskell module Math containing the stubs that take care of interfacing to the external functions.

The specification is almost identical to the ANSI C function prototypes that can be found in a math.h include file, but the IDL function signatures does contain some extra information:

By attributing the parameter and result types with these extra attributes, the ambiguities of C function prototypes are made clear. The IDL compiler takes advantage of this information when generating the corresponding Haskell stub functions:

module Math where

import qualified Prelude

sin :: Prelude.Double -> Prelude.Double
sin x = ...

srand :: Int.Int32 -> IO ()

modf :: Prelude.Double 
     -> Prelude.Double 
     -> Prelude.IO (Prelude.Double, Prelude.Double)
modf x nptr = ...

(Notice how the generated Haskell code takes care of fully qualifying any imported functions or types it uses, avoiding unnecessary name clashes).

The presence of the [pure] attribute on sin causes it to be given the functional type Double -> Double, whereas the side-effecting srand is wrapped up as an IO action. The IDL types double and int correspond to the Haskell types Double and Int32, respectively (Int32 is one of the sized numeric types that are part of the Hugs/GHC extension libraries).

The rules for converting from an IDL function type signature with directional parameters into its corresponding Haskell signature is as expected:

The HaskellDirect distribution contains the Math specification together with some simple code that uses the generated stubs; have a look in the examples/math directory.

Fast strings

Here's an example of how to handle character strings more compactly from within Haskell, representing them as C character strings and by providing the standard C string operations over this compact representation. The IDL is as follows:

module FastString {

   // [ptr] turns FastString into an abstract type,
   // that is, it will not be packed (or unpacked)
   // into the Haskell representation of a string
   // when passing the border between Haskell and
   // the outside world.
   typedef [ptr]char* FastString;

   [unique]FastString malloc([in]unsigned int sz);
   void strcat([in,out]FastString dest,[in]FastString src);
   void strncat([in,out]FastString dest,
                [in]FastString src,
                [in]int n);
   int strcmp ([in]FastString s1,[in]FastString s2);
   int strncmp([in,string]char* s1,[in,string]char* s2,[in]int n);

   double atof([in]FastString str);
   int    atoi([in]FastString str);
   long   atol([in]FastString str);

   //A White Lie - str*cpy returns the dest. string.
   void strcpy([in,out]FastString dest,[in,string]FastString src);
   void strncpy([in,out]FastString dest,
                [in]FastString src,
                [in]int n);
   FastString strchr([in]FastString s, [in]char c);
   FastString strrchr([in]FastString s, [in]char c);
   int    strpos([in]FastString s, char c);
   int    strrpos([in]FastString s, char c);

   int    strspn([in]FastString s, [in]FastString set);
   int    strcspn([in]FastString s, [in]FastString set);
   FastString strpbrk([in]FastString s, [in]FastString set);
   FastString strrpbrk([in]FastString s, [in]FastString set);

   FastString strtok([in]FastString s, [in]FastString set);
};

Points worth noting about this spec:

Should you want to play with this example, examples/string contains the IDL specification along with a simple application that uses it.

The gd library

A third example of how to use IDL to create Haskell bindings to external libraries is an interface to the Gd GIF library (available from Gd library.) The IDL specification for the library is included in the HaskellDirect distribution (see examples/gd), but since it's rather long, we won't include it here. Compared to the previous examples, the Gd IDL specification does make use of a couple of features we haven't seen so far:

The directory examples/gd contains the IDL specification along with Haskellised versions of the two demo programs that comes with the gd distribution, webgif and gddemo.

Strings again

The stub code that the IDL compiler generates take care of converting between the Haskell representation of argument and result values, and their external representations. On top of that, the stub code ensures that the storage taken up by the external data is freed up when we're through with it in the Haskell world. For instance, the following IDL type signature:

int strlen([in,string]char* str);

generates a Haskell function with the type signature

strlen :: String -> IO Int32

The body for this (rather pointless) function, marshals the String into a zero-terminated sequence of characters before calling the external function. When it returns, the storage allocated for the marshalled string is released.

When the external function returns a value that we want to unmarshal into the Haskell world, the situation changes:

[string]char* itoa([in]int i);

Here, the result is a zero terminated character string that will be unmarshaled into a Haskell String. The type of the generated Haskell stub for itoa then becomes:

itoa :: Int -> IO String

When the itoa action has finished unmarhaling the string, it releases the C string. HaskellDirect currently makes the assumption that the C heap is used for all dynamic allocation, so free() is used.

In the cases where the character string doesn't point to dynamically allocated memory, but a static chunk, you don't want to call free() (or any other freeing routine) when you've finished unmarshaling. To signal that the generated stub code shouldn't free an argument/result, use the custom attribute nofree. An example of this attribute in action can be found in examples/env.

A zlib binding

As a final example in this section, here's a Haskell binding to zlib, a general purpose data compression library (format and methods of compression described in RFC-1950/1951/1952), see the Zlib home page. The library provides functions for compressing and uncompressing data in memory, and on top of which a stdio-like interface to .gz compressed files is also provided.

The IDL specification for zlib is a relatively straightforward conversion of the contents of its C header file (from the zlib-1.1.3 distribution), but the spec does use the DCE IDL's support for 'dependent arguments' in a couple of places. For instance, the C function

uLong crc32 (uLong crc, const Bytef* buf, uInt len);

uses the third argument to carry the length of the sequence of bytes passed as second argument. DCE IDL does provide support for expressing this kind of dependency between arguments,

unsigned long crc32   ( unsigned long crc,   [in,size_is(len)]char* buf, [in]unsigned int len);

The size_is() attribute pinned on the second arg convey this piece of information. The IDL compiler makes use of this when generating the Haskell stub for crc32, giving it the type:

crc32 :: Word32 -> [Char] -> IO Word32

The sequence of bytes is represented as a list, whose length carry the value that the stub will pass as third argument when invoking crc32().

This representation of a buffer of bytes is only meant to illustrate how the size_is() attribute is made use of by the compiler; using a more compact buffer representation with crc32() is probably worth your while if you care about performance. (A future extension of HaskellDirect would be to generate stubs here that instead of lists uses a more compact representation, such as a byte array.)

The zlib specification can be found in examples/zlib together with an extremely simple unzipper example.


Next Previous Contents