Did you know g_clear_object/pointer() ?

g_clear_object() has been in glib since 2.28, and now g_clear_pointer() has landed in glib master (see bug #674634).
Their typical usage is to implement GObject::dispose, or “goto out” pattern, previously you would write:

void my_dispose (GObject *obj)
{
  MyObject *self = (MyObject *) obj;
  if (self->priv->object != NULL)
    {
      g_object_unref (self->priv->object);
      self->priv->object = NULL;
    }
  if (self->priv->hash != NULL)
    {
      g_hash_table_unref (self->priv->hash);
      self->priv->my_hash = NULL;
    }
  etc...
}

or:

void some_func ()
{
  GHashTable *tmp = NULL;

  ...
  if (error)
    goto out;

  tmp = g_hash_table_new();
  ...

out:
  if (tmp != NULL)
    g_hash_table_unref (tmp);
}

But now those becomes:

void my_dispose (GObject *obj)
{
  MyObject *self = (MyObject *) obj;

  g_clear_object (&self->priv->object);
  g_clear_pointer (&self->priv->hash, g_hash_table_unref);
  etc...
}

or:

void some_func ()
{
  GHashTable *tmp = NULL;

  ...
  if (error)
    goto out;

  tmp = g_hash_table_new();
  ...

out:
  g_clear_pointer (&tmp, g_hash_table_unref);
}

As extra bonus, g_clear_object() and g_clear_pointer() are thread-safe. That means that 2 threads can clear the same pointer at the same time and it will be freed only once. I’ve heard some code needs thread-safe dispose…

Thanks to Simon McVittie for the original idea in telepathy-glib where we had tp_clear_object/pointer for years.

GTestDBus – test your DBus app

Recently, I’ve been working on making GDBus’ unit test helpers as public API on libgio (see bug #672985). Thanks to the help from David Zeuthen, it landed in gio master as GTestDBus object.

The basic idea is simple: g_test_dbus_up() starts a dbus-daemon and sets DBUS_SESSION_BUS_ADDRESS env variable. That means that your tests will then be using that private session bus instead of messing with user’s. When the unit test is done, call g_test_dbus_down() which will kill the dbus-daemon. Of course in the case you unit test fails and crash, it still make sure the dbus-daemon will be cleaned.

And finally, thanks to Alexander Larsson, it even works on win32.

Enjoy 😀

git fork – quickly publish your git branches

When working with a new git project, I like to publish my patches in git branches on my public git server. I usually use the collabora git server, but some people also have account on freedesktop, etc. But it’s a bit long just to publish a branch:

  1. locally: git clone ssh://git.gnome.org/git/empathy
  2. locally: ssh git.collabora.co.uk
  3. collabora: cd public_html/git && git init --bare git://git.gnome.org/empathy
  4. locally: git remote add xclaesse git+ssh://git.collabora.co.uk/git/user/xclaesse/empathy

Note how GNOME urls are different in 1) and 3), not only the ssh:// VS git:// but also the extra /git/ and it’s different with freedesktop.

So my first step to simplify this is to define insteadOf URLs in ~/.gitconfig both locally and on server:

locally:

[url "ssh://git.gnome.org/git/"]
    insteadof = gnome:
[url "git+ssh://git.collabora.co.uk/git/user/xclaesse/"]
    insteadof = collabora:

on server:

[url "git://git.gnome.org/"]
    insteadof = gnome:

Now the steps are:

  1. locally: git clone gnome:empathy
  2. locally: ssh git.collabora.co.uk
  3. collabora: cd public_html/git && git init --bare gnome:empathy
  4. locally: git remote add xclaesse collabora:empathy

Still I’m lazy so I don’t like sshing the server manually, so the final step is to add this git alias in your local ~/.gitconfig:

[alias]
    fork = !git config --get remote.origin.url | xargs -n 1 ssh git.collabora.co.uk \"cd ~/public_html/git && git init --bare\"

That will create a public repository for the project you’re currently in, just by typing “git fork”! Steps are now:

  1. locally: git clone gnome:empathy
  2. locally: git fork
  3. locally: git remote add xclaesse collabora:empathy

Update: It’s more flexible with a python script, save it as “git-fork”, put it somewhere in your PATH and chmod +x

#!/usr/bin/python

import subprocess

remote_host = "git.collabora.co.uk"
git_remote_prefix = "collabora:"
git_remote_name = "zdra"

# Get current git repository's name
remote = subprocess.check_output(["git", "config", "--get", "remote.origin.url"]).strip()
name = remote[max (remote.rfind("/"), remote.rfind(":")) + 1:]
if not name.endswith(".git"):
    name = name + ".git"

# Create a remote repository
remote_cmd = "cd ~/public_html/git && git init --bare " + name
subprocess.call (["ssh", remote_host, remote_cmd])

# Add the remote repository to git remotes and push master
subprocess.call (["git", "remote", "add", git_remote_name, git_remote_prefix + name])
subprocess.call (["git", "push", git_remote_name, "master"])