Learning GLib - Graph implementation (a draft)

I’m learning GLib, and for that I’m migrating a code of my own using just standard plain C to GLib (the code is a heuristics about paths in a graph). Right now I was able to write a few lines regarding a graph adjacency “list” structure, for that I have used a g_hash_table as a index for vertex/nodes and g_ptr_array to keep track of edges (aka adjancecy list).

The code is compiling and running ok, and proudly valgrind shows no leaks (some still reachable but not my fault ;-).

#include <glib.h>
#include <glib/gprintf.h>

struct graph
{
    GHashTable * nodes;         // A list of nodes in graph
};

struct node
{
    GPtrArray * neighbors;      // A list of all adjacent nodes
    gint weight;            // Node weight
    GString * label;        // Node label
};

struct neighbor
{
    struct node * dst;      // The node that is pointed too
    gint weight;            // Edge SRC->DST weight
};


/* Creation functions */
struct graph    * graph_new                     (void);
struct node     * graph_node_add                (struct graph       *graph, 
                                                 gchar              *label, 
                                                 gint                weight);

void              graph_edge_add                (struct graph       *graph, 
                                                 struct node        *src,
                                                 struct node        *dst,
                                                 gint                weight);

void              graph_edge_add_by_label       (struct graph       *graph, 
                                                 gchar              *src, 
                                                 gchar              *dst, 
                                                 gint                weight);

/* Manipulation functions */
struct node     * graph_node                    (struct graph       *graph, 
                                                 gchar              *label);

/* Print functions */
void              graph_print                   (struct graph       *graph);

void              graph_node_print              (gpointer            key, 
                                                 struct node        *value,
                                                 gpointer            user_data);

void              graph_neighbors_print         (struct neighbor    *n,
                                                 gpointer            user_data);

/* Free functions */
void              graph_free                    (struct graph       *g);

void              graph_node_neighbor_destroyed (gpointer            n);

void              graph_node_value_destroyed    (gpointer            value);


void
graph_free (struct graph *g)
{
    g_hash_table_remove_all(g->nodes);
    g_hash_table_destroy(g->nodes);
    g_free(g);
}


void
graph_node_value_destroyed (gpointer value)
{
    if (((struct node*)value)->neighbors) 
        g_ptr_array_unref (((struct node*)value)->neighbors);
    g_string_free (((struct node*)value)->label, TRUE);
    g_free ((struct node*)value);
}


void 
graph_node_neighbor_destroyed(gpointer n)
{
    g_free(n);
}


struct graph *
graph_new(void)
{
    struct graph * g = g_new(struct graph, 1);
    g->nodes = g_hash_table_new_full(
            g_str_hash, g_str_equal,
            NULL, (GDestroyNotify)graph_node_value_destroyed);
    return g;
}


struct node *
graph_node_add(struct graph *graph,
               gchar        *label,
               gint          weight)
{
    struct node * n = g_new(struct node, 1);
    n->neighbors = g_ptr_array_new_full(1, (GDestroyNotify)graph_node_neighbor_destroyed);
    n->weight = weight;
    n->label = g_string_new(label);
    g_hash_table_insert(graph->nodes, label, n);
    return n;
}


void
graph_edge_add(struct graph *graph, 
               struct node  *src,
               struct node  *dst, 
               gint          weight)
{
    struct neighbor * n = g_new(struct neighbor, 1);
    n->dst = dst;
    n->weight = weight;
    g_ptr_array_add(src->neighbors, n);
}


struct node *
graph_node(struct graph *graph,
           gchar        *label)
{
    return g_hash_table_lookup(graph->nodes, label);
}


void 
graph_edge_add_by_label(struct graph *graph,
                        gchar        *lsrc, 
                        gchar        *ldst, 
                        gint          weight)
{
    graph_edge_add(graph, 
                   graph_node(graph, lsrc), 
                   graph_node(graph, ldst), 
                   weight);
}


void 
graph_neighbors_print(struct neighbor  *n, 
                      gpointer          user_data)
{
    g_printf("%s(%d), ", n->dst->label->str, n->weight);
}


void
graph_node_print(gpointer       key,
                 struct node   *value, 
                 gpointer       user_data)
{
    g_printf("%s(%d) -> ", (gchar*)key, value->weight);
    g_ptr_array_foreach(value->neighbors, (GFunc)graph_neighbors_print, NULL);
    g_printf("\n");
}


void graph_print(struct graph *graph)
{
    g_hash_table_foreach(graph->nodes, (GHFunc)graph_node_print, NULL);
}

gint
main(gint   argc,
     gchar *argv[])
{
    struct graph *G = graph_new();
    struct node *a = graph_node_add(G, "a", 10);
    struct node *b = graph_node_add(G, "b", 20);
    struct node *c = graph_node_add(G, "c", 30);
    graph_edge_add(G, a, b, 2);
    graph_edge_add_by_label(G, "a", "c", 4);
    graph_edge_add(G, b, c, 6);
    graph_print(G);
    graph_free(G);
    return 0;
}

In the code above in function graph_node_value_destroyed, I handle every reference to gpointer data calling it with a cast, otherwise I would not be able to do the references to the struct elements. What alternatives do we have here to enchance code quality? I could think in other 2 methods the first one should be something like:

void graph_node_value_destroyed(gpointer value)
{
    struct node * node = value;  // with this I can call node without cast.
    if(node->neighbors) 
...

The other way is changing the prototype of my custom GDestroyNotify

void graph_node_value_destroyed(struct node * node)
{
    if(node->neighbors) 
...

But I think that will trigger issues in other places.

What is the recommended method for these situations?

Besides that, any improvements for the code listed above?

Hm,

int main (int argc, char *argv[])

I dont know if it is better.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.