Publius
Publius

Reputation: 1224

Value Printed changes based on instructions that come after it

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.

Upvotes: 0

Views: 196

Answers (2)

Stack Overflow is garbage
Stack Overflow is garbage

Reputation: 247919

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

Upvotes: 3

David Stone
David Stone

Reputation: 28773

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.

Upvotes: 2

Related Questions