HiveBrain v1.2.0
Get Started
← Back to all entries
patterncMinor

Creating a common interface

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
creatinginterfacecommon

Problem

I have a sort of database with a many-to-many association between tags and files. For various reasons, I decided to forgo a junction table in favor of having the left and right tables store the associated values in the tables themselves. However I've ended up writing code like this:

GHashTable *tagdb_get_tag_files (tagdb *db, int tag_code)
{
    return g_hash_table_lookup(db->reverse, GINT_TO_POINTER(tag_code));
}

GHashTable *tagdb_get_file_tags (tagdb *db, int file_id)
{
    return g_hash_table_lookup(db->forward, GINT_TO_POINTER(file_id));
}

void tagdb_remove_tag (tagdb *db, int id)
{
    GHashTable *its_files = g_hash_table_lookup(db->reverse, GINT_TO_POINTER(id));
    if (its_files == NULL)
    {
        return;
    }

    GHashTableIter it;
    gpointer key, value;
    g_hash_table_iter_init(&it, its_files);
    if (g_hash_table_iter_next(&it, &key, &value))
    {
        tagdb_remove_tag_from_file(db, id, key);
    }
    g_hash_table_remove(db->reverse, GINT_TO_POINTER(id));
}

void tagdb_remove_file(tagdb *db, int id)
{
    GHashTable *its_tags = g_hash_table_lookup(db->forward, GINT_TO_POINTER(id));
    if (its_tags == NULL)
    {
        return;
    }

    GHashTableIter it;
    gpointer key, value;
    g_hash_table_iter_init(&it, its_tags);
    if (g_hash_table_iter_next(&it, &key, &value))
    {
        tagdb_remove_file_from_tag(db, id, key);
    }
    g_hash_table_remove(db->forward, GINT_TO_POINTER(id));
}


Where only a few identifiers change, but basically the same thing happens, just on different sides. Is there a simpler, less error prone way to do this in c? Maybe with macros?

Solution

You can pass a function that gets the correct object from tagdb.

AKA Service Locator pattern

typedef HashTable* (*ITEM_GETTER)(tagdb*);

HashTable* getTag(tagdb *db)  { return db->reverse;}
HashTable* getFile(tagdb *db) { return db->forward;}

GHashTable *tagdb_get_item_files (tagdb *db, int itemId, ITEM_GETTER getter)
{
    return g_hash_table_lookup(getter(db), GINT_TO_POINTER(itemId));
}

void tagdb_remove_item (tagdb *db, int id, ITEM_GETTER getter)
{
    GHashTable *its_files = g_hash_table_lookup(getter(db), GINT_TO_POINTER(id));
    if (its_files == NULL)
    {
        return;
    }

    GHashTableIter it;
    gpointer key, value;
    g_hash_table_iter_init(&it, its_files);
    if (g_hash_table_iter_next(&it, &key, &value))
    {
        tagdb_remove_tag_from_file(db, id, key);
    }
    g_hash_table_remove(getter(db), GINT_TO_POINTER(id));
}


Edit for clarity:

The old functions can now be implanted like this:

GHashTable *tagdb_get_tag_files (tagdb *db, int tag_code)
{
    return tagdb_get_item_files(db, tag_code, getTag);
}

GHashTable *tagdb_get_file_tags (tagdb *db, int file_id)
{
    return return tagdb_get_item_files(db, file_id, getFile);
}

void tagdb_remove_tag (tagdb *db, int id)
{
    tagdb_remove_item(db, id, getTag);
}

void tagdb_remove_file(tagdb *db, int id)
{
    tagdb_remove_item(db, id, getFile);
}

Code Snippets

typedef HashTable* (*ITEM_GETTER)(tagdb*);

HashTable* getTag(tagdb *db)  { return db->reverse;}
HashTable* getFile(tagdb *db) { return db->forward;}

GHashTable *tagdb_get_item_files (tagdb *db, int itemId, ITEM_GETTER getter)
{
    return g_hash_table_lookup(getter(db), GINT_TO_POINTER(itemId));
}

void tagdb_remove_item (tagdb *db, int id, ITEM_GETTER getter)
{
    GHashTable *its_files = g_hash_table_lookup(getter(db), GINT_TO_POINTER(id));
    if (its_files == NULL)
    {
        return;
    }

    GHashTableIter it;
    gpointer key, value;
    g_hash_table_iter_init(&it, its_files);
    if (g_hash_table_iter_next(&it, &key, &value))
    {
        tagdb_remove_tag_from_file(db, id, key);
    }
    g_hash_table_remove(getter(db), GINT_TO_POINTER(id));
}
GHashTable *tagdb_get_tag_files (tagdb *db, int tag_code)
{
    return tagdb_get_item_files(db, tag_code, getTag);
}

GHashTable *tagdb_get_file_tags (tagdb *db, int file_id)
{
    return return tagdb_get_item_files(db, file_id, getFile);
}

void tagdb_remove_tag (tagdb *db, int id)
{
    tagdb_remove_item(db, id, getTag);
}

void tagdb_remove_file(tagdb *db, int id)
{
    tagdb_remove_item(db, id, getFile);
}

Context

StackExchange Code Review Q#9681, answer score: 3

Revisions (0)

No revisions yet.