sbz's blog

Technical writings and recipes on UNIX, software and systems

Elegant jump table in C programming

Posted at — Jul 1, 2008

At Wallix, I’m writing C and Python code most of my time. I encountered a situation where I wanted to make my code looks cleaner without less conditionals if statements.

I thought we should change the logic and use either the Jump table or Dispatch table concepts to achieve this goal on the code.

The ugly code was related to libssh2 C library and the following:

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <libssh2.h>

unsigned long 
ugly_get_flags(const char *mode) {
    unsigned long flags = 0;

    if (strchr(mode, 'a')) {
        printf("append\n");
        flags |= 0x00000004;
    }

    if (strchr(mode, 'w')) {
        printf("write|trunc|creat\n");
        flags |= 0x00000002 | 0x00000010 | 0x00000008;
    }

    if (strchr(mode, 'r')) {
        printf("read\n");
        flags |= 0x00000001;
    }
    
    if (strchr(mode, '+')) {
        printf("read|write\n");
        flags |= 0x00000001 | 0x00000002;
    }

    if (strchr(mode, 'x')) {
        printf("write|trunc|excl|creat\n");
        flags |= 0x00000002 | 0x00000010 | 0x00000020 | 0x00000008;
    }
}

That function return the current flags bits value as long for the given mode passed as input argument.

As you can see, it seems very repetitive and we might be able to have a cleaner version. Also here I kept the printf statement that I used to debug :)

In C programming, we could commonly use a array of struct to create such indirection table. It’s a pattern that is very often used in the BSD code in order to threat commands and arguments parsing. The new version looks like above:

unsigned long 
nice_get_flags(const char *mode) {
    int i=0;
    unsigned long f=0;
    
    struct {
        char mode;
        unsigned long flags;
        char *capabilities;
    } modeflags[5] = {
        {'a', 0x00000004, "append"},
        {'w', 0x00000002|0x00000010|0x00000008, "write|trunc|creat"},
        {'r', 0x00000001, "read"},
        {'+', 0x00000001|0x00000002, "read|write"},
        {'x', 0x00000002|0x00000010|0x00000020|0x00000008, "write|trunc|excl|creat"}
    };

    for(i=0; i<5; i++) {
        if(strchr(mode, modeflags[i].mode)) {
            printf("%s\n",modeflags[i].capabilities);
            f |= modeflags[i].flags;
        }
    }

    return f;
}

It’s shorter, more elegeant, very extensible and embrace DRY principle. Moreover, we would just need to have a new element in our array of struct to add a new mode.

Finally, the test as demonstrated that the result was 100% identical while executed into main()

int
main(int argc, char *argv[]) {
    if (argc!=2) {
        printf("Usage %s: %s\n", argv[0], argv[1]);
        exit(EXIT_FAILURE);
    }

    ugly_get_flags(argv[1]);
    nice_get_flags(argv[1]);

    exit(EXIT_SUCCESS);
}

Clean code is often shorter and a better pattern to adopt while programming.

Always remember Keep it simple, stupid and Unix Philosophy principles.

The Unix philosophy emphasizes building simple, short, clear, modular, and extensible code that can be easily maintained and repurposed by developers other than its creators. The Unix philosophy favors composability as opposed to monolithic design.