The typical usage consists of the following steps:
- Creates an interface file describing functions and datatypes
- Invoking kdirect on a created file - the .hsc file is generated. This is
a temporary file - used only to run hsc2hs from QForeign library on it
- Invoking hsc2hs on a generated file - the .hs file is generated. This is
the file with interfaced C functions
- Compiling the .hs file
We will go through these steps now.
The library we will interface is called Flag (not very useful, but
quite illustrative):
"flag.h"
#ifndef _flag_h
#define _flag_h
typedef long Age;
typedef char Woman;
typedef enum
{
White = 7, Black
} Color;
typedef struct
{
Color first, second;
} Flag;
typedef struct
{
Age age;
char *name;
Woman isWoman;
} Person;
extern Person *NewPerson(Age age, const char *name, Woman isWoman);
extern void destructor(Person *person);
extern Flag *NewFlag(Color col1, const char *col2);
extern void getRidOf(Flag *f);
extern char *flagName(Flag *f);
#endif
"flag.c"
#include
#include
#include
#include "flag.h"
Person *NewPerson(Age age, const char *name, Woman isWoman)
{
Person *p = (Person *)malloc(sizeof(Person));
p->age = age;
p->name = (char *)malloc(strlen(name) + 1);
p->isWoman = isWoman;
strcpy(p->name, name);
printf("%s was born and lived for %d years\n", p->name, p->age);
return p;
}
void destructor(Person *person)
{
free(person->name);
free(person);
printf("Another one bites the dust\n");
}
Flag *NewFlag(Color col1, const char *col2)
{
Flag *f = (Flag *)malloc(sizeof(Flag));
f->first = col1;
if (strcmp(col2, "white"))
f->second = Black;
else
f->second = White;
return f;
}
void getRidOf(Flag *f)
{
free(f);
printf("Another flag bites the dust\n");
}
char *flagName(Flag *f)
{
char *ret = (char *)malloc(100);
if (f->first == White)
strcpy(ret, "White flag");
else
strcpy(ret, "Black flag");
return ret;
}
The sample program using the Flag library:
#include
#include
#include "Flag.h"
main()
{
Color a = black;
Flag *f;
char *s;
Person *p;
f = NewFlag(a, "white");
s = flagName(f);
printf("%s\n", s);
p = newPerson(23, "Agatha Smith", 1);
printf("My name is %s and I'm %d years old.\n", p->name, p->age);
if (p->isWoman)
printf("I'm a woman!!!\n");
destructor(p);
free(s);
getRidOf(f);
}
In this little library we have both an abstract pointer - that's a Flag
struct - Haskell user doesn't know anything about its internals and a normal
struct - Person. Both have finalizers - functions to run when we no longer
need the pointer. We cannot, however, make struct pointer finalize itself
automagically - we have to do it manually, when we no longer need it.
Note that the caller is responsible for freeing the flagName
result. getRidOf must be called to free a Flag pointer, when we no
longer need it.
Now, to interface the Flag library to Haskell we create Flag.khs file
(comments are marked by //, similarly to C++). The translation is quite
straightforward:
"Flag.khs"
#include "flag.h"
module Flag where
abstractPtr Flag * finalizer getRidOf;
enum Color { Black, White };
typedef Int Age;
typedef Bool Woman;
struct Person
{
Age age;
string name;
Woman isWoman;
};
Person *NewPerson(Age age, string person, Woman isWoman);
void destructor(Person *person);
Flag *NewFlag(enum Color col1, string col2);
void getRidOf(Flag *flag);
[free] string flagName(Flag *flag);
And that's all! Now we run:
kdirect Flag.khs
A Flag.hsc file is created. Then we run:
hsc2hs Flag.hsc
And Flag.hs file is created - with the Flag C library interfaced into Haskell.
Note that KDirect changes first letters to (upper/lower)case when needed.
Let's take a look at the generated code:
typedef Int Age;
typedef Bool Woman;
We have here C typedefs - Age and Woman - interfaced to Haskell. Note
that in typedef declaration in .khs file you tell kdirect
what's the Haskell counterpart of C type - Bool in case of
Woman, Int in case of Age. KDirect
will extract C type information by itself.
data Flag = FlagTag
type FlagPtr = ForeignPtr Flag
data Person = Person
Int -- age
String -- name
type PersonPtr = Ptr Person
For each structure named Foo, two Haskell types are generated: Foo and
FooPtr. The first one consists of the valid datatype, in case of a concrete
structure, and a useless tag in case of an abstract pointer. The second
one is a Haskell counterpart of a corresponding C pointer. This is what C
functions can take as an argument, and what they can return:
newPerson :: Int -> String -> IO (PersonPtr)
destructor :: PersonPtr -> IO (())
newFlag :: Color -> String -> IO (FlagPtr)
getRidOf :: FlagPtr -> IO (())
flagName :: FlagPtr -> IO (String)
To dereference a non-abstract Haskell pointer we use a peek function:
peek :: Ptr a -> IO a
WARNING!!!
Be warned that any interfaced C function doesn't have to be a well-behaved
function. It is a C function - which means it can modify memory to which
points its pointer argument. Thus, in the following piece of code:
do
personPtr <- newPerson 23 "foo"
person1 <- peek personPtr
destructor personPtr
person2 <- peek personPtr
person1 can be different from person2. You have
been warned.
Currently in and out modifiers are supported. Look at Notes file in the source
directory to see how to handle this case, but be warned that the syntax can
and possibly will change.
Now we can write our program in Haskell:
module Main where
import Flag
main = let
a :: Color
a = black in do
f <- newFlag a "white"
s <- flagName f
putStrLn s
personPtr <- newPerson 23 "John Smith"
Person age name <- peek personPtr
putStrLn $ "My name is " ++ name ++ " and I'm " ++ (show age) ++ " years old."
destructor personPtr
and compile everything:
ghc -c Flag.hs -fglasgow-exts `qforeign-config --hcflags`
gcc -c flag.c
ghc -c Main.hs -fglasgow-exts `qforeign-config --hcflags`
ghc Main.o Flag.o flag.o -o flag -fglasgow-exts `qforeign-config --ldflags --ldlibs`
Voila! We've got a working program.
If you have any questions about this tutorial or feel that it isn't clear
enough, email me.
Wojciech Moczydlowski, Jr