cpreprocessor-directive

Avoid duplication of code using C token pasters


This is what my #define macro looks like:

#define CUSTOM_COMPARATOR(table, column, a, b)                          \
    do {                                                                \
        const struct db_##table *row1 = a;                              \
        const struct db_##table *row2 = b;                              \
                                                                        \
        /* Insert lot of code here with multiple row->##column usage */ \
        return strcmp(row1->##column->name1, row2->##column->name2);    \
    } while (0)

I have to generate multiple definitions based on different table and column types. These definitions are preprocessed and called in multiple functions:

static int
db_table_sometable_somecolumn_comparator(const void *a, const void *b) {
    CUSTOM_COMPARATOR(sometable, somecolumn, a, b);
}

static int
db_table_someothertable_someothercolumn_comparator(const void *a, const void *b) {
    CUSTOM_COMPARATOR(someothertable, someothercolumn, a, b);
}

Basically I want to avoid the duplication of code inside CUSTOM_COMPARATOR. When I compile this gcc complains that pasting "->" and "somecolumn" does not give a valid preprocessing token which is understandable since -> breaks the token. It works on db_##table because it produces a single token.

Nonetheless, is there any way I can achieve this? I have like 10s of table/columns with just name change but share the same logic mentioned in CUSTOM_COMPARATOR which is also 50 LoC in real.


Solution

  • Don't try to paste the column name to make a new token. You can simply do this:

            return strcmp(row1->column->name1, row2->column->name2);
    

    This is accepted by the preprocessor, and expands to, e.g. with gcc -E:

    static int
    db_table_sometable_somecolumn_comparator(const void *a, const void *b) {
        do { const struct db_sometable *row1 = a; const struct db_sometable *row2 = b; return strcmp(row1->somecolumn->name1, row2->somecolumn->name2); } while (0);
    }
    
    static int
    db_table_someothertable_someothercolumn_comparator(const void *a, const void *b) {
        do { const struct db_someothertable *row1 = a; const struct db_someothertable *row2 = b; return strcmp(row1->someothercolumn->name1, row2->someothercolumn->name2); } while (0);
    }
    
    

    which is perfectly valid C.