www.digitalmars.com         C & C++   DMDScript  

c++.mfc - win::cout, win::clog, win::cerr output for Windows

reply Richard Grant <fractal clark.net> writes:
Hi,

I just finished a project in MFC, and everything was going smoothly right up
until the end. Defining the interface with resource editor was easy, creating
controls in code was trivial.. Using the Data Exchange to get access to the
control data seemed a little contrived, but worked well. All in all a fairly
complete application with minimal effort.

Then I ran into an intermittent access violation. Ok, so I reached into my usual
bag of tricks, and realized - how was I going to output program state using a
windowing interface that was itself exhibiting problems. Rats. No std::cout to
play with, and typed variables everywhere. Also the program was 3MB and my code
couldn't have been more then 2 or 3 hundred lines.

Anyway, after I finished stuffing MessageBox with stringstream managed strings,
and dropping them into various places, I eventually found the problem. It looked
like something to do with Thread Local Storage being instantiated improperly.
I've worked with TLS before, so I looked around in the MFC code but gave up
after about 10 seconds.

I just restructured the code in places that seemed suspect: 1) using a static
member function as control routine of AfxBeginThread, 2) having my own defined
default c'tor and d'tor for theapp, 3) Passing the class of the static member
function as the argument of the thread start, and casting from void to the
class. And 4) starting the thread in oninitdialog instead of initializeapp.
Everything seemed to work fine after that. Not that I thought what I was doing
was illegal, just that they seemed like places the framework authors might get
confused.

The point is, I think I would have spent considerably more time trying to
identify the problem if there were an iostream based variable like "win::cout"
("win" being a namespace qualifier). I did some minimum looking around, and was
amazed that there was very little material on this issue. What with the
continued success of Windowing environments, I would have thought that such a
subject would have already been exhausted.

I did find a macro at the Code Project "cout", but I hate macros - mostly due to
the if (bool) macro; problem where the macro expands into more than one line.
But that's really a personal distaste. Still, you get some typing and such, but
I don't really like the format. I prefer to decide when a newline will appear,
and what title to give my output. And why should I define the console?

Since I don't program that often for GUI deployments, I am not certain what
features win::cout would have, and what would it mean to do:

int i = 10;
win::cout << i << win::endl;

Would another window appear.. would it be a message box? would it be a window
with menu, exit and just an edit control? Would the program halt execution?

There are probably resources that offer the windows (or windowing) equivalent of
variables that support stdout - a paper on the subject at University level?

Perhaps I'm just being silly, and it could all be done with "new CWnd(.." in
MFC.

Richard
Feb 18 2003
next sibling parent reply "Walter" <walter digitalmars.com> writes:
What I do in such situations is I wrote my own printf() that just opened a
log file, appended a line to it, and closed the file (this was so that even
if the program crashed, the log file would be up to date). It works very
well.
Feb 18 2003
parent Richard Grant <fractal clark.net> writes:
In article <b2v3e1$1mr2$1 digitaldaemon.com>, Walter says...
What I do in such situations is I wrote my own printf() that just opened a
log file, appended a line to it, and closed the file (this was so that even
if the program crashed, the log file would be up to date). It works very
well.

Thanks. Yes that works very well, but its still somewhat discouraging since you don't get "tail -f" on windows. But perhaps there is a gnu utility.. I use something similiar for syslog/debuglog type behavior - it works well as of the recent DM C++ release (thanks again Walter and Christof for your efforts to get Stlport up to speed). The following is just an example. As long as std::endl is used to flush the stream, the log file stays up to date. I'm sure Walter would find it useless for several reasons, and in multi-threaded applications there are also problems, but for me, it is an appwide syslog like construct that accepts complex types. In the example below, I usually have "util::System sys" defined in another file, and then make sure it becomes visible by requiring "util::sys.redirect(const std::string& logfileprefix)" before log start. Depending on the project, I also redirect clog and/or cout (usually to different files, hence logfileprefix above). // redirect cerr to file example #include <iostream> #include <fstream> namespace util { class System { public: System() { errfile = new std::ofstream("example.log"); std::streambuf* errbuf = errfile->rdbuf(); cerrbuf = std::cerr.rdbuf(); std::cerr.rdbuf(errbuf); } ~System() { std::cerr.rdbuf(cerrbuf); delete errfile; } private: System(const System& s) { } System& operator=(const System& s) { return *this; } std::ofstream* errfile; std::streambuf* cerrbuf; }; extern System sys; } // util util::System sys; int main() { std::cerr << "uh oh" << std::endl; } Richard
Feb 19 2003
prev sibling parent reply Jan Knepper <jan smartsoft.us> writes:
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Try attached files... They might make your life easier.
Jan



Richard Grant wrote:

 Hi,

 I just finished a project in MFC, and everything was going smoothly right up
 until the end. Defining the interface with resource editor was easy, creating
 controls in code was trivial.. Using the Data Exchange to get access to the
 control data seemed a little contrived, but worked well. All in all a fairly
 complete application with minimal effort.

 Then I ran into an intermittent access violation. Ok, so I reached into my
usual
 bag of tricks, and realized - how was I going to output program state using a
 windowing interface that was itself exhibiting problems. Rats. No std::cout to
 play with, and typed variables everywhere. Also the program was 3MB and my code
 couldn't have been more then 2 or 3 hundred lines.

 Anyway, after I finished stuffing MessageBox with stringstream managed strings,
 and dropping them into various places, I eventually found the problem. It
looked
 like something to do with Thread Local Storage being instantiated improperly.
 I've worked with TLS before, so I looked around in the MFC code but gave up
 after about 10 seconds.

 I just restructured the code in places that seemed suspect: 1) using a static
 member function as control routine of AfxBeginThread, 2) having my own defined
 default c'tor and d'tor for theapp, 3) Passing the class of the static member
 function as the argument of the thread start, and casting from void to the
 class. And 4) starting the thread in oninitdialog instead of initializeapp.
 Everything seemed to work fine after that. Not that I thought what I was doing
 was illegal, just that they seemed like places the framework authors might get
 confused.

 The point is, I think I would have spent considerably more time trying to
 identify the problem if there were an iostream based variable like "win::cout"
 ("win" being a namespace qualifier). I did some minimum looking around, and was
 amazed that there was very little material on this issue. What with the
 continued success of Windowing environments, I would have thought that such a
 subject would have already been exhausted.

 I did find a macro at the Code Project "cout", but I hate macros - mostly due
to
 the if (bool) macro; problem where the macro expands into more than one line.
 But that's really a personal distaste. Still, you get some typing and such, but
 I don't really like the format. I prefer to decide when a newline will appear,
 and what title to give my output. And why should I define the console?

 Since I don't program that often for GUI deployments, I am not certain what
 features win::cout would have, and what would it mean to do:

 int i = 10;
 win::cout << i << win::endl;

 Would another window appear.. would it be a message box? would it be a window
 with menu, exit and just an edit control? Would the program halt execution?

 There are probably resources that offer the windows (or windowing) equivalent
of
 variables that support stdout - a paper on the subject at University level?

 Perhaps I'm just being silly, and it could all be done with "new CWnd(.." in
 MFC.

 Richard

Feb 19 2003
parent reply Richard Grant <fractal clark.net> writes:
In article <3E53A0E5.AA4818EA smartsoft.us>, Jan Knepper says...
Try attached files... They might make your life easier.
Jan

I can see you have had this problem before! Looking at your attachments, I can see that you have thought through the various types of information that might be required, and how it would effect program state to display that information (ie - stop the thread with messgebox, or continuous flow, etc.). The analysis of the problem demonstrated by your attachment is exactly what I was looking for. Thanks very much for sharing. Richard
Feb 19 2003
parent reply Jan Knepper <jan smartsoft.us> writes:
The problem, or alike problem are very common in software development... Quite
a few
of the systems  I have build are Real Time Systems... For initial testing may
be you
can do something with a debugger, but while the system is running full speed you
would interrupt the flow with a debugger... Tracing like this L_TRACE variations
with I used most work best... They have been implemented for Win32 and Unix...

Jan



Richard Grant wrote:

 In article <3E53A0E5.AA4818EA smartsoft.us>, Jan Knepper says...
Try attached files... They might make your life easier.
Jan

I can see you have had this problem before! Looking at your attachments, I can see that you have thought through the various types of information that might be required, and how it would effect program state to display that information (ie - stop the thread with messgebox, or continuous flow, etc.). The analysis of the problem demonstrated by your attachment is exactly what I was looking for. Thanks very much for sharing. Richard

Feb 19 2003
parent Richard Grant <fractal clark.net> writes:
In article <3E54114F.197D3B0 smartsoft.us>, Jan Knepper says...
The problem, or alike problem are very common in software development... Quite
a few
of the systems  I have build are Real Time Systems... For initial testing may
be you
can do something with a debugger, but while the system is running full speed you
would interrupt the flow with a debugger... Tracing like this L_TRACE variations
with I used most work best... They have been implemented for Win32 and Unix...

And thanks again. My original question was more oriented toward finding an already implemented windows output stream or creating a knowledge base for building a windows output stream for later use (should I need to make another GUI based project). And your material was absolutely right on. I've noticed that for windows, there seems to be a general settling on using DebugOutA API and something like the SysInternals offering of DebugView to capture debug output at runtime. Then derive from basic_streambuf<> and associate that with an ostream to get good output behavior (from Rogue Wave materials). Another method comes from Dietmar Kuhl to derive behavior from streambuf to make a stream capable Motif Widget. Dietmar may also have submitted a windows based stream lib to the folks at boost, and I'm looking into that. I like the use of streams in both underlying implementations, but frankly, I prefer your approach to determining "Where" the output will appear, or perhaps in stream language, the manipulators of the stream. And your TraceTitle is very innovative. In the end, I hope to create a variable like this one below. namespace win { extern windows_ostream<char> werr; } // win Stream manipulators could determine where the output appears. Perhaps something like: int i = 99; // message to tile bar.. win::show causes display in title bar win::werr << win::win_base::title << "var is: " << i << win::show; // message to DebugOutA.. win::show causes new line and flush win::werr << win::win_base::debug << "var is: " << i << win::show; // messagebox.. win::show causes display of message box win::werr << win::win_base::msg << "var is: " << i << win::show; // redirect to log file. std::fstream logfile("c:\\file.log"); std::streambuf* file = logfile.rdbuf(); std::streambuf* s_werr = win::werr.rdbuf(); win::werr.rdbuf(file); // output goes to file.log.. win::show causes newline and flush win::werr << "var is: " << i << win::show; I'm not sure it is wise to reuse the "c" based name win::cout, since even in namespace scope, cout/err/log are *very* reserved words, and some programmers are very much in the habit of doing "use namespace blah". So that requires some thought. I have used werr above. This may be effective since std::wcerr is so unused. The redirection to a log file is such an integral part of the expectations of such a stream class, that it might be better to add a different layer than the manipulators. So that in addition to manipulator behavior, output could be directed to a log file and/or other targets: int i = 99; win::cdbg.logstart("c:\\file.log"); // below outputs to DebugOutA and c:\file.log win::cdbg << win::win_base::debug << "var is: " << i << std::endl; win::cdbg.logstop(); / below outputs only to DebugOutA win::cdbg << "var is: " << i << std::endl; Also, while title (TraceTitle), logstart (TraceLog) and debug (new concept TraceWindow) fit well into the output stream model, TraceMessage halts execution of the program, and has other issues (such as potentially being an input source). In fact, it could be argued that MessageBox is really the windows equivalant of an input stream ala cin. I guess that requires more thought. If you don't mind my asking, using your Macros, what approach do you take with regard to threading issues in the real time systems you have developed? I ask, becuase I imagine that simultaneous access to the same output medium might create a wait condition that effects the behavior of worker threads. Perhaps for TraceWindow and TraceFile you use different targets? Richard
Feb 20 2003