tags:

views:

2178

answers:

18

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

+6  A: 

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.

The question is about when you would use it, not what it does
Graphics Noob
Also for completeness: the comma used in parameter/argument lists is not the comma operator.
Michael Burr
There are no "initializers" in `int a, b, c;`. In `int a, b, c;` comma separates *declarators*.
AndreyT
Yes you are right, I was thinking of `int a[] = {1, 2, 3}` when I wrote that.
+17  A: 

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)

Jack Lloyd
Tricky, I didn't know this. C(++) really has too many 'features' which aren't good for anything but causing endless snorts of stupid laughter on cocktail parties.
Frerich Raabe
+1 because it is sooooo evil
Dolphin
This is certainly tricky because you mix comma op with the comma in declarations xD Now, whether it's legal or not - i didn't know but the Standard knows :) The fact is: you need to paren the third operand of `op?:`, because otherwise the binding is: `int x = (cond ? A : B), 117;` xD
Johannes Schaub - litb
@litb Good catch! I'm not going to change it because this makes the example especially confusing/obscure, which was kind of the intent anyway. :)
Jack Lloyd
And bear in mind that the comma operator is completely different from the comma between function arguments. There's lots of neat ideas in C and C++ that don't actually work well in practice. (Look up Duff's Device if you don't believe me.)
David Thornley
I'm a bit sad that you don't want to fix it. I understand that you want to keep your answer in "zero edits" state. But for the price of keeping code that produces compile errors like "expected unqualified-id before numeric constant" I think that's not beneficial. If you want to keep the code confusing you can aswell write any other non-compiling code. I think the whole point is that the code is both confusing *and* compiling.
Johannes Schaub - litb
There is a serious terminoligical error in the answer. You can't "pack multiple *statements* into a ternary operator" in standard C. What you have "packed into a ternary operator" above are not *statements*. These are subexpressions joined into a larger expression by the `,` operator. "Statement" is an important concept in C and misusing the term like that can only lead to unnecessary confusion.
AndreyT
+4  A: 

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;

SadSido
Important note: If you overload the comma operator then you loose the sequence point, ie. you get function call semantics and the operands to the operator are evaluated in unspecified order.
Richard Corden
I used this once but with / as operator for a class which had no algebraic operations ( like widgets for instance ).
fa.
Here's an example of this: http://www.boost.org/doc/libs/1_40_0/libs/assign/doc/index.html
sharth
+3  A: 

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?

Paul McGuire
Incrementing c in the loop condition is obscure, but the last part of the for loop looks quite sane to me.
UncleBens
Be aware that noting a ";" for an empty body is asking for "for(;;);\n{...}" and wondering why the body always runs once...
stefaanv
Particularly since it increments 'c' every time it _checks_ the condition 'a <= b', including the final check when a > b. So you go one step farther than intended.
Matt B.
@Matt: You could rewrite that line as: for(a = 0, b = 10, c = 0; a <= b; a++, b--, c += 2*a+b);
sharth
+4  A: 

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.

Thomas Padron-McCarthy
Ho yea, that's right, I've seen that. But I consider it to be a bad practice. Replacing stdlib malloc built in function with yours is much better as you can do it post compilation. Many debugging tools do that transparently anyway. Doing that also let you test for bad code (eg, you return NULL randomly and see if it's catched).
@Nicolas: Agreed. And here is my more recent malloc macro, without any comma operators: #define malloc(s) smdm_debug_malloc((s), \_\_FILE\_\_, \_\_LINE\_\_)
Thomas Padron-McCarthy
However, even aside from the specific case of tracking memory allocations, there are times when the comma operator is useful in macro hackery. The comma operator is a hackish operator pretty much by definition - it's to hack in 2 things where only one is allowed. It's almost always ugly, but sometimes that's all you've got. A colleague of mine used to call things like this "fitting 10 pounds of sh** into a 5 pound bag".
Michael Burr
+7  A: 

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__ )
    {
    }

  ....

};
fa.
+2  A: 

I have to agree. Over use of the comma makes your C code a lot less readable.

Obi
+2  A: 

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.

Jay
Nesting ifs is infinitely less cryptic than a comma inside an if.
jmucchiello
A: 

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.

iain
A: 

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.

Paul Stephenson
+3  A: 

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.)

jmucchiello
Then again the benefits of setting a pointer to null after `delete` are highly disputable...
AndreyT
Well, I wouldn't do it in a destructor when deleting a member pointer. But I wouldn't call defensive programming a bad idea. The better point is in my answer: if you are being defensive why aren't you using one of the auto_ptr/unique_ptr/shared_ptr family of defensive pointer programming?
jmucchiello
+8  A: 

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".

Adisak
+1 although I disagree that it is ugly and dangerous. Just declare your macros with caution and your are safe
qrdl
+2  A: 

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;
Stjepan Rajko
+7  A: 

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) ) {};
};
Pavel Shved
UncleBens
@UncleBens , `cin >> str` returns `iostream`, which will convert to `false` boolean value when end-of-file is reached, not when an empty string is encountered!
Pavel Shved
Doesn't that meant that your code will get stuck on the last line of a file? cin >> str won't overwrite str (I think?), and str != "" will be eternally true.
DrPizza
@DrPizza , ah now I understand. I assume that the string I'm comparing with will be encountered before EOF.
Pavel Shved
Your second example needs another pair of parentheses to prevent the comma from acting as a constructor argument separator. (I think you haven't intended that).
Johannes Schaub - litb
@litb , fixed, thanks.
Pavel Shved
A: 

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
}
Marcus Lindblom
+5  A: 

C language (as well as C++) is historically a mix of two completely different programming styles, which one can refer to as "statement programming" and "expression programming". As you know, every procedural language normally supports such fundamental programming constructs as sequencing and branching. These fundamental constructs are present in C/C++ languages in two forms: one for statement programming, another for expression programming.

For example, when you write your program in terms of statements, you might use a sequence of statements separated by ;. When you want to do some branching, you use if statements. You can also use cycles and other kinds of statements.

In expression programing the same constructs are available to you as well. This is actually where , operator comes into play. Operator , in nothing else than a separator of sequential expressions in C, i.e. operator , in expression programming serves the same role as ; does in statement programming. Branching in expression programming is done through ?: operator and, alternatively, through short-circuit evaluation properties of && and || operators. (Expression programming has no cycles though. And to replace them with recursion you'd have to apply statement programming.)

For example, the following code

a = rand();
++a;
b = rand();
c = a + b / 2;
if (a < c - 5)
  d = a;
else
  d = b;

which is an example of traditional statement programming, can be re-written in terms of expression programming as

a = rand(), ++a, b = rand(), c = a + b / 2, a < c - 5 ? d = a : d = b;
a = rand(), ++a, b = rand(), c = a + b / 2, d = a < c - 5 ? a : b;
d = (a = rand(), ++a, b = rand(), c = a + b / 2, a < c - 5 ? a : b);
a = rand(), ++a, b = rand(), c = a + b / 2, (a < c - 5 && (d = a, 1)) || (d = b);

(any line will do).

Needless to say, in most cases in practice statement programming produces much more readable C/C++ code, so we normally use expression programming in very well measured and restricted amounts. But in many cases it comes handy. And the line between what is acceptable and what is not is to a large degree a matter of personal preference.

As an additional note: the very design of the language is obviously tailored towards statements. Statements can freely invoke expressions, but expressions can't invoke statements (aside from calling pre-defined functions). This situation is changed in a rather interesting way in GCC compiler, which supports so called "statement expressions" as an extension (symmetrical to "expression statements" in standard C). "Statement expressions" allow user to directly insert statement-based code into expressions, just like they can insert expression-based code into statements in standard C.

AndreyT
Wow, that's a really interesting explanation, thanks!
Mike McQuaid
A: 

It can be handy for "code golf":

http://stackoverflow.com/questions/1609702/code-golf-playing-cubes/1612634#1612634

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

Kinopiko
A: 

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.

Eclipse