1

Why does this code produce seemingly random behavior,

std::cout << ( thePointerIsGood = ( NULL != (aPointer = aFunctionThatReturnsAPointer(args)) ) );

when this multi-line version that does the same thing works just fine?

aPointer = aFunctionThatReturnsAPointer(args);
thePointerIsGood = (NULL != aPointer);
std::cout << thePointerIsGood;

I am capturing aPointer and thePointerIsGood because I use them later on in the code.

Update

The above actually works just fine. But I was able to reproduce some odd behavior with this program, and I've marked where the error occurs:

// Compiled with:
//   gcc test.cpp -c -o test.o; gcc -lstdc++ test.o -o test

#include <iostream>
#include <cstdlib>

  class
AClass
  { public
    : // Operators ///////////////////////////////////////
      ;  const  bool  operator==  (  const  int  rhs  )  ;
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  }
  ;

  class
AHelperClass
  { public
    : // Functions //////////////////////////////////////////////////////
      ;  static  AClass*  AFunctionThatReturnsAPointer  (  int  arg  )  ;
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  }
  ;

  const
  bool
  AClass::
operator==
  (  const  int  rhs  )
  { return (rhs == 222); }

  AClass*
  AHelperClass::
AFunctionThatReturnsAPointer
  (  int  arg  )
  { return ( (arg == 777)
           ? new AClass
           : NULL
           )
           ;
  }

  int
main
  (     int  argc
  ,  char**  argv
  )
  { // Variables //////////////////
    ;  AClass*  aPointer          ;
    ;     bool  thePointerIsGood  ;
    ;     bool  theValueMatches   ;
    ;      int  i                 ;
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    for ( i = 0
        ; i < 10
        ; i++
        )
        { // First a good pointer
          std::cout << ( ( thePointerIsGood = ( NULL != ( aPointer = AHelperClass::AFunctionThatReturnsAPointer(777) ) ) )
                       ? "Y  "
                       : "N  "
                       )  
                    << ( (thePointerIsGood == true)
                       ? "expected    "
                       : "unexpected  " 
                       )
                    ;

          if ( !thePointerIsGood )
             { std::cout << std::endl; }
          else
             { // This is where the error is, thanks to Peter for pointing it out
               std::cout << ( (theValueMatches = ((*aPointer) == 222))
                            ? "Y  "
                            : "N  "
                            )
                         << ( (theValueMatches == true)
                            ? "expected"
                            : "unexpected"
                            )
                         << std::endl
                         ;
             }


          delete aPointer;

          // Now a NULL pointer
          std::cout << ( ( thePointerIsGood = ( NULL != ( aPointer = AHelperClass::AFunctionThatReturnsAPointer(877) ) ) )
                       ? "Y  "
                       : "N  "
                       )  
                    << ( (thePointerIsGood == false)
                       ? "expected    "
                       : "unexpected  " 
                       )
                    ;

          if ( !thePointerIsGood )
             { std::cout << std::endl; }
          else
             { std::cout << ( (theValueMatches = ((*aPointer) == 222))
                            ? "Y  "
                            : "N  "
                            )
                         << ( (theValueMatches == true)
                            ? "expected"
                            : "unexpected"
                            )
                         << std::endl
                         ;
             }


          delete aPointer;
        }

    return 0;
  }

Which produces for me the following output (everything should say expected)

Y  expected    Y  expected
N  unexpected  
Y  unexpected  Y  expected
N  unexpected  
Y  unexpected  Y  expected
N  unexpected  
Y  unexpected  Y  expected
N  unexpected  
Y  unexpected  Y  expected
N  unexpected  
Y  unexpected  Y  expected
N  unexpected  
Y  unexpected  Y  expected
N  unexpected  
Y  unexpected  Y  expected
N  unexpected  
Y  unexpected  Y  expected
N  unexpected  
Y  unexpected  Y  expected
N  unexpected  
jnfjnjtj
  • 379
  • 3
  • 10
  • I would suggest you not to do that much assignments in one line. The question is interesting though. – heinrich5991 Mar 18 '12 at 19:41
  • 1
    Does `args` refer in any way to `aPointer` and/or to `thePointerIsGood`? Does `aFunctionThatReturnsAPointer`? – ruakh Mar 18 '12 at 19:43
  • 2
    please show us the actual code – Philipp Mar 18 '12 at 19:43
  • 1
    Why even assign to a variable at all before giving it to cout? I'm not a c++ standard expert, but this could cause cout to receive the result of the assignment, rather than what is being assigned to. This would also explain why moving it to multiple lines makes everything work. If you're dead-set on making this code fragment work, I would thumb through the standard and see how cout behavior is defined. More importantly, see if there are any specific conditions resulting in undefined behavior with cout. – pg1989 Mar 18 '12 at 19:50
  • 1
    @pg1989 - First, I don't see how cout is related at all. As for the variable - he explained why he is assigning to a variable, and (unless `operator=` is horribly abused) the value of `a=b` equals to `b` with the side effect of assigning it to `a`. This is an interesting question indeed. – Asaf Mar 18 '12 at 20:05
  • I'm trying to reproduce this error in a simple C++ program right now to maybe illustrate what's going on more thoroughly. `args` does not refer to `aPointer` or `thePointerIsGood`. As far as `cout` receiving the result of the assignment, that's what I was going for. It's really not necessary for the code to be written this way, but I had written it that way and things got.. random. Anyway, I'll post a better sample in a bit. – jnfjnjtj Mar 18 '12 at 20:05
  • @Asaf `operator=` could be returning something other than `*this`. – Manish Mar 18 '12 at 20:21
  • 1
    @Manish - which is what I call 'horribly abused' – Asaf Mar 18 '12 at 20:23
  • I updated the post to include code that illustrates what I'm basically doing. – jnfjnjtj Mar 18 '12 at 20:33
  • i tested code in vc 2008. work perfectly on release mode. crashes with debug. cause: thePointerIsGood used without initialisation – mikbal Mar 18 '12 at 20:37
  • @mikbal That is so weird. I'll have to test VC++ Monday at work. Maybe it's just GCC? – jnfjnjtj Mar 18 '12 at 20:38
  • ok. i take back what i said :) it doesnt work as expected on release mode neither. it just doesn't crash. – mikbal Mar 18 '12 at 20:41
  • For me both the single line version and the multi-line version generate identical assembly output. I used gcc 3.4.4 for this test. – vhallac Mar 18 '12 at 20:52
  • +1 for having the most creative code layout I have seen in years. – Joost Sannen Mar 18 '12 at 21:15
  • @Joost Haha thanks, I call it "the Hanging Gardens" style. I try to make tables, boxes, and vertical lines when it helps to group things visually. Plus, the resulting shapes make it really easy to find a part of the code by sight when I'm scrolling quickly. It gets really interesting when I write JS with jQuery... – jnfjnjtj Mar 18 '12 at 21:31

2 Answers2

1

My thought is that the following is undefined behaviour:

std::cout << ( (theValueMatches = ((*aPointer) == 222))
                            ? "Y  "
                            : "N  "
                            )
                         << ( (theValueMatches == true)
                            ? "expected"
                            : "unexpected"
                            )
                         << std::endl
                         ;

Because theValueMatches is both used and assigned to in the same expression, and it isn't defined whether the assignment happens before or after the comparison to true. It does surprise me if it seems to be nondeterministic, since you'd expect the compiler to pick one approach or another, although I observe that mine isn't - I get the same output from the program each time, with quite a few unexpecteds in there.

Peter
  • 6,978
  • 1
  • 31
  • 43
  • The answer to http://stackoverflow.com/questions/2129230/cout-order-of-call-to-functions-it-prints seems to agree this. – uesp Mar 18 '12 at 20:44
  • That makes sense to me. I see now how it could be interpreted ambiguously. The problem isn't really the first part of my post at all, but rather this part after it. I don't think I would have thought about this statement the way you just put it for awhile. Thanks! My code is written the way that surely works every time, but I was really curious to why this way wasn't working. – jnfjnjtj Mar 18 '12 at 20:46
0

There is no guarantee in the order of evaluation of expressions. If you have:

x << (a = b) << a;

Then "a" may be evaluated before "(a = b)"

Rémi
  • 3,344
  • 1
  • 25
  • 37