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