Re: RFC: glocal - automatically freeing memory when it goes out of scope



On 10 April 2012 11:28, Mikkel Kamstrup Erlandsen
<mikkel kamstrup canonical com> wrote:
> On 04/04/2012 05:35 AM, Colin Walters wrote:
>>
>> On Wed, 2011-11-16 at 21:05 +0100, Mikkel Kamstrup Erlandsen wrote:
>>>
>>> Hi all,
>>>
>>> I have been looking at gcc's "cleanup" attribute[1] that allows one to
>>> specify a callback that will be invoked when a variable goes out of
>>> scope. This allows one to play with automatically freeing resources.
>>
>>
>> So this is frankly pretty cool - but we can't make GLib/GTK+
>> dependent on GNU C.
>
>
> Totally agreed. (although cursory research seems to indicate this will work
> on clang as well, I realize glib is used with many other compilers)
>
>
>> I could imagine using this in some gcc/Linux-specific parts of GNOME
>> though; care to polish up the header, and we could put it somewhere it
>> can be shared/reused (not sure where...libegg?)
>
>
> Sure, I have some updates lying around I want to apply as well (fx. Go-style
> deferred method calls), then I repost an updated version.

Attached a new version as a late update to this thread, but better
than nothing I hope :-) I still haven't gotten around to sticking it
in libegg (or even a bug). Sorry! I'll do that when I am more happy
with what I have.

In any case, here are some updates:

 - Added glocal_defer(callback, arg): Calls @callback with @arg when
leaving the current scope

 - Add convenience lock/unlock macros for GMutex, GRecMutex, and
GRWLock. They grab a given lock and unlock it when leaving the current
scope

 - Renamed to glib-gnuc.h since I find that more appropriate, and
better fits with clang using the gnuc profile (for those with those
tendencies).

Cheers,
Mikkel
/* glib-gnuc.h: gcc specific extensions to glib
 *
 * Copyright (C) 2011  Canonical Ltd
 *               2012  Mikkel Kamstrup Erlandsen
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Author: Mikkel Kamstrup Erlandsen <mikkel kamstrup canonical com>
 */

#include <glib.h>
#include <glib-object.h>

#ifndef __GLIB_GNUC_H__
#define __GLIB_GNUC_H__

#ifndef __GNUC__
  #error "This project uses non-standard GNU C extensions and requires 'gcc´ to compile."
#endif

G_BEGIN_DECLS

/**
 * glocal_string:
 * 
 * Macro declaring that a null terminated string be automatically freed when it
 * goes out of scope. For example:
 * <informalexample><programlisting>
 *   if (print_hello_world)
 *     {
 *       glocal_string gchar *str = g_strdup ("world!");
 *       g_printf ("Hello %s\n", str);
 *       // at this point 'str´ is automatically freed
 *     }
 * </programlisting></informalexample>
 * Beware that if you assign the value of the local string to some other string
 * that there is no transfer of ownership. The local variable will still be
 * freed when it goes out of scope. If you want to avoid this you can set the
 * local variable to %NULL. The following snippet is thus legal:
 * <informalexample><programlisting>
 *   gchar *snooped_string;
 *   if (print_hello_world)
 *     {
 *       glocal_string gchar *str = g_strdup ("world!");
 *       g_printf ("Hello %s\n", str);
 *       snooped_string = str;
 *       str = NULL;
 *       // at this point 'str´ is NULL and nothing will happen
 *     }
 *   g_printf ("Woohoo. I just stole the whole %s\n", snooped_string);
 *   g_free (snooped_string);
 * </programlisting></informalexample>
 */
#define glocal_string __attribute__ ((cleanup(_glocal_free_str)))

static void
_glocal_free_str (gchar **strp)
{
  if (strp && *strp)
    g_free (*strp);
}

/**
 * glocal_object:
 * 
 * Macro declaring that a GObject will be automatically unreferenced when 
 * it goes out of scope.
 *
 * Example:
 * <informalexample><programlisting>
 *   glocal_object GFile *file =  g_file_new_for_path ("/tmp");
 *   glocal_string gchar *basename = g_file_get_basename (file);
 *   g_printf ("Basename is '%s'\n", basename);
 *
 *   // The two variables file and basename are automatically freed
 * </programlisting></informalexample>
 *
 * If you want to avoid freeing the object when it goes out of scope you can
 * set the pointer to %NULL. See glocal_string() for an example.
 */
#define glocal_object __attribute__ ((cleanup(_glocal_unref_object)))

static void
_glocal_unref_object (void *ptraddr)
{
  GObject **objp = (GObject**) ptraddr;
  if (objp && *objp)
    g_object_unref (*objp);
}

/**
 * glocal_defer:
 * @cb: Callback to invoke when leaving the current scope
 * @arg: A pointer argument to pass to @cb
 *
 * Specify a function to call when leaving the current scope. The function
 * should take a single pointer argument and return void.
 *
 * The two arguments must be given as explicit function- and variable names.
 * You can not pass verbatim constants or expressions.
 *
 * You can only defer same pair of @cb and @arg once in each scope. You can,
 * however, defer the same callback with different arguments, or different
 * callbacks on the same argument.
 */
#define glocal_defer(cb, arg) \
  void *glocal_deferred_ ## cb ## _ ## arg [2] = { cb, arg }; \
  __attribute__ ((cleanup(_glocal_defer))) void **_glocal_deferred_ ## cb ## _ ## arg = glocal_deferred_ ## cb ## _ ## arg

static void
_glocal_defer (void ***cb_data)
{
  void **_cb_data = *cb_data;
  void (*cb) (void *) = _cb_data[0];
  void *data = _cb_data[1];
  
  cb (data);
}

/**
 * glocal_lock_mutex:
 * @mutex: The mutex to lock
 *
 * Convenience macro to lock a #GMutex and unlock it automatically when
 * leaving the current scope.
 *
 * The argument must be given as an explicit pointer variable. You can
 * not use an expression to this macro.
 */
#define glocal_lock_mutex(mutex) \
  g_mutex_lock (mutex); \
  glocal_defer (g_mutex_unlock, mutex)

/**
 * glocal_lock_rec_mutex:
 * @mutex: The recursive mutex to lock
 *
 * Convenience macro to lock a #GRecMutex and unlock it automatically when
 * leaving the current scope.
 *
 * The argument must be given as an explicit pointer variable. You can
 * not use an expression to this macro.
 */
#define glocal_lock_rec_mutex(mutex) \
  g_rec_mutex_lock (mutex); \
  glocal_defer (g_rec_mutex_unlock, mutex)

/**
 * glocal_lock_writer:
 * @rwlock: The read-write lock to take the writing lock on
 *
 * Convenience macro to take the writing lock on a #GRWLock and unlock it
 * automatically when leaving the current scope.
 *
 * The argument must be given as an explicit pointer variable. You can
 * not use an expression to this macro.
 */
#define glocal_lock_writer(rwlock) \
  g_rw_lock_writer_lock (rwlock); \
  glocal_defer (g_rw_lock_writer_unlock, rwlock)

/**
 * glocal_lock_reader:
 * @rwlock: The read-write lock to take the reading lock on
 *
 * Convenience macro to take the reading lock on a #GRWLock and unlock it
 * automatically when leaving the current scope.
 *
 * The argument must be given as an explicit pointer variable. You can
 * not use an expression to this macro.
 */
#define glocal_lock_reader(rwlock) \
  g_rw_lock_reader_lock (rwlock); \
  glocal_defer (g_rw_lock_reader_unlock, rwlock)

G_END_DECLS

#endif /* __GLIB_GNUC_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]