views:

237

answers:

5

i ran into a curious problem regarding evaluation of expressions:

reference operator()(size_type i, size_type j) {
  return by_index(i, j, index)(i, j); // return matrix index reference with changed i, j
}

matrix& by_index(size_type &i, size_type &j, index_vector &index) {
  size_type a = position(i, index); // find position of i using std::upper_bound
  size_type b = position(j, index);
  i -= index[a];
  j -= index[b];
  return matrix_(a,b); // returns matrix reference stored in 2-D array
}

I have thought matrix(i,j) will be evaluated after the call to buy_index, so that i, j will be updated. this appears to be correct, i verified in debugger. however, for some types of matrix, specifically those which have to cast size_type the something else, for example int, the update in by_index is lost. modifying code slightly removes the problem:

reference operator()(size_type i, size_type j) {
  matrix &m = by_index(i, j, index);
  return m(i, j); 
}

do you know why the first operator misbehaves? thanks

prototypes which work and which do not

inline reference operator () (size_t i, size_t j); // ublas, size_type is std::size_t
reference operator () (int i, int j); // other prototype, size_type is int

in debugger backtrace stack looks like this:

  • i = 1 upon entry to operator() //okay
  • i = 0 after finish from by_index //okay
  • i = 1 upon entry to matrix:: operator() //not right, should be 0
+2  A: 

I suspect that casting a reference to a different type breaks strict aliasing rules that your compiler uses to optimize more efficiently. You have two variables/references of different type and the compiler assumes that they don't refer to the same memory (but which they in fact do). The compiler then optimizes the code under that wrong assumption which produces wrong results.

You can try to compile with -fno-strict-aliasing (or equivalent) to disable these optimizations and see if it improves the situation.

sth
have been compiling using g++ -O0 -Wall. I have thought that disables all potential dangers optimizations. will try your suggestion
aaa
Hmmm, -O0 should have turned it off, so probably this is not the solution. Would have fit the symptoms, though.
sth
A: 

Passing data through argument lists like that is very, very, unclear. Relying on implicit casts between reference types to different-sized bases is very, very unsafe.

Anyway, if you're calling by_index(size_t &,… with an int argument, you're taking the reference of a temporary. This should be a warning, but maybe you're on an older compiler. Try an explicit cast.

reinterpret_cast< size_t & >( i ) /* modify i before next fn call */

But of course that's not guaranteed to do the right thing. Really, you need to sort out unsigned from signed and not assume sizeof(int) == sizeof(size_t) because often it isn't.

Potatoswatter
+2  A: 

In my opinion, this boils down to order of evaluation.

The standard says -

(5.4) Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.

Which fits the bill exactly. The values of i and j may be evaluated before the call to by_index(), or after it. You can't tell - this is unspecified.

I will add that the form that solves your problem is far more readable in my eyes, and I would have used it regardless of correctness of the first form...

Hexagon
okay, that is what i started thinking after the problem.before that I have thought the order was guaranteed.
aaa
it is only guaranteed to be before the next sequence point
jk
@jk, Actually, I doubt sequence points are relevant here. There *is* a sequence point right after returning from the by_index() function call. But the evaluation of the parameters themselves happen regardless of sequence points.
Hexagon
+2  A: 

Finally I found where in the standard this is specified (n1905 draft):

(5.2.2-8) - The order of evaluation of arguments is unspecified. All side effects of argument expression evaluations take effect before the function is entered. The order of evaluation of the postfix expression and the argument expression list is unspecified.

The postfix expression mentioned is the part to the left of (). So in the "outer" function call it is not specified if by_index(i, j, index) or it's arguments (i, j) are evaluated first.

There is a sequence point after a function returns, so when by_index(i, j, index) returns all side effects are complete, but the (i, j) parameters might already have been evaluated (and the values been stored in a register or sth.) before that function even go called.

sth
A: 

http://www.rawkam.com/?p=963

sunil
Stop spamming your stuff everywhere, please. An answer with just a link is spam. If questions are answered, there's no need to bump it with repeated information. Link your post when the question comes up again.
GMan