Skip navigation

I noticed I still haven’t blogged about a project I worked on some time ago, so here goes.

A long time ago I wrote a small library for wrapping OGRE in Haskell. I wrote it manually, i.e. first writing some C functions that call C++ and have a C interface, and then wrote some Haskell functions to call the C functions. That’s the principle when wrapping C++ libraries in most higher level languages, mainly because most language implementations support calling C, but not C++.

Now, by writing the whole wrapper manually you have the complete power of the resulting interface, so you can try to bend the C interface so that the resulting interface has the natural feel of the higher level language you’re writing the binding for. The negative side to this is, of course, that it’s lots of work, and more or less tedious at that. There are therefore a lot of programs that automate some or part of the process of writing bindings to C libraries.┬áSo when wrapping a C library for Haskell, you can use a tool like hsc2hs, bindings-DSL or c2hs.

But it gets more problematic when you want to wrap C++ code. In the end you somehow have to wrap the C++ code to C – either manually or automatically. Some C++ libraries maintain their own C wrappers, like bullet and LLVM. For others, the wrapper writers must either write or generate their own C wrappers. When it comes to Haskell bindings for C++ libraries, the C wrappers were written manually at least for Irrlicht. I also wrote the C wrapper manually for the first versions of Hogre. As for the libraries that generate the C wrapper using a tool, there’s at least qtHaskell.

Now, at some point it got tiresome to manually write the Haskell bindings to Hogre, so I wrote a tool to automate it. Actually, there are two tools: cgen is a program that parses C++ header files and creates a C wrapper around the C++ library, and cgen-hs is a tool that parses the generated C header files and creates a Haskell wrapper around the C library. Using these two tools I created the newest version of Hogre, which is a lot more complete than the previous, manually written binding.

I also wrote a small example on how cgen works that I’ll demonstrate here. The input is Animals, a C++ library that allows access to a programmatic use of a wide range of animals. It’s a header-only library because I wanted to keep it simple, but that doesn’t really make a difference here. It’s split across three header files, listed here:

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;
};
class Dog : public Animal {
    public:
        Dog() { }
        void make_sound()
        {
            std::cout << "Growl!\n";
        }
};

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;
};
}

So, given this piece of code one can feed it to cgen, which generates a C interface for it. The result looks something like this (simplified):

/* Sheep.h */
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);
/* Sheep.cpp */
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();
}

So there we have a C wrapper for a C++ library ready for compilation.

For writing the Haskell binding, the next step is to run the next tool, cgen-hs. It takes the generated headers of the C wrapper as input, and as output creates a Haskell library that wraps the C library. The output looks something like this:

{-# 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

Then one can write a Haskell program using this new Haskell library, and run it for the output:

$ cat 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!"
$ ./Main
Growl!
Baa!
The sheep is now 5 years old!

So, everything seems to work well, and the newest Hogre version can be found at Hackage, as well as cgen.

Cgen is, however, in a very early stage of development and has, therefore, some weaknesses. First of all, it’s not a complete C++ parser, not even close, so it fails to parse many C++ header files. After configuring it for OGRE, it does manage to parse about two thirds of the OGRE headers, which is a good start, I guess. The C++ parser was the first real parser I wrote using Parsec, so a rewrite is probably the way to go.

Second of all, as I’ve only seriously used cgen for OGRE, it makes some assumptions about the C++ headers that may not hold for other libraries. The most critical one is probably that cgen-hs expects that all the classes and functions to wrap are in some namespace, which leads to problems when wrapping libraries that reside in the global namespace.

The third weakness is that it the generated Haskell functions are simply all in the IO monad, which suits well for OGRE (which doesn’t really have that many pure functions), but may be far from ideal for another library. Finally, cgen currently doesn’t support any class templates, neither wrapping them or using them as parameters or return values, so wrapping interfaces that rely heavily e.g. on STL will be troublesome. For Hogre this is not really a killer, although it does prevent the Hogre user from using some features that are available in OGRE.

All in all, however, cgen is working well for Hogre, and if you’re planning on writing a binding for a C++ library, you can give it a try. For more information, see the cgen homepage, a simple example, and the Hogre homepage.

About these ads

4 Comments

  1. Great work, i must try your tools on some lame C++ code i have floating around somewhere :)

  2. Wrapping C++ is a pain. For a recent project (http://www.althainz.de/HGamer3D.html) I wrote a python library, which parses C++ headers and generates wrapper code. Look at the link, source is also provided.

  3. This package is actually pretty baller. I wonder why there isn’t more buzz around it. With a little bit of work, it can be used to parse more significant projects like Qt or something!

  4. Why generate the C interface? Just because of name mangling? Why not implement the name mangling on haskells side. Then all you need is sizeof(T), a check for additional overloaded operator new symbols that would apply here, and you are done. Then you could even look into dealing with explicitly instantiated templates.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: