How many times have you ever legitimately used the comma operator in live C or C++ code? I’ve seen a Boost project use it as convenience notation for small compile-time datasets, but that’s about it. So, here’s an example of an absolutely terrible way to use it (yes… this is how I blow off steam at work):

wtf.c

#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
    return argc > 1 ? !strcmp(argv[1], "foo") ? \
        (puts("foo"),0) : (puts("not foo"),1) : 1;
}

This program prints “foo”, “not foo”, or nothing based on the first argument; then it returns true (UNIX-style) only if the first argument was “foo”. While most language experts will not bat an eye at this, it’s definitely on the Perl side of ugly.

operator, in action

Why was I playing with the comma operator? I was looking at the definition of assert() provided on my system (either part of libc or gcc, I’m not sure). If you disable assertions (-DNDEBUG) you’ll see a null statement like this:

test.c

#include <assert.h>
void test(int x, int y)
{
    assert(x < y);
}

gcc test.c -E -DNDEBUG

void test(int x, int y)
{
    ((void)(0));
}

If you leave assertions enabled, however, you’ll still see a null statement due to the somewhat-creepy comma-operator magic I’ve demonstrated above:

gcc test.c -E

void test(int x, int y)
{
    ((void)((x < y) ? 0 : \
        (__assert_fail("x < y", "test.c", 4, "test"), 0)));
}

So, assert is defined as an expression rather than a statement. That means that you can combine it with other chunks of code in some surprising fashions:

char *short_strdup(const char *str, size_t len)
{
    // don't allow anyone to pass in a null or over-long string
    return assert(str != NULL), assert(strlen(str) < len), strdup(str);
}

long infallible_atoi(const char *number)
{
    char *end;
    long value = strtol(number, &end, 10);
    // validate that there was no conversion error
    //          and that we consumed all input
    return assert(errno == 0), assert(*end == '\0'), value;
}

This is not recommended, however, because assert is not a general-purpose error-handling mechanism. I could imagine this being used as the basis for a hand-rolled assertion mechanisms in a large codebase (you could throw an exception, log an error with a stack trace, or cause monkeys to fly out of the original developer’s nose, for example).

About these ads