Here's the input, a library with animals.
/* cpp/Animal.h */
#ifndef ANIMALS_ANIMAL_H
#define ANIMALS_ANIMAL_H
#include <iostream>
namespace Animals {
class Animal {
public:
Animal()
: age(0) { }
~Animal() { }
virtual void make_sound() = 0;
int get_age() const
{
return age;
}
void increment_age()
{
age++;
}
private:
int age;
};
}
#endif
/* cpp/Dog.h */
#ifndef ANIMALS_DOG_H
#define ANIMALS_DOG_H
#include "Animal.h"
namespace Animals {
class Dog : public Animal {
public:
Dog() { }
void make_sound()
{
std::cout << "Growl!\n";
}
};
}
#endif
/* cpp/Sheep.h */
#ifndef ANIMALS_SHEEP_H
#define ANIMALS_SHEEP_H
#include "Animal.h"
namespace Animals {
class Sheep : public Animal {
public:
Sheep(int wooliness_level_ = 0)
: wooliness_level(wooliness_level_)
{ }
void make_sound()
{
std::cout << "Baa!\n";
}
void shear()
{
/* something */
}
private:
int wooliness_level;
};
}
#endif
The first thing to do is to create the C++ library with a C interface:
$ cgen -o c --header=Animal.h --header=Dog.h \
--header=Sheep.h cpp/Dog.h cpp/Sheep.h cpp/Animal.h
This command creates the C wrapper in directory "c". The given header files are those that the files in the C wrapper need to include in order to compile. This is what the output looks like:
$ cat c/Sheep.h
fndef CGEN_SHEEP_H
#define CGEN_SHEEP_H
#include <Sheep.h>
#include <Dog.h>
#include <Animal.h>
extern "C"
{
using namespace Animals;
#ifdef CGEN_HS
#endif
void Animals_Sheep_delete(Sheep* this_ptr);
Sheep* Animals_Sheep_new(int wooliness_level_);
void Animals_Sheep_make_sound(Sheep* this_ptr);
void Animals_Sheep_shear(Sheep* this_ptr);
}
#endif
$ cat c/Sheep.cpp
#define CGEN_OUTPUT_INTERN
#include "Sheep.h"
void Animals_Sheep_delete(Sheep* this_ptr)
{
delete this_ptr;
}
Sheep* Animals_Sheep_new(int wooliness_level_)
{
return new Sheep(wooliness_level_);
}
void Animals_Sheep_make_sound(Sheep* this_ptr)
{
this_ptr->make_sound();
}
void Animals_Sheep_shear(Sheep* this_ptr)
{
this_ptr->shear();
}
Now, the C wrapper can be compiled and an archive file can be created for static linking:
$ g++ -c -Icpp -o c/Animal.o c/Animal.cpp $ g++ -c -Icpp -o c/Dog.o c/Dog.cpp $ g++ -c -Icpp -o c/Sheep.o c/Sheep.cpp $ ar q libanimals.a c/Animal.o c/Dog.o c/Sheep.o
For creating the Haskell bindings, a class inheritance graph must first be created using grgen. This is necessary for creating the functions for upcasting in Haskell, which is implicit in C++.
$ grgen -o graph cpp/Animal.h cpp/Dog.h cpp/Sheep.h $ cat graph Animal| Dog|Animal Sheep|Animal
Then, the Haskell wrapper can be generated using cgen-hs. The files will be written in the "hs" directory.
$ cgen-hs -o hs --inherit=graph c/Animal.h c/Sheep.h c/Dog.h
This is what the output looks like:
$ cat hs/Sheep.hs
{-# LANGUAGE ForeignFunctionInterface #-}
module Sheep(
sheep_with,
sheep_delete,
sheep_new,
sheep_make_sound,
sheep_shear
)
where
import Types
import Control.Monad
import Foreign
import Foreign.C.String
import Foreign.C.Types
sheep_with :: Int -> (Sheep -> IO a) -> IO a
sheep_with p1 f = do
obj <- sheep_new p1
res <- f obj
sheep_delete obj
return res
foreign import ccall "Sheep.h Animals_Sheep_delete" c_sheep_delete :: Sheep -> IO ()
sheep_delete :: Sheep -> IO ()
sheep_delete p1 = c_sheep_delete p1
foreign import ccall "Sheep.h Animals_Sheep_new" c_sheep_new :: CInt -> IO Sheep
sheep_new :: Int -> IO Sheep
sheep_new p1 = c_sheep_new (fromIntegral p1)
foreign import ccall "Sheep.h Animals_Sheep_make_sound"
c_sheep_make_sound :: Sheep -> IO ()
sheep_make_sound :: Sheep -> IO ()
sheep_make_sound p1 = c_sheep_make_sound p1
foreign import ccall "Sheep.h Animals_Sheep_shear" c_sheep_shear :: Sheep -> IO ()
sheep_shear :: Sheep -> IO ()
sheep_shear p1 = c_sheep_shear p1
In addition to generating Haskell files that directly correspond to the header files, a file Types.hs is created, which has the code defining the upcasts mentioned above:
$ cat hs/Types.hs module Types where import Foreign import Foreign.C.String import Foreign.C.Types type CBool = CChar newtype Animal = Animal (Ptr Animal) -- nullary data type newtype Dog = Dog (Ptr Dog) -- nullary data type newtype Sheep = Sheep (Ptr Sheep) -- nullary data type newtype Void = Void (Ptr Void) -- nullary data type class CAnimal a where toAnimal :: a -> Animal instance CAnimal Dog where toAnimal (Dog p) = Animal (castPtr p) instance CAnimal Sheep where toAnimal (Sheep p) = Animal (castPtr p)
Then, the Haskell library can be used:
$ cat > hs/Main.hs
module Main
where
import Control.Monad (replicateM_)
import Types
import Animal
import Dog
import Sheep
main = do
dog_with $ \d ->
sheep_with 1 $ \s -> do
dog_make_sound d
sheep_make_sound s
sheep_shear s
replicateM_ 5 $ animal_increment_age (toAnimal s)
animal_get_age (toAnimal s) >>= \a -> putStrLn $ "The sheep is now " ++
show a ++ " years old!"
This can be compiled as follows (note linking against the created static library as well as the C++ runtime):
$ cd hs $ ghc --make -o Main -L.. -lanimals -lstdc++ Main.hs
Result:
$ ./Main Growl! Baa! The sheep is now 5 years old!
git clone -b example-animals git://github.com/anttisalonen/cgen.git