December 10, 2008

Programming GNOME applications with Vala

Author: Ben Martin

GNOME's Vala programming language lets you use the GLib2 object system at the heart of the GNOME desktop without having to do object-oriented programming in ANSI C. Unlike Mono or Java, a Vala program does not require any virtual machine or runtime libraries, so people who use your Vala objects don't even have to know they are not written in C.

The Vala compiler, valac, compiles Vala code into C code, which is then compiled with gcc into object code. A big issue when you try to use a high-level language in a traditionally C environment is language bindings: where they come from, how well maintained they are, and whether there are bugs in them. Using a high-level language can be more frustrating than just using C if the bindings are not high quality. Vala includes tools that use GLib introspection, which lets you generate a Vala binding for any GLib object. The packages of Vala for Fedora 9 include bindings to GLib2, GTK+2, SDL, SQLite, WebKit, libsoup, libglade-2, hildon, hal, gstreamer, cairo, and dbus -- thus, many of the libraries you would want for a GLib2/GTK+2 based desktop or handheld application are already available to a Vala application. The project also maintains a list of projects that provide Vala bindings.

That takes care of using C code from Vala -- what about the other way around? Vala can produce header files for GLib2 objects so that folks who use C/C++ can easily use your objects. Any language that can work from a C header file describing your GLib2 object should be able to use your Vala objects, so Perl and Python should also be able to use objects implemented in Vala.

Using Glib2 lets you define your own classes with signals and properties. For signals, in a GLib2 program written in C you use g_signal_new in your class_init function to tell GLib2 that this GObject should have a new signal associated with it. This new signal gets a unique numeric identifier, the signalID. To emit a signal you use g_signal_emit, passing the signalID and the parameters, if any, for the signal. Registering a new signal is quite verbose, calling the signal requires use of a numeric signalID, and you're not going to get good static type checking on the line that emits the signal.

The below C code first registers a new signal called "test-signal1" for a GLib2 object. The notify function can be attached to this signal and is used to respond to changes when the test-signal1 is emitted. The g_signal_connect function call ensures that the notify function is called whenever the test-signal1 is emitted. The next line actually emits the signal, causing the notify function to be called.

... class_init() {
...
g_test_signals[TEST_SIGNAL1] =
g_signal_new ("test-signal1",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GTestClass, test_signal1), NULL,
NULL, g_cclosure_marshal_VOID__INT,
G_TYPE_NONE, 1, G_TYPE_INT);
...
}

...
static void
notify (GObject *object, GParamSpec *spec, gpointer user_data)
{
gint value;

g_object_get (object, "test-prop", &value, NULL);
g_print ("+ %d", value);
}
...
test1 = g_object_new (G_TYPE_TEST, NULL);
g_signal_connect (test1, "test-signal1", G_CALLBACK (notify), NULL);
...
g_signal_emit (G_OBJECT (test), g_test_signals[TEST_SIGNAL1], 0, 0);

By contrast, the below Vala code defines and uses a Glib2 signal test_signal1. The signature of the signal is the same as the one in the above C example: it takes an integer and returns nothing. Notice that the signal is defined almost like a method; the parameters are defined using the same types as are used in other places in the code instead of the G_TYPE_NONE/G_TYPE_INT macros, and emitting the signal as is done on the last line of the program via a function invocation syntax. The line that includes the append operator for the signal, +=, defines a handler function inline that simply prints the argument to standard output.

public class Test : GLib.Object {

public signal void test_signal1(int a);

public static void main(string[] args) {
Test t1 = new Test();

t1.test_signal1 += (t, a) => {
stdout.printf("%d\n", a);
};

t1.test_signal1(5);
}
}

The Vala code is much more succinct, even though it actually does a lot more than the comparable C code. It is the complete Vala source for a runnable program using a custom GLib2 class.

Note that Vala will perform static type checking on the line that the signal is emitted. If I changed that line to try to emit test_signal1 with a string as its argument, the below compilation error would result.

$ valac -o signal signal.vala
signal.vala:12.25-12.29: error: Argument 1: Cannot convert from `string' to `int'
t1.test_signal1("foo");
^^^^^
Compilation failed: 1 error(s), 0 warning(s)

The language features of Vala do not stop at just making object and signal definition and handling easier. Vala also supports type inference for local variables, var foo = getFoo();, turning GLib2 errors into exceptions, and using foreach to iterate over a collection instead of the more verbose GLib C API.

Having a language with modern design features like foreach and lambda functions to write GLib2 object-based programs is wonderful. With Vala you can generate header files for your objects, and folks using other languages can use your objects without needing to think about how you have implemented them.

However, while Vala includes features targeted at making writing GLib2 programs as simple and pleasurable as possible, there are few resources for learning, let alone books about, the Vala language.

Category:

  • Programming