0

Our C codebase is littered with pattern-like code that performs the same iteration, NULL handling, etc. for any "list append" operation on structs which have a .next pointer of the same type as themselves:

struct foo {
    int bar;
    char *baz;
    struct foo *next;
}

Example pattern-code:

struct foo *foo_list;

int func(struct foo *f) {
    struct foo *it;

    ...

    if (!foo_list) {
        foo_list = f;
    } else {
        for (it = foo_list; it->next; it = it->next)
            {}
        it->next = f;
    }

     ...
}

I'm trying to come up with a macro that would simplify the append process for any struct with such a .next member. Keep in mind that addition may also be done to a NULL list, so the macro must also return the new list head:

#define add_last(what, where) \
    ({ \
        if (!(where)) { \
            (what); \
        } else { \
            typeof(where) __wit = (where); \
            while (__wit->next) \
                __wit = __wit->next; \
            __wit->next = (what); \
            (where); \
        }})

However, when using it like this:

return add_last(filter, filters);

, gcc isn't too happy, and spews:

cachedb/../db/../ut.h:104:4: warning: statement with no effect [-Wunused-value] (what); \ ^ cachedb/cachedb.c:797:9: note: in expansion of macro ‘add_last’ return add_last(new, existing); ^ cachedb/../db/../ut.h:110:4: warning: statement with no effect [-Wunused-value] (where); \ ^ cachedb/cachedb.c:797:9: note: in expansion of macro ‘add_last’ return add_last(new, existing); ^ cachedb/../db/../ut.h:103:2: error: void value not ignored as it ought to be ({ if (!(where)) { \ ^ cachedb/cachedb.c:797:9: note: in expansion of macro ‘add_last’ return add_last(new, existing); ^

I've tried with a do while (0) block, no luck either. Is what I'm trying even possible? Thank you!

Liviu Chircu
  • 1,000
  • 3
  • 11
  • 24

1 Answers1

3

I slightly changed the internal behavior of the macro. Not only does it work now, but it fits the codebase better:

#define add_last(what, where) \
    do { \
        if (!(where)) { \
            (where) = (what); \
        } else { \
            typeof(where) __wit = (where); \
            while (__wit->next) \
                __wit = __wit->next; \
            __wit->next = (what); \
        } \
    } while (0)

So instead of using it like below, which does an extraneous write operation in most of the cases:

filters = add_last(filter, filters);

, you now simply use it like:

add_last(filter, filters);

And the original return code becomes:

add_last(filter, filters);
return filters;

Still, any hints as to why the original solution threw those warnings would be helpful. Those are not "statements with no effect" IMHO - they are useful as block return values.

FINAL ANSWER: In order for a C block to return a value, the value must only be placed within a simple, colon-terminated expression (full explanation), otherwise the block will return void. So our original macro idea (which I will no longer use anyway) could have only worked if tweaked as follows:

#define add_last(what, where) \
    do { \
        typeof(where) __wit; \
        if (!(where)) { \
            __wit = (what); \
        } else { \
            __wit = (where); \
            while (__wit->next) \
                __wit = __wit->next; \
            __wit->next = (what); \
            __wit = (where); \
        } \
        __wit; \
    } while (0)

This would finally make gcc happy :)

Liviu Chircu
  • 1,000
  • 3
  • 11
  • 24
  • For hints please show a [MCVE]. – Jabberwocky Feb 16 '18 at 13:49
  • 1
    How exactly were they useful? `if(!(filters)) (filter);` Of course `filter` is an expression so it is a valid statement yet it had no effect, it is merely a variable name. – bipll Feb 16 '18 at 14:00
  • But isn't the `return` using it? That's the part I don't understand. I had the impression that C blocks return the value of their last statement -- in our case (the 1st macro), this is something useful! – Liviu Chircu Feb 16 '18 at 14:01
  • see https://stackoverflow.com/a/1635562/2054305 - exactly what I was trying to do – Liviu Chircu Feb 16 '18 at 14:07