'Uses of C comma operator [duplicate]

You see it used in for loop statements, but it's legal syntax anywhere. What uses have you found for it elsewhere, if any?



Solution 1:[1]

I think generally C's comma is not a good style to use simply because it's so very very easy to miss - either by someone else trying to read/understand/fix your code, or you yourself a month down the line. Outside of variable declarations and for loops, of course, where it is idiomatic.

You can use it, for example, to pack multiple statements into a ternary operator (?:), ala:

int x = some_bool ? printf("WTF"), 5 : fprintf(stderr, "No, really, WTF"), 117;

but my gods, why?!? (I've seen it used in this way in real code, but don't have access to it to show unfortunately)

Solution 2:[2]

Two killer comma operator features in C++:

a) Read from stream until specific string is encountered (helps to keep the code DRY):

 while (cin >> str, str != "STOP") {
   //process str
 }

b) Write complex code in constructor initializers:

class X : public A {
  X() : A( (global_function(), global_result) ) {};
};

Solution 3:[3]

I've seen it used in macros where the macro is pretending to be a function and wants to return a value but needs to do some other work first. It's always ugly and often looks like a dangerous hack though.

Simplified example:

#define SomeMacro(A) ( DoWork(A), Permute(A) )

Here B=SomeMacro(A) "returns" the result of Permute(A) and assigns it to "B".

Solution 4:[4]

The Boost Assignment library is a good example of overloading the comma operator in a useful, readable way. For example:

using namespace boost::assign;

vector<int> v; 
v += 1,2,3,4,5,6,7,8,9;

Solution 5:[5]

I had to use a comma to debug mutex locks to put a message before the lock starts to wait.

I could not but the log message in the body of the derived lock constructor, so I had to put it in the arguments of the base class constructor using : baseclass( ( log( "message" ) , actual_arg )) in the initialization list. Note the extra parenthesis.

Here is an extract of the classes :

class NamedMutex : public boost::timed_mutex
{
public:
    ...

private:
    std::string name_ ;
};

void log( NamedMutex & ref__ , std::string const& name__ )
{
    LOG( name__ << " waits for " << ref__.name_ );
}

class NamedUniqueLock : public boost::unique_lock< NamedMutex >
{
public:

    NamedUniqueLock::NamedUniqueLock(
        NamedMutex & ref__ ,
        std::string const& name__ ,
        size_t const& nbmilliseconds )
    :
        boost::unique_lock< NamedMutex >( ( log( ref__ , name__ ) , ref__ ) ,
            boost::get_system_time() + boost::posix_time::milliseconds( nbmilliseconds ) ),
            ref_( ref__ ),
            name_( name__ )
    {
    }

  ....

};

Solution 6:[6]

From the C standard:

The left operand of a comma operator is evaluated as a void expression; there is a sequence point after its evaluation. Then the right operand is evaluated; the result has its type and value. (A comma operator does not yield an lvalue.)) If an attempt is made to modify the result of a comma operator or to access it after the next sequence point, the behavior is undefined.

In short it let you specify more than one expression where C expects only one. But in practice it's mostly used in for loops.

Note that:

int a, b, c;

is NOT the comma operator, it's a list of declarators.

Solution 7:[7]

It is sometimes used in macros, such as debug macros like this:

#define malloc(size) (printf("malloc(%d)\n", (int)(size)), malloc((size)))

(But look at this horrible failure, by yours truly, for what can happen when you overdo it.)

But unless you really need it, or you are sure that it makes the code more readable and maintainable, I would recommend against using the comma operator.

Solution 8:[8]

You can overload it (as long as this question has a "C++" tag). I have seen some code, where overloaded comma was used for generating matrices. Or vectors, I don't remember exactly. Isn't it pretty (although a little confusing):

MyVector foo = 2, 3, 4, 5, 6;

Solution 9:[9]

Outside of a for loop, and even there is has can have an aroma of code smell, the only place I've seen as a good use for the comma operator is as part of a delete:

 delete p, p = 0;

The only value over the alternative is you can accidently copy/paste only half of this operation if it is on two lines.

I also like it because if you do it out of habit, you'll never forget the zero assignment. (Of course, why p isn't inside somekind of auto_ptr, smart_ptr, shared_ptr, etc wrapper is a different question.)

Solution 10:[10]

Given @Nicolas Goy's citation from the standard, then it sounds like you could write one-liner for loops like:

int a, b, c;
for(a = 0, b = 10; c += 2*a+b, a <= b; a++, b--);
printf("%d", c);

But good God, man, do you really want to make your C code more obscure in this way?

Solution 11:[11]

It's very useful in adding some commentary into ASSERT macros:

ASSERT(("This value must be true.", x));

Since most assert style macros will output the entire text of their argument, this adds an extra bit of useful information into the assertion.

Solution 12:[12]

In general I avoid using the comma operator because it just makes code less readable. In almost all cases, it would be simpler and clearer to just make two statements. Like:

foo=bar*2, plugh=hoo+7;

offers no clear advantage over:

foo=bar*2;
plugh=hoo+7;

The one place besides loops where I have used it it in if/else constructs, like:

if (a==1)
... do something ...
else if (function_with_side_effects_including_setting_b(), b==2)
... do something that relies on the side effects ...

You could put the function before the IF, but if the function takes a long time to run, you might want to avoid doing it if it's not necessary, and if the function should not be done unless a!=1, then that's not an option. The alternative is to nest the IF's an extra layer. That's actually what I usually do because the above code is a little cryptic. But I've done it the comma way now and then because nesting is also cryptic.

Solution 13:[13]

I often use it to run a static initializer function in some cpp files, to avoid lazy initalization problems with classic singletons:

void* s_static_pointer = 0;

void init() {
    configureLib(); 
    s_static_pointer = calculateFancyStuff(x,y,z);
    regptr(s_static_pointer);
}

bool s_init = init(), true; // just run init() before anything else

Foo::Foo() {
  s_static_pointer->doStuff(); // works properly
}

Solution 14:[14]

For me the one really useful case with commas in C is using them to perform something conditionally.

  if (something) dothis(), dothat(), x++;

this is equivalent to

  if (something) { dothis(); dothat(); x++; }

This is not about "typing less", it's just looks very clear sometimes.

Also loops are just like that:

while(true) x++, y += 5;

Of course both can only be useful when the conditional part or executable part of the loop is quite small, two-three operations.

Solution 15:[15]

The only time I have ever seen the , operator used outside a for loop was to perform an assingment in a ternary statement. It was a long time ago so I cannot remeber the exact statement but it was something like:

int ans = isRunning() ? total += 10, newAnswer(total) : 0;

Obviously no sane person would write code like this, but the author was an evil genius who construct c statements based on the assembler code they generated, not readability. For instance he sometimes used loops instead of if statements because he preferred the assembler it generated.

His code was very fast but unmaintainable, I am glad I don't have to work with it any more.

Solution 16:[16]

I've used it for a macro to "assign a value of any type to an output buffer pointed to by a char*, and then increment the pointer by the required number of bytes", like this:

#define ASSIGN_INCR(p, val, type)  ((*((type) *)(p) = (val)), (p) += sizeof(type))

Using the comma operator means the macro can be used in expressions or as statements as desired:

if (need_to_output_short)
    ASSIGN_INCR(ptr, short_value, short);

latest_pos = ASSIGN_INCR(ptr, int_value, int);

send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff));

It reduced some repetitive typing but you do have to be careful it doesn't get too unreadable.

Please see my overly-long version of this answer here.

Solution 17:[17]

It can be handy for "code golf":

Code Golf: Playing Cubes

The , in if(i>0)t=i,i=0; saves two characters.

Solution 18:[18]

qemu has some code that uses the comma operator within the conditional portion of a for loop (see QTAILQ_FOREACH_SAFE in qemu-queue.h). What they did boils down to the following:

#include <stdio.h>

int main( int argc, char* argv[] ){
  int x = 0, y = 0;

  for( x = 0; x < 3 && (y = x+1,1); x = y ){
    printf( "%d, %d\n", x, y );
  }

  printf( "\n%d, %d\n\n", x, y );

  for( x = 0, y = x+1; x < 3; x = y, y = x+1 ){
    printf( "%d, %d\n", x, y );
  }

  printf( "\n%d, %d\n", x, y );
  return 0;
}

... with the following output:

0, 1
1, 2
2, 3

3, 3

0, 1
1, 2
2, 3

3, 4

The first version of this loop has the following effects:

  • It avoids doing two assignments, so the chances of the code getting out of sync is reduced
  • Since it uses &&, the assignment is not evaluated after the last iteration
  • Since the assignment isn't evaluated, it won't try to de-reference the next element in the queue when it's at the end (in qemu's code, not the code above).
  • Inside the loop, you have access to the current and next element

Solution 19:[19]

Found it in array initialization:

In C what exactly happens if i use () to initialize a double dimension array instead of the {}?

When I initialize an array a[][]:

int a[2][5]={(8,9,7,67,11),(7,8,9,199,89)};

and then display the array elements.

I get:

11 89 0 0 0 
0 0 0 0 0