Bringing the awesomeness of penguins to C#
Introduction
It has been a while, hasn't it? Well, here we go. Have you ever wondered how native C libraries are ported to C# with such ease? Take for instance Gtk#, or GtkMozEmbed#? In this article I will take you by the hand and explain how easy it is to port C functions to C#. You can even port them to object-oriented goodness in the tiniest of seconds by using a both external and internal class (that sounds bogus, but wait and see). If the library is written properly you wont even have to port difficult structures, you just juggle around with IntPtr's. Bouncing yet?
Enter libpenguin
Oh snap! I have finally found an easy library for effective penguin management, but it's in C! We'll have to port it to C# to make use of the awesome penguin-powers, for it would be a shame to lose them. Let's have a look at it, shall we? A penguin is defined like so:
char* name;
int pokes;
int max_pokes;
gender sex;
} penguin;
Naturally, there is an enum for gender which consists of male and female. Now if we would like to unleash a new penguin upon the world, and afterwards do some other stuff, we would use this code:
penguin *pingerella = penguin_new("pingerella",15,female); // This one's a little bit tougher
penguin_bounce(pingerinno); // Make our little friend bounce.
penguin_poke(pingerinno); // Don't poke him too much though!
penguin_print(pingerinno); // Print his information
penguin *kiddy = penguin_mate(pingerinno,pingerella, NULL); // Make them mate, null to generate new name.
penguin *named = penguin_mate(pingerella, pingerinno, "John Doe"); // Make them mate and specify name
Really, these are functions in libpenguin :) If you want to know more, you can find it's source here. All these are normally compiled into a libpenguin.so shared-object file using the GNU C-compiler, something like this:
The -fPIC flag tells gcc to generate code without an entrypoint, also known as a library. Then ld neatly wraps the object file in to a shared object, to be included by any other program that can interpret it. Now to bring these functions to C#, we'll have to use the System.Runtime.InteropServices namespace. But that's not all, we'll have to define the functions' skeletons in the C# code too, as extern functions. So here it goes:
Porting the goodness
So all we have to do is specify the .so (you can import form dll's too, by the way) file we are importing from, and the skeleton for the function. I didn't care much for generating a pointer, so the char* argument to penguin_new I just changed into a string. But lo and behold - it worked! No segfaults, so thumbs up to Mono! Now we can use the C functions in the same fashion we used them before:
That's all very well, but that doesn't really adhere to C# coding standards, does it? We'll have to make libpenguin object oriented! It's a good thing we made the External class. We'll mark this one as internal and make a new class that calls these functions for us from the same namespace. So we'll add this class to Antarctica:
All we do is wrap the code around a neat class, really. But this makes it much more pretty in an object-oriented sense. We also used the technique of overloading The code two blocks up would become something like this:
In my opinion, this looks a lot prettier. You don't have to worry about external names anymore and you can make function names in CamelCase without having to worry about specifying the wrong entrypoint into the shared object. Now it is up to you to be creative. You could port your favourite library to C#, although it is hard to find one that isn't yet these days. What you can also do is take an existing port and optimise it. For example, I have taken LibCurlNet by Jeff from Seaside Research and forked it into what I like to call CurlSharp. I cleaned up the enum declarations into proper CamelCase, added a little functionality and voila!
Epilogue
Being the awesome language that it is, it certainly thumbs up to C# for letting me port my old-time favourite libraries to my new-found love. Not only is it fun to see your old functions back again with fancier object-oriented programming, it's also very easy. You wont have to worry about char pointers, just give a string argument and it will be converted to a char* for you by the JIT (at least, mono does so). I plan to port something big to C# that isn't there yet when I need it, and looking forward I think it'll be massive fun.