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?