Does someone remember this old bug involving broken debug output streams? Well, some nice person just gave me the key to solve this problem.
Today, Tom Novelli posted the following comment on the blog post linked above:
I ran into similar problems using C++ stream modifiers for colorized debugging messages. I wanted to save the previous color(s) on a stack:
cerr << color.push(C_RED) << "Fatal Error" << color.pop() << newline;
Trouble is, most compilers use right-to-left evaluation order, so pop() executes before push(). There is a solution but it’s not nearly as straightforward as putting push() and pop() in separate statements. C++ streams are a failure, I’m afraid. Like Amenel, I went back to using printf(). Then I went back to plain C, using the UThash macro library instead of STL maps/vectors/lists.
I really don’t think C++ is a good choice for OS implementation, especially on your first attempt. You’ll get distracted by all kinds of language issues. Some of the newer languages are designed better than C++, but those are mostly dynamic/interpreted languages with complex runtime environments, not suitable as system languages.
At first, I was going to say that we probably have different problems, because my modifier implementation only depends on the order in which the “<<” are evaluated, which is standard in the C++ spec.
To explain this better, let’s examine the example which I gave in that article, dbgout << txtcolor(TXT_LIGHTRED) << “RED” << txtcolor(TXT_LIGHTGRAY) << “GRAY”;
When I wrote the DebugOutput class which the dbgout stream is based on, one of my goals was to make it possible to seamlessly display several independant debug streams of text on the screen at once. Kind of like ncurses’ windows, but with a cleaner syntax. For this to work, modifiers had to solely modify the state of the stream which they are fed into.
To implement this behavior, I had created a set of objects that can be fed into a DebugOutput stream in order to modify its properties (text color, target area on the screen…), and made my modifier functions return such objects.
When one runs txtcolor(), as an example, it creates a DebugAttributeChanger object, which noticeably includes information on the text color change. This DebugAttributeChanger object can then be fed into a DebugOutput object like dbgout using the DebugOutput& operator<<(const DebugAttributeChanger& manipulator); method, and it is this method which is going to modify the state of the DebugOutput object based on the contents of the DebugAttributeChanger one.
So, what would happen if the C++ compiler evaluated txtcolor(TXT_LIGHTGRAY) before txtcolor(TXT_LIGHTRED)? Nothing special. The evaluated results would just sit there, waiting to be fed into the dbgout stream (in an order that’s guaranteed by the C++ standard) before they have some effect.
And then I thought.
And thought again.
And I realized my mistake.
The problem was that my modifier functions didn’t return a result on the stack, the usual way. Instead, they returned a reference to a static buffer, where the result was stored.
A single buffer. That was overwritten each time the function was run.
I probably did this out of fear that the stack could be overflowed with modifier objects in extreme use cases. However, that was not a reasonable fear. And it made my code sensitive to the order in which things are evaluated, which could well cause problems like the one I encountered, and is a pretty bad coding practice anyway (since, as we all know, compiler from the future will evaluate things in parallel, breaking all code which works this way).
I thus removed the superfluous “&” here and there in function prototypes, put my test code back in the main function, and compiled a floppy image.
That worked flawlessly.
So thanks, Tom, for helping me notice how stupid I am. And by the way, I do think that C++, or at least a subset of it, has some potential as a low-level language. Makes code easier to read for me, which is a pretty desirable characteristic in something as crashy and convoluted as an OS kernel.