0

I appear to have coded a class that travels backwards in time. Allow me to explain:

I have a function, OrthogonalCamera::project(), that sets a matrix to a certain value. I then print out the value of that matrix, as such.

cam.project();

std::cout << "My Projection Matrix: " << std::endl << ProjectionMatrix::getMatrix() << std::endl;

cam.project() pushes a matrix onto ProjectionMatrix's stack (I am using the std::stack container), and ProjectionMatrix::getMatrix() just returns the stack's top element. If I run just this code, I get the following output:

 2      0      0      0      
 0      7.7957 0      0      
 0      0      -0.001 0      
-1     -1      -0.998 1   

But if I run the code with these to lines after the std::cout call

float *foo = new float[16];

Mat4 fooMatrix = foo;

Then I get this output:

 2      0      0      0      
 0     -2      0      0      
 0      0      -0.001 0      
-1      1      -0.998 1    

My question is the following: what could I possibly be doing such that code executed after I print a value changes the value being printed?

Some of the functions I'm using:

static void load(Mat4 &set)
{
    if(ProjectionMatrix::matrices.size() > 0)
        ProjectionMatrix::matrices.pop();

    ProjectionMatrix::matrices.push(set);
}
static Mat4 &getMatrix()
{
    return ProjectionMatrix::matrices.top();
}

and

void OrthogonalCamera::project()
{
    Mat4 orthProjection = { { 2.0f / (this->r - this->l), 0, 0, -1 * ((this->r + this->l) / (this->r - this->l)) },
    { 0, 2.0f / (this->t - this->b), 0, -1 * ((this->t + this->b) / (this->t - this->b)) },
    { 0, 0, -2.0f / (this->farClip - this->nearClip), -1 * ((this->farClip + this->nearClip) / (this->farClip - this->nearClip)) },
    { 0, 0, 0, 1 } }; //this is apparently the projection matrix for an orthographic projection. 

    orthProjection = orthProjection.transpose();

    ProjectionMatrix::load(orthProjection);
}

EDIT: whoever formatted my code, thank you. I'm not really too good with the formatting here, and it looks much nicer now :)

FURTHER EDIT: I have verified that the initialization of fooMatrix is running after I call std::cout.

UPTEENTH EDIT: Here is the function that initializes fooMatrix:

typedef Matrix<float, 4, 4> Mat4;

template<typename T, unsigned int rows, unsigned int cols>
Matrix<T, rows, cols>::Matrix(T *set)
{
    this->matrixData = new T*[rows];

    for (unsigned int i = 0; i < rows; i++)
    {
        this->matrixData[i] = new T[cols];
    }

    unsigned int counter = 0; //because I was too lazy to use set[(i * cols) + j]

    for (unsigned int i = 0; i < rows; i++)
    {
        for (unsigned int j = 0; j < cols; j++)
        {
            this->matrixData[i][j] = set[counter];
            counter++;
        }
    }
}

g64th EDIT: This isn't just an output problem. I actually have to use the value of the matrix elsewhere, and it's value aligns with the described behaviours (whether or not I print it).

TREE 3rd EDIT: Running it through the debugger gave me a yet again different value:

-7.559 0      0      0      
0      -2     0      0      
0      0      -0.001 0      
1      1      -0.998 1    

a(g64, g64)th EDIT: the problem does not exist compiling on linux. Just on Windows with MinGW. Could it be a compiler bug? That would make me sad.

FINAL EDIT: It works now. I don't know what I did, but it works. I've made sure I was using an up-to-date build that didn't have the code that ensures causality still functions, and it works. Thank you for helping me figure this out, stackoverflow community. As always you've been helpful and tolerant of my slowness. I'll by hypervigilant for any undefined behaviours or pointer screw-ups that can cause this unpredictability.

Publius
  • 1,184
  • 2
  • 10
  • 27
  • 1
    It would be much easier to answer this if you could construct a [small test-case](http://sscce.org) to demonstrate the problem. Right now, my guess is that you're relying on undefined behaviour somewhere. – Oliver Charlesworth May 30 '12 at 21:52
  • I'll try to construct one, but there are a few modules involved here. Without a test case, is there any specific thing you might know of that could cause this behaviour? – Publius May 30 '12 at 21:54
  • The usual suspects are writing off the end of an array or accessing freed memory. If you're on a Unix-like platform, try running your program under valgrind. – Philip Kendall May 30 '12 at 21:55
  • Okay, but how could doing those things _after_ I print the value cause the printed value to change? How, in fact, could _any_ code I run after I print the value cause the printed value to change? – Publius May 30 '12 at 21:57
  • The compiler may make optimisations that reorder code. – Oliver Charlesworth May 30 '12 at 22:00
  • A reordering of code is the only thing I can think of. Is there a way to prevent or work around that? Also, I added a bit more of the code which might offer some insight. Sorry that I'm having trouble coming up with a simple, compileable example that uses all the code I'm using. The matrix class is kind of complex. – Publius May 30 '12 at 22:01
  • 4
    If your program is not invoking undefined behaviour, the compiler's reorderings will not have any functional effect (assuming no compiler bugs). Stop your program invoking undefined behaviour and the problem will almost certainly go away. – Philip Kendall May 30 '12 at 22:03
  • We need to see `Mat4`'s assignment operator at the very least. – David Brown May 30 '12 at 22:06
  • How do I identify undefined behaviour? David Brown: done. – Publius May 30 '12 at 22:06
  • Finding the actual bit of code which is offending is one of the "fun" bits of debugging C/C++ programs, as it will often move around as you turn optimisations on and off and/or run your test program under a debugger. Experience helps a lot, as do tools like `valgrind`. As @DavidBrown says, `Mat4`'s assignment operator would be a prime place to start looking in this case. – Philip Kendall May 30 '12 at 22:15
  • I already verified that the matrix assignment is happening _after_ the std::cout call. Thus, unless the assignment is going back in time, it can't be that. (Not to mention that I did some sanity tests on the values anyways, just to be sure.) – Publius May 30 '12 at 22:20
  • If your invoking undefined behaviour in your program, you can't rely on *anything* after that point to do what you expect; any "ordering" bets are certainly off. – Philip Kendall May 30 '12 at 22:23
  • Does your `Matrix` class implement the [Rule of Three](http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29)? Do you have a destructor, copy constructor, and/or assignment operator defined for it? – Adam Rosenfield May 30 '12 at 22:24
  • Yes, but again, those are happening _after_ I call std::cout. Philip: I didn't just say that the Mat4 initialization happened after the std::cout call because that's how it's written in the code. I actually added more printing to verify it came after. And I can't find any undefined behaviours that I'm calling, unless you see one in the code I provided. – Publius May 30 '12 at 22:26
  • 2
    You not being able to find any undefined behaviour doesn't mean that it doesn't exist. Given the possibilities of 1) non-causality 2) compiler bug or 3) program invoking undefined behaviour, (3) is by far the most likely, and once you have invoked undefined behaviour, the ordering of your std::cout calls means nothing. – Philip Kendall May 30 '12 at 22:29
  • Well I could be seeing undefined behaviour and not recognizing it. How do I tell if the behaviour is undefined? (and I've shown you all the code that the change of the printed value depends upon). Also, for now I'm assuming retrocausality. Not because it's most likely, but because it's most interesting and awesome. – Publius May 30 '12 at 22:31
  • Formally, you read the C++ specification in great detail and very carefully and work out which bit of your program is invoking undefined behaviour. In practice, you pour over your code for a while until you get an insight. Have you tried stepping through your code with a debugger? – Philip Kendall May 30 '12 at 22:33
  • 2
    @Avi : You need to get past the idea that code directly touching the printed value is the culprit. If there's UB _anywhere_ in your program, all bets are off. – ildjarn May 30 '12 at 22:33
  • 2
    @PhilipKendall: if you're invoking undefined behavior in your program you can't rely on what happened *before* that point either. :) – jalf May 30 '12 at 22:35
  • Sorry, I didn't mean to say that I showed you the code only directly touching the printed value. I showed you the code that, even if it exists after the printing, determines what is printed. – Publius May 30 '12 at 22:36
  • By the way, regarding the formatting, it's easy, and it's much appreciated if you take a few seconds to make your post readable yourself. Simply use the mouse to select the text you would like to format as code, then click the `{ }` button above the editor field. – jalf May 30 '12 at 22:47
  • Thanks jalf. The problem wasn't so much that I didn't care about code formatting as it was that I found this formatting method rather counterintuitive. – Publius May 30 '12 at 22:50
  • Well, that's just the easy-to-find way to do it. Prefixing every line in a block of code with four spaces also formats it as code. For inline code snippets, surround them with backticks (`\``) – jalf May 30 '12 at 22:55
  • If you've got this running under Linux, you can use `valgrind`; that will almost certainly point out at least one problem with your code. The odds on this being a compiler bug as opposed to an error on your part are vanishingly small at this point. – Philip Kendall May 31 '12 at 05:34

2 Answers2

3

You're not writing your program instruction by instruction. You are describing its behavior to a C++ compiler, which then tries to express the same in machine code.

The compiler is allowed to reorder your code, as long as the observable behavior does not change.

In other words, the compiler is almost certainly reordering your code. So why does the observable behavior change?

Because your code exhibits undefined behavior.

Again, you are writing C++ code. C++ is a standard, a specification saying what the "meaning" of your code is. You're working under a contract that "As long as I, the programmer, write code that can be interpreted according to the C++ standard, then you, the compiler, will generate an executable whose behavior matches that of my source code".

If your code does anything not specified in this standard, then you have violated this contract. You have fed the compiler code whose behavior can not be interpreted according to the C++ standard. And then all bets are off. The compiler trusted you. It believed that you would fulfill the contract. It analyzed your code and generated an executable based on the assumption that you would write code that had a well-defined meaning. You did not, so the compiler was working under a false assumption. And then anything it builds on top of that assumption is also invalid.

Garbage in, garbage out. :)

Sadly, there's no easy way to pinpoint the error. You can carefully study ever piece of your code, or you can try stepping through the offending code in the debugger. Or break into the debugger at the point where the "wrong" value is seen, and study the disassembly and how you got there.

It's a pain, but that's undefined behavior for you. :) Static analysis tools (Valgrind on Linux, and depending on your version of Visual Studio, the /analyze switch may or may not be available. Clang has a similar option built in) may help

jalf
  • 243,077
  • 51
  • 345
  • 550
  • I'm aware of that. The problem is that I've posted the code that, if it exists, post-hoc changes the value printed (not to mention other code that sets the value, gets it, etc.), and neither I nor anybody else has identified within it undefined behaviour. – Publius May 30 '12 at 22:49
  • 1
    no, you're making false assumptions. You're assuming that it is the code you posted that travels backwards in time to change the state of your program. It does not. The compiler is reordering *something*, or possibly just changing how certain variables and memory sections are initialized. I can't tell you exactly what is wrong, but you simply can't assume that your matrix is changed *by those lines of code that came after the `cout` line. You'll have to study the code that the compiler actually generated to determine that. – jalf May 30 '12 at 22:54
  • Huh? When I include that bit of code that comes after the std::cout line, the value is one thing. If I don't, the value is another thing. It is necessarily the case that something about that code changes the value being printed, because it is the only thing that changes. I know there's not actual retrocausality going on (though that would be interesting), but it's something about that code I add afterwards that causes the value to be changed. – Publius May 30 '12 at 23:00
  • Update! After randomly changing bits of code, I found the undefined behaviour. it was the variable 'counter'. I am now accessing 'set' via 'set[j * rows + i]' – Publius May 30 '12 at 23:05
  • update again! That wasn't actually it, I was running an older version like a moron. – Publius May 30 '12 at 23:14
  • @Avi: no. Something in your program has already trashed the heap, the stack or quite possibly both *before* you get to the bit of code you are quoting. That your program is working at all is lucky - everything here is pointing to undefined behaviour in your program. – Philip Kendall May 31 '12 at 05:37
  • The thing is, I have no idea how I'd do that. If you want, I can post all the code before I get to the part I quoted, it's 15 lines. Most of which are calling library initialization functions and then making sure they didn't fail. What everybody keeps saying (and what I'm absolutely not refuting) is that it's a problem with my code invoking an undefined behaviour, but I've looked at all the code, shown you 90% of it, and nobody has found any undefined behaviours. We know there is a problem, I just can't figure out what it is. – Publius May 31 '12 at 06:17
  • @Avi: well, step one, if you want us to help, is to provide a minimal test case. Take your code, compile it, verify that the error exists in it. Then gradually try to remove lines of code, until the error disappears. Then you will get a smaller and smaller test case, and when you get to the point where removing another line would eliminate the error, show us the program. The only other angle, unfortunately, is to hit the debugger, and examine *exactly* what the compiler is doing. Step through the code line by line (or even instruction by instruction), and keep an eye on the valuesi n memory. – jalf May 31 '12 at 07:14
  • well, except that he's on Windows, as mentioned in another comment :) – jalf May 31 '12 at 08:01
  • From @Avi's comment on David Stone's reply: "I only get this error with MinGW on Windows. On linux, this isn't a problem." – Philip Kendall May 31 '12 at 08:50
2

What is your compiler? If you are compiling with gcc, try turning on thorough and verbose warnings. If you are using Visual Studio, set your warnings to /W4 and treat all warnings as errors.

Once you have done that and can still compile, if the bug still exists, then run the program through Valgrind. It is likely that at some point in your program, at an earlier point, you read past the end of some array and then write something. That something you write is overwriting what you're trying to print. Therefore, when you put more things on the stack, reading past the end of some array will put you in a completely different location in memory, so you are instead overwriting something else. Valgrind was made to catch stuff like that.

Community
  • 1
  • 1
David Stone
  • 26,872
  • 14
  • 68
  • 84
  • I'm sad to say I'm using Windows, so I don't think I can use Valgrind. Turning on thorough and verbose warnings, I come up with nothing. – Publius May 30 '12 at 22:42
  • See http://stackoverflow.com/questions/413477/is-there-a-good-valgrind-substitute-for-windows – Philip Kendall May 30 '12 at 22:43
  • I'm not part of a business I can use for the insure profile and the IBM website isn't recognizing my registration. Something is working against me here, whether it's the universe or my own stupidity. – Publius May 30 '12 at 22:59
  • I only get this error with MinGW on Windows. On linux, this isn't a problem. – Publius May 31 '12 at 02:45