[PATCH] GIterator



Iterators for GLib and GObject

	OK, here are my thoughts on iterators for GLib.
	First, a couple of terms.  I am discussing iterators as Python
or Java use them.  My original code was based on Java's Enumeration
interface, and is now based loosely on the Iterator interface.  Python
iterators behave similarly.  When I speak of an iterator "user," I do
not mean a person implementing an iterator.  That person I refer to as
the "implementor."  The point of an iterator is that the person
retrieving an iterator from a provided API (the "user") does not have
any idea how it is implemented.  They only know the access functions for
iterators.  If this isn't clear, hopefully it will become more clear as
this document progresses ;-)
	I got going on this from the great debate about returning static
versus allocated constructs.  The discussion mostly revolved around
returning static strings vs g_strdup()ing them.  The subject of
allocated vs static GList returns was brought up but not really
discussed.  At the time, various GTK+ API returned GLists, and they
varied whether you were required to free the list.  We've improved that
a lot now, thank heavens.
	In providing an iterator structure, I generally had the
following goals in mind:

o The implementor should have flexible control over how the iterator
  behaves.
o People linking libgobject should be able to take advantage of GValue.
o There should be usable iterators for people only linking libglib.
o The interface should be similar whether GValue is being used or
  not.

	I looked at trying to use the same exact API for GValue and
non-GValue iterators, but it ended up being non-obvious how to do it
cleanly.  The generic use is:

--8<--------------------------------------------------------------
iter = foo_something_iterate(foo);
while (g_iterator_has_more(iter))
{
    item = (FooItem *)g_iterator_get_next(iter);
    do_something(item);
}
g_iterator_free(iter);

v_iter = foo_something_else_iterate(foo);
while (g_value_iterator_has_more(v_iter);
{
    g_value_iterator_get_next(v_iter, &gvalue);
    do_something_else(gvalue);
}
g_value_iterator_free(v_iter);
-->8--------------------------------------------------------------

        I've also added some nice convenience functions to do things
like iterate GLists and create a GValueIterator from a GIterator.  As
a side effect of the implementation, "generators" ala python are also
quite possible.  There is an example of a generator in testglib.c.
	Three patches are attached.  They are all against GLib HEAD.
The first (glib-2.0.3-giterator-00-iterator-core.patch) provides the
core GIterator data structure.  The second
(glib-2.0.3-iterator-10-giterator-funcs.patch) provides various
convenience functions to other parts of GLib.  The third
(glib-2.0.3-iterator-20-giterator-gvalue.patch) adds GValueIterator.
These patches are also in Bugzilla against the Bug ID #83729.
	Please have a look at this.  Folks who are interested, please
make your interest known.  I'm open to any and all feedback.
	I myself use a variation of the GIterator code in three
different projects and I enjoy the flexibility it provides.  I think it
would be a useful addition to GLib.

Joel

-- 

Life's Little Instruction Book #313

	"Never underestimate the power of love."

Joel Becker
http://www.jlbec.org/
diff -x CVS -uNr glib.orig/glib/Makefile.am glib.core/glib/Makefile.am
--- glib.orig/glib/Makefile.am	Mon Mar  4 21:17:39 2002
+++ glib.core/glib/Makefile.am	Thu Mar 14 19:08:31 2002
@@ -50,6 +50,7 @@
 	ghash.c			\
 	ghook.c			\
 	giochannel.c    	\
+	giterator.c		\
 	glibintl.h		\
 	glist.c			\
 	gmain.c         	\
@@ -113,6 +114,7 @@
 	ghash.h		\
 	ghook.h		\
 	giochannel.h	\
+	giterator.h	\
 	glist.h		\
 	gmacros.h	\
 	gmain.h		\
diff -x CVS -uNr glib.orig/glib/giterator.c glib.core/glib/giterator.c
--- glib.orig/glib/giterator.c	Wed Dec 31 16:00:00 1969
+++ glib.core/glib/giterator.c	Sun Feb  3 00:39:31 2002
@@ -0,0 +1,171 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * 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.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GLib Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+/* 
+ * MT safe - The implementor of the has_more/get_next functions is
+ *           responsible for the true MT safety of a GIterator.
+ */
+
+#include "glib.h"
+
+
+
+/* Structures
+ */
+struct _GIterator
+{
+  gpointer context;
+  GIteratorFunc         has_more_func;
+  GIteratorFunc         get_next_func;
+  GDestroyNotify        notify_func;
+};
+
+
+
+/* Prototypes
+ */
+static gpointer          g_iterator_false (gpointer thrway);
+
+
+
+/* Iterators
+ */
+
+/**
+ * g_iterator_new:
+ * @context: A context for the GIteratorFuncs to work with.
+ * @has_more_func: A function that returns TRUE if the iterator has more items.
+ * @get_next_func: A function that returns the next item.
+ * @notify_func: An optional destroy function to clean up the context upon g_iterator_free().
+ *
+ * Creates a new iterator object with the specified context and
+ * functions.  Iterator users should never see this, it is called by
+ * the implementor of the iterator.
+ *
+ * Return value: A new #GIterator.
+ */
+GIterator*
+g_iterator_new (gpointer                context,
+                GIteratorFunc           has_more_func,
+                GIteratorFunc           get_next_func,
+                GDestroyNotify          notify_func)
+{
+  GIterator *iter;
+
+  iter = g_new (GIterator, 1);
+  iter->context = context;
+  iter->has_more_func = has_more_func;
+  iter->get_next_func = get_next_func;
+  iter->notify_func = notify_func;
+
+  return iter;
+}  /* g_iterator_new() */
+
+/* This function simply returns FALSE for g_iterator_new_empty() */
+static gpointer
+g_iterator_false (gpointer thrway)
+{
+    return (gpointer) FALSE;
+}  /* g_iterator_false() */
+
+/**
+ * g_iterator_new_empty:
+ *
+ * Creates an iterator that always returns FALSE from the has_more_func.
+ *
+ * Return value: A new #GIterator.
+ */
+GIterator*
+g_iterator_new_empty (void)
+{
+    return g_iterator_new(NULL, g_iterator_false, NULL, NULL);
+}  /* g_iterator_new_empty() */
+
+/**
+ * g_iterator_has_more:
+ * @iter: The #GIterator to operate on.
+ *
+ * Query the #GIterator to see if there are more items.  This merely 
+ * calls the implementor's has_more_func.
+ *
+ * Return value: TRUE if the #GIterator has more items.
+ */
+gboolean
+g_iterator_has_more (GIterator *iter)
+{
+  gpointer result;
+
+  g_return_val_if_fail (iter != NULL, FALSE);
+
+  if (*iter->has_more_func != NULL)
+    result = (*iter->has_more_func) (iter->context);
+  else
+    result = (gpointer) FALSE;
+
+  return (gboolean) result;
+}  /* g_iterator_has_more() */
+
+/**
+ * g_iterator_get_next:
+ * @iter: The #GIterator to operate on.
+ *
+ * Retrieve the next item in the #GIterator.  This is undefined if
+ * has_more is FALSE.  This merely calls the implementor's get_next_func.
+ * Note that the context pointer is not modifiable by the get_next_func.
+ *
+ * Return value: TRUE if the #GIterator has more items.
+ */
+gpointer
+g_iterator_get_next (GIterator *iter)
+{
+  gpointer result;
+
+  g_return_val_if_fail (iter != NULL, NULL);
+  g_return_val_if_fail (*iter->get_next_func != NULL, NULL);
+
+  result = (*iter->get_next_func) (iter->context);
+
+  return result;
+}  /* g_iterator_get_next() */
+
+/**
+ * g_iterator_free:
+ * @iter: The #GIterator to free.
+ *
+ * Frees the #GIterator, calling the notify_func if it exists.
+ */
+void
+g_iterator_free (GIterator *iter)
+{
+  g_return_if_fail(iter != NULL);
+
+  if (*iter->notify_func != NULL)
+    (*iter->notify_func) (iter->context);
+
+  g_free(iter);
+}  /* g_iterator_free() */
+
+
diff -x CVS -uNr glib.orig/glib/giterator.h glib.core/glib/giterator.h
--- glib.orig/glib/giterator.h	Wed Dec 31 16:00:00 1969
+++ glib.core/glib/giterator.h	Sat Feb  2 22:51:18 2002
@@ -0,0 +1,58 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * 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.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GLib Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+#ifndef __G_ITERATOR_H__
+#define __G_ITERATOR_H__
+
+#include <glib/gtypes.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GIterator		GIterator;
+
+typedef gpointer (*GIteratorFunc)	(gpointer context);
+
+
+/* GIterator
+ */
+GIterator* g_iterator_new               (gpointer        context,
+                                         GIteratorFunc   has_more_func,
+                                         GIteratorFunc   get_next_func,
+                                         GDestroyNotify	 notify_func);
+
+GIterator* g_iterator_new_empty         (void);
+
+gboolean   g_iterator_has_more		(GIterator	*iter);
+
+gpointer   g_iterator_get_next		(GIterator	*iter);
+
+void	   g_iterator_free		(GIterator	*iter);
+
+
+G_END_DECLS
+
+#endif /* __G_STRING_H__ */
+
diff -x CVS -uNr glib.orig/glib/glib.h glib.core/glib/glib.h
--- glib.orig/glib/glib.h	Sun Nov  4 17:47:31 2001
+++ glib.core/glib/glib.h	Thu Jan 31 19:27:36 2002
@@ -42,6 +42,7 @@
 #include <glib/ghash.h>
 #include <glib/ghook.h>
 #include <glib/giochannel.h>
+#include <glib/giterator.h>
 #include <glib/glist.h>
 #include <glib/gmacros.h>
 #include <glib/gmain.h>
diff -x CVS -uNr glib.orig/tests/testglib.c glib.core/tests/testglib.c
--- glib.orig/tests/testglib.c	Thu May 30 18:47:16 2002
+++ glib.core/tests/testglib.c	Thu May 30 18:51:24 2002
@@ -307,6 +307,40 @@
   return FALSE;
 }
 
+static gpointer
+my_iterate_next (gpointer context)
+{
+    int *i;
+    int r;
+
+    i = (int *) context;
+
+    r = *i;
+    *i += 1;
+
+    return (gpointer) r;
+}
+
+static gpointer
+my_iterate_has (gpointer context)
+{
+    int *i;
+
+    i = (int *) context;
+
+    return (gpointer) (*i < 10);
+}
+
+static void
+my_iterate_free (gpointer context)
+{
+    int *i;
+
+    i = (int *) context;
+
+    g_free(i);
+}
+
 int
 main (int   argc,
       char *argv[])
@@ -331,6 +365,7 @@
   char chars[62];
   GRelation *relation;
   GTuples *tuples;
+  GIterator *iter;
   gint data [1024];
   struct {
     gchar *filename;
@@ -746,6 +781,20 @@
   g_print ("ok\n");
 
 
+  g_print ("checking iterators... ");
+
+  iter = g_iterator_new (g_new0(int, 1),
+                              my_iterate_has,
+                              my_iterate_next,
+                              my_iterate_free);
+  while (g_iterator_has_more (iter))
+      g_print ("%d ", (int) g_iterator_get_next (iter));
+
+  g_iterator_free (iter);
+
+  g_print ("ok\n");
+
+
   g_print ("checking string chunks...");
 
   string_chunk = g_string_chunk_new (1024);
@@ -1156,6 +1205,7 @@
 
   g_byte_array_free (gbarray, TRUE);
   g_print ("ok\n");
+
 
   g_printerr ("g_log tests:");
   g_warning ("harmless warning with parameters: %d %s %#x", 42, "Boo", 12345);
diff -x CVS -uNr glib.core/glib/ghash.c glib.funcs/glib/ghash.c
--- glib.core/glib/ghash.c	Thu May 30 18:51:19 2002
+++ glib.funcs/glib/ghash.c	Thu May 30 18:51:55 2002
@@ -697,3 +697,64 @@
       G_UNLOCK (g_hash_global);
     }
 }
+
+/**
+ * g_hash_table_iterate_keys:
+ * @list: The #GHashTable to iterate over.
+ *
+ * Creates an iterator to traverse the list of keys in this hash table.
+ * The list of keys is a snapshot of the current state of the hash table.
+ *
+ * Return value: The new #GIterator.
+ */
+GIterator*
+g_hash_table_iterate_keys (GHashTable *hash_table)
+{
+    GList *list = NULL;
+    GHashNode *node;
+    GIterator *iter;
+    gint i;
+
+    g_return_val_if_fail (hash_table != NULL, g_iterator_new_empty());
+
+    for (i = 0; i < hash_table->size; i++)
+        for (node = hash_table->nodes[i]; node; node = node->next)
+            list = g_list_prepend(list, node->key);
+
+    list = g_list_reverse(list);
+
+    iter = g_list_iterate_and_consume(list);
+
+    return iter;
+}
+
+/**
+ * g_hash_table_iterate_values:
+ * @list: The #GHashTable to iterate over.
+ *
+ * Creates an iterator to traverse the list of values in this hash table.
+ * The list of values is a snapshot of the current state of the hash
+ * table.
+ *
+ * Return value: The new #GIterator.
+ */
+GIterator*
+g_hash_table_iterate_values (GHashTable *hash_table)
+{
+    GList *list = NULL;
+    GHashNode *node;
+    GIterator *iter;
+    gint i;
+
+    g_return_val_if_fail (hash_table != NULL, g_iterator_new_empty());
+
+    for (i = 0; i < hash_table->size; i++)
+        for (node = hash_table->nodes[i]; node; node = node->next)
+            list = g_list_prepend(list, node->value);
+
+    list = g_list_reverse(list);
+
+    iter = g_list_iterate_and_consume(list);
+
+    return iter;
+}
diff -x CVS -uNr glib.core/glib/ghash.h glib.funcs/glib/ghash.h
--- glib.core/glib/ghash.h	Thu Dec  6 14:37:05 2001
+++ glib.funcs/glib/ghash.h	Sat Feb  2 21:48:31 2002
@@ -28,6 +28,7 @@
 #define __G_HASH_H__
 
 #include <glib/gtypes.h>
+#include <glib/giterator.h>
 
 G_BEGIN_DECLS
 
@@ -71,6 +72,8 @@
 guint	    g_hash_table_foreach_steal	   (GHashTable	   *hash_table,
 					    GHRFunc	    func,
 					    gpointer	    user_data);
+GIterator*  g_hash_table_iterate_keys      (GHashTable     *hash_table);
+GIterator*  g_hash_table_iterate_values    (GHashTable     *hash_table);
 guint	    g_hash_table_size		   (GHashTable	   *hash_table);
 
 #ifndef G_DISABLE_DEPRECATED
diff -x CVS -uNr glib.core/glib/glist.c glib.funcs/glib/glist.c
--- glib.core/glib/glist.c	Sun Jul  1 22:02:12 2001
+++ glib.funcs/glib/glist.c	Sun Feb  3 21:51:33 2002
@@ -874,3 +874,109 @@
   g_slist_free (runs);
   return list;
 }
+
+static gpointer
+g_list_iterator_has_more (gpointer context)
+{
+  gpointer *header;
+
+  g_return_val_if_fail (context != NULL, (gpointer) FALSE);
+
+  header = (gpointer *) context;
+  return (gpointer) (header[0] ? TRUE : FALSE);
+}
+
+static gpointer
+g_list_iterator_get_next (gpointer context)
+{
+  gpointer *header;
+  gpointer result;
+  GList *elem;
+  gboolean consume;
+
+  g_return_val_if_fail (context != NULL, NULL);
+
+  header = (gpointer *) context;
+  elem = (GList *) header[0];
+  consume = (gboolean) header[1];
+
+  /* User should have called has_more() */
+  g_return_val_if_fail(elem != NULL, NULL);
+
+  result = elem->data;
+
+  if (consume)
+    header[0] = (gpointer) g_list_delete_link(elem, elem);
+  else
+    header[0] = (gpointer) g_list_next(elem);
+
+  return result;
+}
+
+static void
+g_list_iterator_destroy_notify (gpointer context)
+{
+    gpointer *header;
+
+    g_return_if_fail (context != NULL);
+
+    header = (gpointer *) context;
+
+    g_free(header);
+}
+
+static GIterator *
+g_list_iterate_real (GList *list, gboolean consume)
+{
+  gpointer *header;
+  GIterator *iter;
+
+  if (!list)
+    return g_iterator_new_empty();
+
+  /* The header contains two elements.  The first is the next GList
+   * element to iterate.  The second is the value of consume.
+   */
+  header = g_new0(gpointer, 2);
+  header[0] = list;
+  header[1] = (gpointer) consume;
+
+  iter = g_iterator_new (header,
+                         g_list_iterator_has_more,
+                         g_list_iterator_get_next,
+                         g_list_iterator_destroy_notify);
+
+  return iter;
+}
+
+/**
+ * g_list_iterate:
+ * @list: The #GList to iterate over.
+ *
+ * Creates an iterator to iterate over the list.  The iterator traverses
+ * the list directly.  Removal of the most recently iterated item is
+ * safe, but no other deletion is supported.
+ *
+ * Return value: The new #GIterator.
+ */
+GIterator*
+g_list_iterate (GList *list)
+{
+  return g_list_iterate_real (list, FALSE);
+}
+
+/**
+ * g_list_iterate_and_consume:
+ * @list: The #GList to iterate over and consume.
+ *
+ * Creates an iterator that consumes the list as it iterates it.  The
+ * data elements are untouched.  Modification of the list is strictly
+ * forbidden, as the iterator destroys it.
+ *
+ * Return value: The new #GIterator.
+ */
+GIterator*
+g_list_iterate_and_consume (GList *list)
+{
+  return g_list_iterate_real (list, TRUE);
+}
diff -x CVS -uNr glib.core/glib/glist.h glib.funcs/glib/glist.h
--- glib.core/glib/glist.h	Sun Jul  1 22:02:12 2001
+++ glib.funcs/glib/glist.h	Sat Feb  2 21:18:57 2002
@@ -27,6 +27,7 @@
 #ifndef __G_LIST_H__
 #define __G_LIST_H__
 
+#include <glib/giterator.h>
 #include <glib/gmem.h>
 
 G_BEGIN_DECLS
@@ -91,6 +92,8 @@
 void     g_list_foreach        (GList            *list,
 				GFunc             func,
 				gpointer          user_data);
+GIterator* g_list_iterate      (GList            *list);
+GIterator* g_list_iterate_and_consume (GList     *list);
 GList*   g_list_sort           (GList            *list,
 				GCompareFunc      compare_func);
 GList*   g_list_sort_with_data (GList            *list,
diff -x CVS -uNr glib.core/glib/gslist.c glib.funcs/glib/gslist.c
--- glib.core/glib/gslist.c	Sun Jul  1 22:02:12 2001
+++ glib.funcs/glib/gslist.c	Sat Feb  2 23:14:08 2002
@@ -765,3 +765,109 @@
 {
   return g_slist_sort_real (list, (GFunc) compare_func, TRUE, user_data);
 }
+
+static gpointer
+g_slist_iterator_has_more (gpointer context)
+{
+  gpointer *header;
+
+  g_return_val_if_fail (context != NULL, (gpointer) FALSE);
+
+  header = (gpointer *) context;
+  return (gpointer) (header[0] ? TRUE : FALSE);
+}
+
+static gpointer
+g_slist_iterator_get_next (gpointer context)
+{
+  gpointer *header;
+  gpointer result;
+  GSList *elem;
+  gboolean consume;
+
+  g_return_val_if_fail (context != NULL, NULL);
+
+  header = (gpointer *) context;
+  elem = (GSList *) header[0];
+  consume = (gboolean) header[1];
+
+  /* User should have called has_more() */
+  g_return_val_if_fail(elem != NULL, NULL);
+
+  result = elem->data;
+
+  if (consume)
+    header[0] = (gpointer) g_slist_delete_link(elem, elem);
+  else
+    header[0] = (gpointer) g_slist_next(elem);
+
+  return result;
+}
+
+static void
+g_slist_iterator_destroy_notify (gpointer context)
+{
+    gpointer *header;
+
+    g_return_if_fail(context != NULL);
+
+    header = (gpointer *) context;
+
+    g_free(header);
+}
+
+static GIterator *
+g_slist_iterate_real (GSList *list, gboolean consume)
+{
+  gpointer *header;
+  GIterator *iter;
+
+  if (!list)
+    return g_iterator_new_empty();
+
+  /* The header contains two elements.  The first is the next GSList
+   * element to iterate.  The second is the value of consume.
+   */
+  header = g_new0(gpointer, 2);
+  header[0] = list;
+  header[1] = (gpointer) consume;
+
+  iter = g_iterator_new (header,
+                         g_slist_iterator_has_more,
+                         g_slist_iterator_get_next,
+                         g_slist_iterator_destroy_notify);
+
+  return iter;
+}
+
+/**
+ * g_slist_iterate:
+ * @list: The #GSList to iterate over.
+ *
+ * Creates an iterator to iterate over the list.  The iterator traverses
+ * the list directly.  Removal of the most recently iterated item is
+ * safe, but no other deletion is supported.
+ *
+ * Return value: The new #GIterator.
+ */
+GIterator*
+g_slist_iterate (GSList *list)
+{
+  return g_slist_iterate_real (list, FALSE);
+}
+
+/**
+ * g_slist_iterate_and_consume:
+ * @list: The #GSList to iterate over and consume.
+ *
+ * Creates an iterator that consumes the list as it iterates it.  The
+ * data elements are untouched.  Modification of the list is strictly
+ * forbidden, as the iterator destroys it.
+ *
+ * Return value: The new #GIterator.
+ */
+GIterator*
+g_slist_iterate_and_consume (GSList *list)
+{
+  return g_slist_iterate_real (list, TRUE);
+}
diff -x CVS -uNr glib.core/glib/gslist.h glib.funcs/glib/gslist.h
--- glib.core/glib/gslist.h	Tue Jun 26 09:01:14 2001
+++ glib.funcs/glib/gslist.h	Sat Feb  2 21:20:51 2002
@@ -27,6 +27,7 @@
 #ifndef __G_SLIST_H__
 #define __G_SLIST_H__
 
+#include <glib/giterator.h>
 #include <glib/gmem.h>
 
 G_BEGIN_DECLS
@@ -87,6 +88,8 @@
 void     g_slist_foreach        (GSList           *list,
 				 GFunc             func,
 				 gpointer          user_data);
+GIterator* g_slist_iterate      (GSList           *list);
+GIterator* g_slist_iterate_and_consume (GSList    *list);
 GSList*  g_slist_sort           (GSList           *list,
 				 GCompareFunc      compare_func);
 GSList*  g_slist_sort_with_data (GSList           *list,
diff -x CVS -uNr glib.core/tests/testglib.c glib.funcs/tests/testglib.c
--- glib.core/tests/testglib.c	Thu May 30 18:51:24 2002
+++ glib.funcs/tests/testglib.c	Thu May 30 18:52:00 2002
@@ -795,6 +795,62 @@
   g_print ("ok\n");
 
 
+  g_print ("checking iteration from GList... ");
+
+  list = NULL;
+  for (i = 0; i < 10; i++)
+    list = g_list_append(list, (gpointer) i);
+
+  iter = g_list_iterate(list);
+  while (g_iterator_has_more (iter))
+    g_print ("%d ", (int) g_iterator_get_next (iter));
+
+  g_iterator_free(iter);
+  g_list_free(list);
+
+  g_print ("ok\n");
+
+  g_print ("checking iteration from GSList... ");
+
+  slist = NULL;
+  for (i = 0; i < 10; i++)
+    slist = g_slist_append(slist, (gpointer) i);
+
+  iter = g_slist_iterate(slist);
+  while (g_iterator_has_more (iter))
+    g_print ("%d ", (int) g_iterator_get_next (iter));
+
+  g_iterator_free(iter);
+  g_slist_free(slist);
+
+  g_print ("ok\n");
+
+
+  g_print ("checking iteration of hash keys... ");
+
+  hash_table = g_hash_table_new (my_hash, my_hash_equal);
+  for (i = 0; i < 10; i++)
+    {
+      array[i] = i;
+      g_hash_table_insert (hash_table, &array[i], &array[i]);
+    }
+
+  iter = g_hash_table_iterate_keys (hash_table);
+
+  i = 0;
+  while (g_iterator_has_more (iter))
+      i += *((guint8 *) g_iterator_get_next (iter));
+      
+  if (i != (0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9))
+    g_print ("bad\n");
+  
+  g_iterator_free (iter);
+  
+  g_hash_table_destroy (hash_table);
+
+  g_print ("ok\n");
+
+
   g_print ("checking string chunks...");
 
   string_chunk = g_string_chunk_new (1024);
diff -x CVS -uNr glib.funcs/gobject/Makefile.am glib.gvalue/gobject/Makefile.am
--- glib.funcs/gobject/Makefile.am	Thu May 30 18:51:56 2002
+++ glib.gvalue/gobject/Makefile.am	Thu May 30 18:52:36 2002
@@ -77,6 +77,7 @@
 	gvalue.h		\
 	gvaluearray.h		\
 	gvaluecollector.h	\
+	gvalueiterator.h	\
 	gvaluetypes.h		\
 	gobjectnotifyqueue.c	\
 @STRIP_END@
@@ -98,6 +99,7 @@
 	gtypeplugin.c		\
 	gvalue.c		\
 	gvaluearray.c		\
+	gvalueiterator.c	\
 	gvaluetransform.c	\
 	gvaluetypes.c		\
 @STRIP_END@
@@ -191,18 +193,20 @@
 #
 bin_PROGRAMS = gobject-query glib-genmarshal
 bin_SCRIPTS = glib-mkenums
-noinst_PROGRAMS =  testgobject testoverride
+noinst_PROGRAMS =  testgobject testoverride testgvalueiterator
 # source files
 gobject_query_SOURCES = gobject-query.c
 glib_genmarshal_SOURCES = glib-genmarshal.c
 testgobject_SOURCES = testgobject.c
 testoverride_SOURCES = testoverride.c
+testgvalueiterator_SOURCES = testgvalueiterator.c
 # link programs against libgobject
 progs_LDADD = ./libgobject-2.0.la $(libglib)
 glib_genmarshal_LDADD = $(libglib)
 gobject_query_LDADD = $(progs_LDADD)
 testgobject_LDADD = $(progs_LDADD)
 testoverride_LDADD = $(progs_LDADD)
+testgvalueiterator_LDADD = $(progs_LDADD)
 
 #
 # manual pages to install
diff -x CVS -uNr glib.funcs/gobject/gvalueiterator.c glib.gvalue/gobject/gvalueiterator.c
--- glib.funcs/gobject/gvalueiterator.c	Wed Dec 31 16:00:00 1969
+++ glib.gvalue/gobject/gvalueiterator.c	Tue Feb  5 23:22:17 2002
@@ -0,0 +1,395 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * 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.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GLib Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+/* 
+ * MT safe - The implementor of the has_more/get_next functions is
+ *           responsible for the true MT safety of a GIterator.
+ */
+
+#include "glib.h"
+#include "gvalue.h"
+#include "gvaluetypes.h"
+#include "genums.h"
+#include "gparam.h"
+#include "gboxed.h"
+#include "gobject.h"
+#include "gvalueiterator.h"
+
+
+
+/* Typedefs
+ */
+typedef struct _GIterCtxt GIterCtxt;
+
+
+
+/* Structures
+ */
+struct _GValueIterator
+{
+  gpointer context;
+  GIteratorFunc         has_more_func;
+  GValueIteratorFunc    get_next_func;
+  GDestroyNotify        notify_func;
+};
+
+struct _GIterCtxt
+{
+    GType               g_type;
+    GValueSetFunc       set_func;
+    GIterator          *iter;
+};
+
+
+
+/* Value Iterators
+ */
+
+/**
+ * g_value_iterator_new:
+ * @context: A context for the IteratorFuncs to work with.
+ * @has_more_func: A function that returns TRUE if the iterator has more items.
+ * @get_next_func: A function that sets the #GValue to the next item.
+ * @notify_func: An optional destroy function to clean up the context upon g_value_iterator_free().
+ *
+ * Creates a new iterator object with the specified context and
+ * functions.  Iterator users should never see this, it is called by
+ * the implementor of the iterator.
+ *
+ * Return value: A new #GValueIterator.
+ */
+GValueIterator*
+g_value_iterator_new (gpointer           context,
+                      GIteratorFunc      has_more_func,
+                      GValueIteratorFunc get_next_func,
+                      GDestroyNotify     notify_func)
+{
+  GValueIterator *iter;
+
+  iter = g_new (GValueIterator, 1);
+  iter->context = context;
+  iter->has_more_func = has_more_func;
+  iter->get_next_func = get_next_func;
+  iter->notify_func = notify_func;
+
+  return iter;
+}  /* g_value_iterator_new() */
+
+/**
+ * g_value_iterator_new_empty:
+ *
+ * Creates an iterator that always returns FALSE from the has_more_func.
+ *
+ * Return value: A new #GValueIterator.
+ */
+GValueIterator*
+g_value_iterator_new_empty (void)
+{
+    return g_value_iterator_new_from_g_iterator (g_iterator_new_empty (),
+                                                 G_TYPE_POINTER);
+}  /* g_value_iterator_new_empty() */
+
+/**
+ * g_value_iterator_has_more:
+ * @iter: The #GValueIterator to operate on.
+ *
+ * Query the #GValueIterator to see if there are more items.  This
+ * merely calls the implementor's has_more_func.
+ *
+ * Return value: TRUE if the #GValueIterator has more items.
+ */
+gboolean
+g_value_iterator_has_more (GValueIterator *iter)
+{
+  gpointer result;
+
+  g_return_val_if_fail (iter != NULL, FALSE);
+
+  if (*iter->has_more_func != NULL)
+    result = (*iter->has_more_func) (iter->context);
+  else
+    result = (gpointer) FALSE;
+
+  return (gboolean) result;
+}  /* g_value_iterator_has_more() */
+
+/**
+ * g_iterator_get_next:
+ * @iter: The #GValueIterator to operate on.
+ * @value: The #GValue to fill with the next item.
+ *
+ * Retrieve the next item in the #GValueIterator and place it in the
+ * provided GValue.  This is undefined if has_more is FALSE.  This
+ * merely calls the implementor's get_next_func.
+ * Note that the context pointer is not modifiable by the get_next_func.
+ */
+void
+g_value_iterator_get_next (GValueIterator *iter,
+                           GValue    *value)
+{
+  g_return_if_fail (iter != NULL);
+  g_return_if_fail (*iter->get_next_func != NULL);
+
+  if (G_IS_VALUE (value))
+    g_value_unset(value);
+  (*iter->get_next_func) (iter->context, value);
+}  /* g_value_iterator_get_next() */
+
+/**
+ * g_value_iterator_free:
+ * @iter: The #GValueIterator to free.
+ *
+ * Frees the #GValueIterator, calling the notify_func if it exists.
+ */
+void
+g_value_iterator_free (GValueIterator *iter)
+{
+  g_return_if_fail(iter != NULL);
+
+  if (*iter->notify_func != NULL)
+    (*iter->notify_func) (iter->context);
+
+  g_free(iter);
+}  /* g_value_iterator_free() */
+
+static gpointer
+g_value_iterator_g_iterator_has_more (gpointer context)
+{
+  GIterCtxt *ctxt;
+
+  g_return_val_if_fail (context != NULL, FALSE);
+
+  ctxt = (GIterCtxt *) context;
+
+  return (gpointer) g_iterator_has_more (ctxt->iter);
+}
+
+static void
+g_value_iterator_g_iterator_get_next (gpointer  context,
+                                      GValue   *value)
+{
+  GIterCtxt *ctxt;
+  gpointer result;
+
+  g_return_if_fail (context != NULL);
+  g_return_if_fail (value != NULL);
+
+  ctxt = (GIterCtxt *) context;
+
+  result = g_iterator_get_next (ctxt->iter);
+
+  g_value_init (value, ctxt->g_type);
+  (*ctxt->set_func) (value, result);
+}
+
+static void
+g_value_iterator_g_iterator_destroy_notify (gpointer context)
+{
+  GIterCtxt *ctxt;
+
+  g_return_if_fail (context != NULL);
+
+  ctxt = (GIterCtxt *) context;
+
+  g_iterator_free (ctxt->iter);
+  g_free(ctxt);
+}
+
+/**
+ * g_value_iterator_new_from_g_iterator:
+ * @iter: The #GIterator to use as a base.
+ * @type: The #GType for the returned values.
+ *
+ * Creates a new iterator object.  This object will retrieve its
+ * values from the passed #GIterator.  When it fills #GValue structures,
+ * it will use the passed #GType.
+ * 
+ * This convenience wrapper around
+ * g_value_iterator_new_from_g_iterator_full() guesses the proper
+ * bits to use based on the passed #GType.  This means the #GType has
+ * to be a fundamental type.  To create a #GValueIterator from a
+ * #GIterator with complex types, use
+ * g_value_iterator_new_from_g_iterator_full().
+ *
+ * In addition, it is worth noting that #GIterators cannot hold types
+ * of G_TYPE_DOUBLE, G_TYPE_INT64, and the like on platforms where
+ * gpointer is not big enough.  It can, of course, hold pointers to
+ * those types.
+ *
+ * Return value: A new #GValueIterator.
+ */
+GValueIterator*
+g_value_iterator_new_from_g_iterator(GIterator     *iter,
+                                     GType          g_type)
+{
+  GValueIterator *v_iter;
+  GValueSetFunc   set_func = NULL;
+
+  g_return_val_if_fail(G_TYPE_IS_VALUE (g_type),
+                       g_value_iterator_new_empty ());
+
+  /* If you have non-fundamental types, use
+   * g_value_iterator_new_from_g_iterator_full() and provide
+   * a set_func
+   */
+  g_return_val_if_fail(G_TYPE_IS_FUNDAMENTAL (g_type),
+                       g_value_iterator_new_empty ());
+
+  /* Types that aren't supported really */
+  g_return_val_if_fail((G_TYPE_FUNDAMENTAL (g_type) !=
+                        G_TYPE_INVALID) &&
+                       (G_TYPE_FUNDAMENTAL (g_type) !=
+                        G_TYPE_NONE) &&
+                       (G_TYPE_FUNDAMENTAL (g_type) !=
+                        G_TYPE_INTERFACE),
+                       g_value_iterator_new_empty ());
+
+  /* Types that can't fit in a GIterator gpointer */
+  g_return_val_if_fail((G_TYPE_FUNDAMENTAL (g_type) !=
+                        G_TYPE_INT64) &&
+                       (G_TYPE_FUNDAMENTAL (g_type) !=
+                        G_TYPE_UINT64) &&
+                       (G_TYPE_FUNDAMENTAL (g_type) !=
+                        G_TYPE_FLOAT) &&
+                       (G_TYPE_FUNDAMENTAL (g_type) !=
+                        G_TYPE_DOUBLE),
+                       g_value_iterator_new_empty ());
+
+  switch (g_type)
+  {
+      case G_TYPE_CHAR:
+          set_func = G_VALUE_SET_FUNC(g_value_set_char);
+          break;
+
+      case G_TYPE_UCHAR:
+          set_func = G_VALUE_SET_FUNC(g_value_set_uchar);
+          break;
+
+      case G_TYPE_BOOLEAN:
+          set_func = G_VALUE_SET_FUNC(g_value_set_boolean);
+          break;
+
+      case G_TYPE_INT:
+          set_func = G_VALUE_SET_FUNC(g_value_set_int);
+          break;
+
+      case G_TYPE_UINT:
+          set_func = G_VALUE_SET_FUNC(g_value_set_uint);
+          break;
+
+      case G_TYPE_LONG:
+          set_func = G_VALUE_SET_FUNC(g_value_set_long);
+          break;
+
+      case G_TYPE_ULONG:
+          set_func = G_VALUE_SET_FUNC(g_value_set_ulong);
+          break;
+
+      case G_TYPE_ENUM:
+          set_func = G_VALUE_SET_FUNC(g_value_set_enum);
+          break;
+
+      case G_TYPE_FLAGS:
+          set_func = G_VALUE_SET_FUNC(g_value_set_flags);
+          break;
+
+      case G_TYPE_STRING:
+          set_func = G_VALUE_SET_FUNC(g_value_set_string);
+          break;
+
+      case G_TYPE_POINTER:
+          set_func = G_VALUE_SET_FUNC(g_value_set_pointer);
+          break;
+
+      case G_TYPE_BOXED:
+          set_func = G_VALUE_SET_FUNC(g_value_set_boxed);
+          break;
+
+      case G_TYPE_PARAM:
+          set_func = G_VALUE_SET_FUNC(g_value_set_param);
+          break;
+
+      case G_TYPE_OBJECT:
+          set_func = G_VALUE_SET_FUNC(g_value_set_object);
+          break;
+
+
+      default:
+          g_assert_not_reached();
+          break;
+  }
+
+  v_iter = g_value_iterator_new_from_g_iterator_full (iter,
+                                                      g_type,
+                                                      set_func);
+  return v_iter;
+}
+
+/**
+ * g_value_iterator_new_from_g_iterator_full:
+ * @iter: The #GIterator to use as a base.
+ * @type: The #GType for the returned values.
+ * @set_fun: The function to set #GValues.
+ *
+ * Creates a new iterator object.  This object will retrieve its
+ * values from the passed #GIterator.  When it fills #GValue structures,
+ * it will use the passed #GType.  It sets the #GValue function with
+ * the given #GValueSetFunc.
+ * 
+ * It is worth noting that #GIterators cannot hold types
+ * of G_TYPE_DOUBLE, G_TYPE_INT64, and the like on platforms where
+ * gpointer is not big enough.  It can, of course, hold pointers to
+ * those types.
+ *
+ * Return value: A new #GValueIterator.
+ */
+GValueIterator*
+g_value_iterator_new_from_g_iterator_full (GIterator     *iter,
+                                           GType          g_type,
+                                           GValueSetFunc  set_func)
+{
+    GIterCtxt *ctxt;
+    GValueIterator *v_iter;
+
+    g_return_val_if_fail(G_TYPE_IS_VALUE_TYPE (g_type),
+                         g_value_iterator_new_empty ());
+
+    ctxt = g_new0(GIterCtxt, 1);
+    ctxt->g_type = g_type;
+    ctxt->set_func = set_func ? set_func : g_value_set_pointer;
+
+    if (!iter)
+      ctxt->iter = g_iterator_new_empty();
+    else
+      ctxt->iter = iter;
+
+    v_iter = g_value_iterator_new (ctxt,
+                                   g_value_iterator_g_iterator_has_more,
+                                   g_value_iterator_g_iterator_get_next,
+                                   g_value_iterator_g_iterator_destroy_notify);
+
+    return v_iter;
+}
diff -x CVS -uNr glib.funcs/gobject/gvalueiterator.h glib.gvalue/gobject/gvalueiterator.h
--- glib.funcs/gobject/gvalueiterator.h	Wed Dec 31 16:00:00 1969
+++ glib.gvalue/gobject/gvalueiterator.h	Tue Feb  5 23:08:45 2002
@@ -0,0 +1,71 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * 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.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GLib Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+#ifndef __G_VALUE_ITERATOR_H__
+#define __G_VALUE_ITERATOR_H__
+
+#include <glib/gtypes.h>
+#include <glib/giterator.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GValueIterator	         GValueIterator;
+
+typedef void (*GValueIteratorFunc)      (gpointer  context,
+                                         GValue   *value);
+typedef void (*GValueSetFunc)           (GValue   *value,
+                                         gpointer  v_val);
+
+#define G_VALUE_SET_FUNC(v_func)        ((GValueSetFunc)(v_func))
+
+/* GIterator
+ */
+GValueIterator* g_value_iterator_new       (gpointer           context,
+                                            GIteratorFunc      has_more_func,
+                                            GValueIteratorFunc get_next_func,
+                                            GDestroyNotify     notify_func);
+
+GValueIterator* g_value_iterator_new_empty (void);
+
+GValueIterator* g_value_iterator_new_from_g_iterator (GIterator *iter,
+                                                      GType      g_type);
+
+GValueIterator* g_value_iterator_new_from_g_iterator_full (GIterator    *iter,
+                                                           GType         g_type,
+                                                           GValueSetFunc set_func);
+
+gboolean        g_value_iterator_has_more  (GValueIterator      *iter);
+
+void            g_value_iterator_get_next  (GValueIterator      *iter,
+                                            GValue              *value);
+
+void	        g_value_iterator_free	   (GValueIterator      *iter);
+
+
+G_END_DECLS
+
+#endif /* __G_VALUE_STRING_H__ */
+
diff -x CVS -uNr glib.funcs/gobject/testgvalueiterator.c glib.gvalue/gobject/testgvalueiterator.c
--- glib.funcs/gobject/testgvalueiterator.c	Wed Dec 31 16:00:00 1969
+++ glib.gvalue/gobject/testgvalueiterator.c	Fri May 31 09:43:10 2002
@@ -0,0 +1,83 @@
+#include <stdio.h>
+
+#include "glib.h"
+#include "gvalue.h"
+#include "gvaluetypes.h"
+#include "gvalueiterator.h"
+
+
+static void dbl_set(GValue *val, gpointer v_val)
+{
+    g_value_set_double(val, *(gdouble *)v_val);
+}
+
+
+int main(int argc, char *argv[])
+{
+    GList *list;
+    gdouble dbls[] = {1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9};
+    GIterator *iter;
+    GValueIterator *v_iter;
+    GValue val = {0, };
+    gint i;
+    gchar c;
+
+    g_type_init();
+
+    for (i = 'a', list = NULL; i < 'j'; i++)
+        list = g_list_append(list, (gpointer)i);
+
+    iter = g_list_iterate(list);
+    fprintf(stderr, "List iterate... ");
+    while (g_iterator_has_more(iter))
+    {
+        c = (gchar)g_iterator_get_next(iter);
+        fprintf(stderr, "%c ", c);
+    }
+    g_iterator_free(iter);
+    fprintf(stderr, "done\n");
+
+    iter = g_list_iterate(list);
+    v_iter = g_value_iterator_new_from_g_iterator(iter, G_TYPE_CHAR);
+    fprintf(stderr, "Value iterate... ");
+    while (g_value_iterator_has_more(v_iter))
+    {
+        g_value_iterator_get_next(v_iter, &val);
+        if (G_VALUE_TYPE(&val) != G_TYPE_CHAR)
+        {
+            fprintf(stderr, "Error: Value is of type %s\n",
+                    G_VALUE_TYPE_NAME(&val));
+            break;
+        }
+        fprintf(stderr, "%c ", g_value_get_char(&val));
+    }
+    g_value_iterator_free(v_iter);
+    fprintf(stderr, "done\n");
+
+    g_list_free(list);
+
+    for (i = 0, list = NULL; i < (sizeof(dbls) / sizeof(dbls[0])); i++)
+        list = g_list_append(list, &(dbls[i]));
+
+    iter = g_list_iterate(list);
+    fprintf(stderr, "Value iterate full... ");
+    v_iter = g_value_iterator_new_from_g_iterator_full(iter,
+                                                       G_TYPE_DOUBLE,
+                                                       dbl_set);
+    while (g_value_iterator_has_more(v_iter))
+    {
+        g_value_iterator_get_next(v_iter, &val);
+        if (G_VALUE_TYPE(&val) != G_TYPE_DOUBLE)
+        {
+            fprintf(stderr, "Error: Value is of type %s\n",
+                    G_VALUE_TYPE_NAME(&val));
+            break;
+        }
+        fprintf(stderr, "%1.1f ", g_value_get_double(&val));
+    }
+    g_value_iterator_free(v_iter);
+    fprintf(stderr, "done\n");
+
+    return(0);
+}
+


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