views:

44

answers:

1

In my program, I've redirected stdout to print to a file 'console.txt'. A function writes to that file like this:

void printToConsole(const std::string& text, const TCODColor& fc, const TCODColor& bc)
    {
        // write the string
        cout << text << "@";

        // write the two color values
        cout << static_cast<int>(fc.r) << " "
             << static_cast<int>(fc.g) << " " 
             << static_cast<int>(fc.b) << " "
             << static_cast<int>(bc.r) << " "
             << static_cast<int>(bc.g) << " " 
             << static_cast<int>(bc.b) << " " << endl;
    }

I have a function that reads from that file that looks like this:

    void Console::readLogFile()
    {
        ifstream log("console.txt", ifstream::in);
        if(!log.is_open())
        {
            cerr << "ERROR: console.txt not found!" << endl;
            return;
        }

        // read new input into Console
        string str;
        while(getline(log, str))
        {
            cerr << "str: " << str << endl;
            /* do stuff with str here */
        }
        cerr << endl;

        log.close();
        clearLogFile();
    }


    void Console::clearLogFile()
    {
        ofstream("console.txt", ios_base::trunc);
    }

The first time through the readLogFile, everything works fine. Afterwards, however, it starts to have problems. It will read in the first line of console.txt as a blank string. I stepped through the program with console.txt open in gvim, and monitored how it changed. The first time through, when it worked correctly, console.txt looks something like this:

  1 moved UP.@191 191 191 0 0 0
  2 Player moved.@191 191 191 0 0 0
~
~

which is as it should be. The program then goes to clearLogFile, and afterwards console.txt is empty. However, the second time through, when I open the ifstream, console.txt looks like this:

  1 ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
    ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@moved UP.@191 191 191 0 0 0
  2 Player moved.@191 191 191 0 0 0
~
~

This time, when getline reads the first line into str, str is blank. Strangely, the cerr << "str: " << str << endl; line still prints str as "moved UP.@191 191 191 0 0 0", even though inspecting str in gdb reveals that it's empty.

Anyone know what's going on here?

A: 

The routine that is writing is not resetting it's file position before rewriting to the file. As a result it starts off at an offset into the target file.

I believe a cout.seekp(0) after performing the write will reset the write pointer, thus restarting the write at the start of the file.

You're probably going to have a lot of problems with consistency of the content of the logfile with the way you're using it, though.

Comment added here, as I can't get formatting in the comment box...

By resetting the file pointer after every write, there will only ever be one line in the output file. You need to make sure that you read it before you write to it, otherwise the new data will be over-written by the old data, or worse may be partially overwritten.

e.g. write 1:

Player moved 1 2 3 4

e.g. write 2:

Player fell over

Would leave:

Player fell over
3 4

In the written file if the reader didn't get there before the writer made it's second write.

You may be better off using an explicit file, rather than using cout, or you could remember the read position between reads and seek to that before performing your next read, but that leaves you with an ever growing logfile.

HTH

Petesh
That solved the problem, thanks. What kind of problems do you think I'll be having? Any suggestions on how I could use it better?
Max
By resetting the file pointer after every write, there will only ever be one line in the output file. You need to make sure that you read it before you write to it, otherwise the new data will be over-written by the old data, or worse may be partially overwritten.e.g.first write:Player moved 1 2 3 4 5Second write:Player fell overleaves the following in the file:Player fell over3 4 5if the reader didn't get to the file before the writer wrote the second message.It's one of the fundamental problems of synchronization.
Petesh
I put `cout.seekp(0)` after `ofstream("console.txt", ios_base::trunc);` in `clearLogFile`, so it will only reset the pointer after I clear everything out of console.txt, not after every write. It seems to be working fine so far. Do you see any other problems?
Max
As long as they're both in the same program that would work, but there is still a race condition... after you've processed the data, and before clearing the log file, the writer may get another line in. This line would be lost.
Petesh