Bug 158725 - free linked list with data
authorRyan Lortie <desrt@desrt.ca>
Fri, 16 Oct 2009 10:19:06 +0000 (12:19 +0200)
committerRyan Lortie <desrt@desrt.ca>
Thu, 28 Oct 2010 15:20:57 +0000 (11:20 -0400)
Add some helpers for freeing a linked list along with its elements by
providing a GDestroyNotify to call on each of them.

Add a test.

Based on a patch from Cosimo Cecchi.

docs/reference/glib/glib-sections.txt
glib/glib.symbols
glib/glist.c
glib/glist.h
glib/gslist.c
glib/gslist.h
glib/tests/list.c

index c547c8c1d4dff84222fe7dbc3f8bac3f4343e950..eefb3915a9165bf97ee6881c26e92c36503c1976 100644 (file)
@@ -1992,6 +1992,7 @@ g_list_remove_link
 g_list_delete_link
 g_list_remove_all
 g_list_free
+g_list_free_full
 
 <SUBSECTION>
 g_list_alloc
@@ -2048,6 +2049,7 @@ g_slist_remove_link
 g_slist_delete_link
 g_slist_remove_all
 g_slist_free
+g_slist_free_full
 g_slist_free_1
 g_slist_free1
 
index d0b3fa94c23a2f7c4c0dc3cd174a875cc75ff81b..c4211872cdaaaddaa25035d538626eb03ad345c5 100644 (file)
@@ -649,6 +649,7 @@ g_list_find_custom
 g_list_first
 g_list_foreach
 g_list_free
+g_list_free_full
 g_list_free_1
 g_list_index
 g_list_insert
@@ -1152,6 +1153,7 @@ g_slist_find
 g_slist_find_custom
 g_slist_foreach
 g_slist_free
+g_slist_free_full
 g_slist_free_1
 g_slist_index
 g_slist_insert
index c8f74346375eabbad4091ea2931271ffec1b00d2..6664964b7dddbee75e7cccb8abc5fda3c9cdab3f 100644 (file)
@@ -172,7 +172,8 @@ g_list_alloc (void)
  *
  * <note><para>
  * If list elements contain dynamically-allocated memory, 
- * they should be freed first.
+ * you should either use g_list_free_full() or free them manually
+ * first.
  * </para></note>
  */
 void
@@ -199,6 +200,24 @@ g_list_free_1 (GList *list)
   _g_list_free1 (list);
 }
 
+/**
+ * g_list_free_full:
+ * @list: a pointer to a #GList
+ * @free_func: the function to be called to free each element's data
+ *
+ * Convenience method, which frees all the memory used by a #GList, and
+ * calls the specified destroy function on every element's data.
+ *
+ * Since: 2.28
+ */
+void
+g_list_free_full (GList          *list,
+                 GDestroyNotify  free_func)
+{
+  g_list_foreach (list, (GFunc) free_func, NULL);
+  g_list_free (list);
+}
+
 /**
  * g_list_append:
  * @list: a pointer to a #GList
index e74ed96f15aa0656197f15374460955697c52a63..275005c79306b88a18d4bb28c78f11224a3b9950 100644 (file)
@@ -50,6 +50,8 @@ GList*   g_list_alloc                   (void) G_GNUC_WARN_UNUSED_RESULT;
 void     g_list_free                    (GList            *list);
 void     g_list_free_1                  (GList            *list);
 #define  g_list_free1                   g_list_free_1
+void     g_list_free_full               (GList            *list,
+                                        GDestroyNotify    free_func);
 GList*   g_list_append                  (GList            *list,
                                         gpointer          data) G_GNUC_WARN_UNUSED_RESULT;
 GList*   g_list_prepend                 (GList            *list,
index dfbd354403a9c537d0874e44b36ada9ccf1253df..f6581a8fa2858721d33ff8c4147c3beff08c4c5c 100644 (file)
@@ -186,6 +186,24 @@ g_slist_free_1 (GSList *list)
   _g_slist_free1 (list);
 }
 
+/**
+ * g_slist_free_full:
+ * @list: a pointer to a #GSList
+ * @free_func: the function to be called to free each element's data
+ *
+ * Convenience method, which frees all the memory used by a #GSList, and
+ * calls the specified destroy function on every element's data.
+ *
+ * Since: 2.28
+ **/
+void
+g_slist_free_full (GSList         *list,
+                  GDestroyNotify  free_func)
+{
+  g_slist_foreach (list, (GFunc) free_func, NULL);
+  g_slist_free (list);
+}
+
 /**
  * g_slist_append:
  * @list: a #GSList
index 8b01faf5cd468f7161ef723a2ff85226fcdca5dd..3731ba97ddaa7fae03b7a994624b9ab9faafb495 100644 (file)
@@ -49,6 +49,8 @@ GSList*  g_slist_alloc                   (void) G_GNUC_WARN_UNUSED_RESULT;
 void     g_slist_free                    (GSList           *list);
 void     g_slist_free_1                  (GSList           *list);
 #define         g_slist_free1                   g_slist_free_1
+void     g_slist_free_full               (GSList           *list,
+                                         GDestroyNotify    free_func);
 GSList*  g_slist_append                  (GSList           *list,
                                          gpointer          data) G_GNUC_WARN_UNUSED_RESULT;
 GSList*  g_slist_prepend                 (GSList           *list,
index 05bc8e4b4eee0f16f701560872096bdcf1280e55..084f7ead5e118833178ea42e42f14d06a255c02b 100644 (file)
@@ -294,6 +294,68 @@ test_list_insert (void)
   g_list_free (list);
 }
 
+typedef struct
+{
+  gboolean freed;
+  int x;
+} ListItem;
+
+static void
+free_func (gpointer data)
+{
+  ListItem *item = data;
+
+  item->freed = TRUE;
+}
+
+static ListItem *
+new_item (int x)
+{
+  ListItem *item;
+
+  item = g_slice_new (ListItem);
+  item->freed = FALSE;
+  item->x = x;
+
+  return item;
+}
+
+static void
+test_free_full (void)
+{
+  ListItem *one, *two, *three;
+  GSList *slist = NULL;
+  GList *list = NULL;
+
+  slist = g_slist_prepend (slist, one = new_item (1));
+  slist = g_slist_prepend (slist, two = new_item (2));
+  slist = g_slist_prepend (slist, three = new_item (3));
+  g_assert (!one->freed);
+  g_assert (!two->freed);
+  g_assert (!three->freed);
+  g_slist_free_full (slist, free_func);
+  g_assert (one->freed);
+  g_assert (two->freed);
+  g_assert (three->freed);
+  g_slice_free (ListItem, one);
+  g_slice_free (ListItem, two);
+  g_slice_free (ListItem, three);
+
+  list = g_list_prepend (list, one = new_item (1));
+  list = g_list_prepend (list, two = new_item (2));
+  list = g_list_prepend (list, three = new_item (3));
+  g_assert (!one->freed);
+  g_assert (!two->freed);
+  g_assert (!three->freed);
+  g_list_free_full (list, free_func);
+  g_assert (one->freed);
+  g_assert (two->freed);
+  g_assert (three->freed);
+  g_slice_free (ListItem, one);
+  g_slice_free (ListItem, two);
+  g_slice_free (ListItem, three);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -316,6 +378,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/list/remove-all", test_list_remove_all);
   g_test_add_func ("/list/first-last", test_list_first_last);
   g_test_add_func ("/list/insert", test_list_insert);
+  g_test_add_func ("/list/free-full", test_free_full);
 
   return g_test_run ();
 }