domenica 22 marzo 2009

Automatic bindings to C++ libraries

As I wrote last time, this post will be about lqt, and first of all about what lqt does.

Qt's and KDE's native language is C++, however some people wants to use them in a different language (in my case Lua) so many bindings have been created for these libraries. There are several methods you can use to do this.

The simplest way in principle is to write the bindings by hand: most languages have some way to communicate with C/C++ so, by writing wrappers for all the functions and methods in the library, you can create the modules you want. For example if you have this

int foo(int);

you can write this (in C)
int call_foo(lua_State *L) {
int ret;
int arg;
arg = lua_tointeger(L, 1); /* take the first argument */
ret = foo(arg);
lua_pushinteger(L, ret); /* give the return value to Lua */
return 1;
}

This function can then be used when a Lua script calls foo().

If you had instead "int bar(int);" only a single line needs to be changed, while the rest is boilerplate code. This fact is not Lua-specific at all and in fact many solutions have been created to make bindings more automated. Java bindings to Qt are created this way, and also Swig and SMOKE just to name a few.

Most of the automated generators require an "API description" file that tells the generator which functions, classes and methods it should wrap, with their arguments and members. This is the case for QtJambi and Swig (and tolua/tolua++). So someone has to write down this description in the first place, but for big libraries it is not so easy. QtSoftware can pay someone to write and mantain this file, but what about KDE?

KDE has SMOKE that takes a better approach because it parses the C++ code, with almost no manual work required. The headers are changed; run it again; you have new bindings; stop.

(SMOKE has many other qualities, such as being language agnostic. Stuff for another post/author)

What lqt does is basically the same as SMOKE, but specialized for Lua, and using a different tool. While SMOKE uses the perl based "kalyptus" to generate the wrappers, I used the parser written by Roberto Raggi. Since a well written C++ parser like this is officially a Big Thing(TM), there are uncountable copies and versions around the marble (the last one I'm aware of is in QtCreator). Thus it's difficult to make it clear which one you are talking about. The one lqt uses was written for KDevelop4 and used in QtJambi. I usually call it RPP for the sake of brevity, until Roberto gives it an official name and versioning system.

My little tool "cpptoxml" just dumps the data structure of RPP into an XML. This simple step has drawn some attention to cpptoxml, while the main work is actually done by RPP. Then a Lua script takes this XML and writes the binding modules. Peter wrote a sane build system, so the bindings can also be used on Windows and OSX. And in the end you can write your Lua program using Qt:
require'qtcore'
require'qtgui'

app = QApplication(1 + select('#', ...), {arg[0], ...})

hello = QPushButton(QString("Hello Planet!"))
hello:resize(140, 40)
hello:show()
app.exec()

I was planning to show some complex example with cool stuff like WebKit but I'd rather leave something for the next post.

4 commenti:

Anonimo ha detto...

"I was planning to show some complex example with cool stuff like WebKit but I'd rather leave something for the next post."

Cool - looking forward to it :)

MrGwen ha detto...

It's a nice solution the binding generation. Is the source code available ? I'm very interested by this solution for a Qt/Smalltalk binding

Thanks

Mauro Iazzi ha detto...

yes, it is available at http://repo.or.cz/w/lqt.git

have fun and let me know how it works.

mauro

Unknown ha detto...
Questo commento è stato eliminato da un amministratore del blog.