Contents

Faster Languages

Contents

Due to an exercise in an AI course, I’m forced to confront an old nemesis - C++. Part of the reason is that the exercise contains a time-limited tournament, and the code needs to run very quickly. Another reason is, I guess, the fact that C++ serves as a sort of lowest common denominator in the course (which used, by the way, to be taught in LISP, along with the language).

I never liked C++ language much. As a matter of fact, I prefer C. I’ve been going over some old code for a project, which needed to use DBus to talk to NetworkManager. Back then I wrote it using Python, embedded in C - it seemed easier at the time, due to lack of documentation. After hunting around, I figured out how to do most of the stuff I wanted in C, using DBus’s GLib API.

In this process, the most helpful bit of documentation turned out to be GLib’s. GLib looks like a wonderful library to get big-program stuff done relatively nicely in C, without mucking about in C++. Exception handling (of sorts), object-oriented programming (of sorts) as well as garbage collection (of sorts) are implemented in a usable way, and extremely well-documented.

At the end of the day, I was able to turn this Python gem:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import dbus

def _nm_device_interface(dev_object):
    """Returns an interface to the device object dev_object"""
    return dbus.Interface(dev_object, NM_DEVICE_IFACE)

def _nm_get_object(object_path):
    """Returns an object with the given object path using the NM service"""
    return dbus.SystemBus().get_object(NM_SERVICE, object_path)

def _nm(): return _nm_get_object(NM_OBJECT_PATH)

def _nm_dbus_exception(e, guessed_exception):
    """Checks if the DBus exception e is (exactly) of type guessed_exception"""
    try:
        return e.get_dbus_name().endswith(guessed_exception)
    except:
        # If it doesn't have a get_dbus_name, it probably isn't the DBus
        # exception we're looking for.
        return False

def _nm_all_device_interfaces():
    """Return a list of interfaces to all devices NM sees"""
    try:
        return [ _nm_device_interface(_nm_get_object(devicename))
                    for devicename in _nm().getDevices() ]
    except dbus.DBusException, e:
        if _nm_dbus_exception(e, "NoDevices"):
            return [] # No devices means list of devices is empty
        else: raise

…into this C gem:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#define DBUS_SERVICE_NM "org.freedesktop.NetworkManager"
#define DBUS_PATH_NM "/org/freedesktop/NetworkManager"
#define DBUS_INTERFACE_NM "org.freedesktop.NetworkManager"
#define NM_ERR_NODEVICES "org.freedesktop.NetworkManager.NoDevices"

gboolean is_remote_dbus_exception(GError *error, char * exception_name) {
        g_assert(error);

        if (error->domain != DBUS_GERROR ||
                        error->code != DBUS_GERROR_REMOTE_EXCEPTION)
                return FALSE;

        if (!exception_name)
                return TRUE;

        return strcmp(dbus_g_error_get_name(error), exception_name) == 0;
}

GPtrArray * get_nm_devices(DBusGConnection *connection, GError **err) {
        GError *tmp_error = NULL;
        DBusGProxy *proxy;
        GPtrArray *ptr_array;

        g_return_val_if_fail(connection != NULL, NULL);

        proxy = dbus_g_proxy_new_for_name(
                        connection,
                        DBUS_SERVICE_NM,
                        DBUS_PATH_NM,
                        DBUS_INTERFACE_NM);

        dbus_g_proxy_call(proxy, "getDevices", &tmp_error, G_TYPE_INVALID,
                        dbus_g_type_get_collection("GPtrArray",
                                DBUS_TYPE_G_PROXY), &ptr_array, G_TYPE_INVALID);

        if (tmp_error != NULL) {
                if (is_remote_dbus_exception(tmp_error, NM_ERR_NODEVICES)) {
                        g_error_free(tmp_error);
                        return NULL;
                }
                else {
                        g_propagate_error(err, tmp_error);
                        return NULL;
                }
        }

        g_object_unref(proxy);

        return ptr_array;
}

The C code runs much faster, and I suspect is more maintainable then its original counterpart (which uses embedded python in C).