Any thoughts on the comma operator (C/C++/Obj-C?)

Discussion in 'Mac Programming' started by ArtOfWarfare, Feb 21, 2013.

  1. macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #1
    I recently decided to insert a few comma operators into the output lines of my C++ code and I feel like they've cleaned up my code a lot.

    For example, I had this code:

    Code:
    cout << "Can't place " << v << " at " << x << ", " << y << endl << "Add: ";
    printBitsForInt(mask);
    cout << "Old: ";
    printBitsForInt(conflicts[v-1]);
    cout << endl;
    Just an FYI, printBitsForInt() takes an int and prints out the bits from it. I couldn't find any way to overload the insertion operator between a stream and an int (because it's already defined by C++.) I couldn't find any stream parameters for cout I could set to make it print ints as binary, either. I tried using a bitset container, but it didn't allow its contents to be printed out with the grouping I wanted (groups of nine bits with spaces between them.)

    And by introducing a few sequence points I feel like I was able to clean it up quite a bit, improve legibility, and reduce the amount of space it takes up.

    Code:
    cout << "Can't place " << v << " at " << x << ", " << y << endl
         << "Add: ", printBitsForInt(mask), cout << endl;
         << "Old: ", printBitsForInt(conflicts[v-1]), cout << endl;
    I'm a little confused why I so rarely see anyone using them.

    I'm wondering if I could use the same basic idea in the future with some C or Obj-C code.

    Also, is there any real difference between using the comma operator and inserting a semicolon? I feel like the compiler probably generates the same code for both, wouldn't it?

    Are there any other good uses for the comma operator?
     
  2. macrumors G5

    gnasher729

    Joined:
    Nov 25, 2005
    #2
    I felt like that for a very short period after learning C. Then I felt it made the code less readable.

    There's no reason why the code would be different.

    Very, very rare. This would be an example:

    Code:
    while (tmp = x*x + y*y, tmp > 10 && tmp < 20) { ... }
    
    instead of

    Code:
    for (;;) {
        tmp = x*x + y*y; 
        if (tmp <= 10 || tmp >= 20) break;
        ...
    }
     
  3. lee1210, Feb 21, 2013
    Last edited: Feb 21, 2013

    macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #3
    I've never felt the need to use it, I've rarely seen it at all, really. Executing multiple statements in the incrementor of a for, etc. is a rare but useful case. I had to fix one very pernicious bug caused by a comma operator, which put it into my bad graces.

    As for the difference between ; and , is that ; ends a statement and , joins statements and evaluates to the right operand.
    x = haveFun(), eatPie(), takeNap();
    Will result in x being set to the result of takeNap().

    I'm not big on C++, but %b is the format specifier for printing the binary representation of an int. Why not return a std::string from your helper, then just keep using <<. Having the function print after formatting that way limits the utility.

    -Lee

    Edit: figured there was a better way. Try:
    cout << setbase(2) << myInt << endl;
     
  4. chown33, Feb 21, 2013
    Last edited: Feb 21, 2013

    macrumors 603

    Joined:
    Aug 9, 2009
    #4
    I used to write loops like the former, or try to. Then I spent way too much time trying to figure them out weeks later, or trying to debug them at the time. Now I only do it like the latter (though I usually put the break on its own indented line).


    My thoughts regarding the comma operator:
    1. It can make it difficult to set a breakpoint at a statement, if the statement is a bunch of comma-separated expressions. Better to make each one a separate statement.

    2. There were times when an aggressive optimizer wouldn't rearrange the parts of a comma'ed statement, but it would rearrange statements with semicolons. I don't know how true that is any more, since optimizers are a lot better these days. Still, it wouldn't surprise me if commas aren't entirely interchangeable with semicolons when it comes to code rearrangement.

    3. If you want to add something between the comma'ed expressions, it gets ugly very quickly. Even when the expressions may seem to be inseparable in the design, things can evolve and change over time.

    4. One place where I still use commas is in the initializer (1st) or the iterator (3rd) clauses of a for loop, e.g.:
    Code:
    for ( a = x, b = y;  someCondition;  ++a, --b )
    {  something using both a and b;  }
    
    The fact that a continue in a for loop will go to the iterator part means that if there's more than one iterative calculation, you can guarantee that both are always done. However, there are also times when you DON'T want both things done, and for that I'd probably write it as a for (;;) so every conditional pathway was easy to follow.


    EDIT
    I think the underlined part is incorrect. Assignment binds tighter than comma, so the assignment is done first, then eatPie() is called, then takeNap(). Effectively:
    Code:
    (x = haveFun()), eatPie(), takeNap();
    
    If this precedence weren't so, then this expression wouldn't work:
    Code:
    a = x, b = y
    
    Since functions that return values can have the value ignored with little or no compiler complaint, the fact that takeNap() returns a value that's ignored would not cause the compiler to warn about it. Unexpected results ensue.

    I don't have the time right now to write up a test case, but doing so would prove enlightening on several levels. One level is the fundamental operation of comma: its precedence, associativity, debuggability, etc. Another level is the potential confusion or errors when using it, even for skilled programmers with years of experience. I include myself in that bunch of confused error-makers, because I could be wrong. It's exactly the kind of thing I'd only trust after writing test code for, and frankly, if I have to write a test to figure out how it works, is it really worth using in real-world code? I could name all my variables as nothing but different numbers of _'s, too, but why would I do that except to sow confusion?
     
  5. macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #5
    Oops, there you go.
    Code:
    x = (haveFun(),eatPie(),takeNap());
    Works the way I was thinking. Screwed up the precedence, but was right about the second operand being the result.

    One more data point for myself generally avoiding the , operator. Clauses in a for loop are the rare exception.

    -Lee
     
  6. gnasher729, Feb 22, 2013
    Last edited: Feb 22, 2013

    macrumors G5

    gnasher729

    Joined:
    Nov 25, 2005
    #6
    That's a very important argument. Making your code easy to debug is very important. I tend to write code with "setting breakpoints" in mind. For example: The first one doesn't let you easily examine the result of calling f (x), the second does.

    Code:
    return f (x);
    
    Code:
    int result = f (x);
    return result;
     
  7. macrumors 603

    whooleytoo

    Joined:
    Aug 2, 2002
    Location:
    Cork, Ireland.
    #7
    Yikes. I'm ashamed to admit after years of study and more years of development work on C-esque languages, I'd never even heard of the comma operator.

    I'm kind of glad I didn't though. If there's one thing I've learned through experience it's the importance of code maintainability, and having easily readable and debuggable code is far, far more important than keeping code terse.

    Even reading through the Wikipedia, the simple examples gave me a shiver:
    i = (a,b); // b
    i = a,b; // a

    I can see how one of those lines, buried in hundreds of lines of code, could introduce an easily missed bug.
     
  8. macrumors 65816

    Joined:
    Apr 17, 2012
    Location:
    Destin, FL
    #8
    Occasionally, I'll come up with an amazing line of code that is pure poetry. I take a sip of tea, then break the line into about 30 separate lines of easily readable code and let the compiler do it's job of making the executable efficient.

    Coming back to fix something 4 months and 6 projects later on poetry is a serious PITA.
     
  9. macrumors G5

    gnasher729

    Joined:
    Nov 25, 2005
    #9
    That's why you turn on all compiler warnings that you can. i = a, b; doesn't even compile on my Mac (it produces a warning because the value b is ignored, and I turn all warnings into errors).
     
  10. macrumors 603

    notjustjay

    Joined:
    Sep 19, 2003
    Location:
    Canada, eh?
    #10
    This is why I tend not to go for "clever" code. I work on a codebase that's huge (500K+ lines), historical (some of this code was written in the 80's) and maintained by dozens of people. Code I wrote today might be maintained by someone else 6 months from now when I transfer to a different project. Likewise, I'll frequently inherit code written 4 years ago by other programmers who no longer work with the company.

    To me, good code is cleanly formatted, straightforward, and easy to follow (with comments where helpful). If I have to stop and say "wait wait wait, what is this doing?" then that slows things down and potentially allows bugs to go unnoticed.
     
  11. thread starter macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #11
    Huh. Can I have it put a space after every 9th bit/digit?
     
  12. macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #12
    You can probably write your own modifier, but otherwise I'd just write a formatter that returns what you want to print. I'll happily admit that I don't know the right way to do this safely as it pertains to memory in C++, but surely this is a solved problem.

    -Lee
     
  13. macrumors 6502a

    Joined:
    Feb 18, 2008
    #13
    Wow. :eek:
     

Share This Page