home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #27 / NN_1992_27.iso / spool / comp / lang / cplus / 16810 < prev    next >
Encoding:
Text File  |  1992-11-24  |  11.0 KB  |  302 lines

  1. Xref: sparky comp.lang.c++:16810 comp.os.ms-windows.programmer.misc:3628
  2. Newsgroups: comp.lang.c++,comp.os.ms-windows.programmer.misc
  3. Path: sparky!uunet!think.com!cass.ma02.bull.com!catfish.az05.bull.com!news
  4. From: aking@trees.az05.bull.com (Andy King)
  5. Subject: Re:Implementing a 'message' class
  6. Message-ID: <1992Nov23.235954.4348@catfish.az05.bull.com>
  7. Sender: news@catfish.az05.bull.com (placeholder for future)
  8. Reply-To: aking@trees.az05.bull.com (Andy King)
  9. Organization: Bull HN Information Systems Inc., Phoenix Product Division
  10. Date: Mon, 23 Nov 92 23:59:54 GMT
  11. Lines: 289
  12.  
  13.  
  14. I received two responses to my original questions, from Skip Carter and
  15. Steve Clamage.  These responses 'inspired' the code which follows at the
  16. end of this article.
  17.  
  18. I am posting this follow-up to both the C++ group and the windows
  19. programming group.  The code is actually more appropriate to a
  20. Borland newsgroup, but I don't know of one.
  21.  
  22. Basically, I wanted a facility for displaying debug or trace messages
  23. in a GUI based environment, sending these messages to a window, instead
  24. of to standard or error out.  Additionally, I wanted the option of
  25. writing the output to a file, in addition to the display window.
  26.  
  27. My solution is actually for the Borland OWL (Object Windows Library)
  28. environment - it won't work with other GUIs.  But the approach should
  29. adapt easily to other environments.  There are two facilities
  30. within the include file at the end of this article: the first is a
  31. redefinition of the 'assert()' macro; the second is the message display
  32. ('owldebug') class definition.
  33.  
  34. The 'assert()' macro is overridden to display a message box, and provides
  35. the option to continue with execution of the program, or to exit.  This
  36. doesn't have any relationship to the original request, but I thought
  37. it was appropriate.
  38.  
  39. There are comments in the code describing how to use the 'owldebug'
  40. class.  I have not separated the declaration and definition sections
  41. (into '.cpp' and '.h' files).  An additional note to these comments is
  42. that the output window can be updated from the keyboard while the program
  43. is running; I had considered disabling input, but in retrospect decided
  44. that it may be useful.  For example, it facilitates annotation of the
  45. displayed values.
  46.  
  47. Some questions:
  48.  
  49.   - is it safe to just 'exit()' a Windows program?  Almost certainly
  50.     not, since destructors will not be called.  So, what should be
  51.     done, instead of using 'exit()'?
  52.  
  53.   - how do I reset the stream buffer - at present, I call 'memset()'
  54.     and 'seekp(beg)', but the 'memset()' seems unnecessary (the results
  55.     were unreliable without this call).
  56.  
  57.   - how do I set the text position to the end of the 'Editor' buffer?
  58.     At present, output will occur at the current cursor position.
  59.  
  60.   - are there any Borland-specific newsgroups?  (We don't receive
  61.     most of the 'alt' groups, but I could request them).
  62.  
  63. If anyone finds this code interesting or useful, I'd appreciate an
  64. email message, with just a 'thanks!'.  If you have questions, comments,
  65. or suggestions, I'd welcome those, too.
  66.  
  67. Thanks,
  68.  
  69. Andy King.
  70.  
  71. email: aking@trees.az05.bull.com
  72.  
  73.  
  74. //----------------------------- owldebug.h -----------------------------------\\
  75.  
  76. #ifndef __OWLDEBUG_H
  77. #define __OWLDEBUG_H
  78.  
  79. #include <strstream.h>
  80. #include <filewnd.h>
  81.  
  82. //----------------------------------------------------------------------------\\
  83.  
  84. //
  85. // The following macro and function definitions provide an alternative 'assert'
  86. // mechanism, for use in a Windows program.  When 'assert()' is used, this
  87. // function will display a message box, with the standard assert information.
  88. // An option is provided to 'exit()' the program, or to continue.
  89. //
  90.  
  91. #ifndef NDEBUG
  92. #    ifdef assert
  93. #        undef assert
  94. #    endif
  95. #    define assert(expression) if (!(##expression)) \
  96.                                    winassert(#expression, __FILE__, __LINE__)
  97. #endif
  98.  
  99. void
  100. winassert(char* expression, char* filename, long line_number)
  101. {
  102.     int        response;
  103.     char       message_buffer[200];
  104.     ostrstream message(message_buffer, sizeof(message_buffer));
  105.  
  106.     memset(message_buffer, '\0', sizeof(message_buffer));
  107.     message.seekp(ostream::beg);
  108.     message << "Assertion failed: " << expression << endl;
  109.     message << "File: " << filename << "  Line: " << line_number;
  110.     if (errno != 0)
  111.         message << "  errno: " << errno;
  112.     message << endl;
  113.     message << "Do you want to exit the program?" << endl;
  114.     response = MessageBox(NULL, message.str(), "Failed Assertion",
  115.                           MB_YESNO | MB_ICONHAND | MB_SYSTEMMODAL);
  116.     if (response == IDYES)
  117.         exit(1);
  118. } // end of winassert()
  119.  
  120. //----------------------------------------------------------------------------\\
  121.  
  122. //
  123. // The 'debug_window_class' provides the output window for the 'owldebug_class'.
  124. // It is implemented as a 'TFileWindow', which provides both the 'EditWindow'
  125. // facilities (e.g., automatic scrolling) and the facility for saving the
  126. // output to a file.
  127. //
  128.  
  129. class debug_window_class : public TFileWindow
  130. {
  131.     public:
  132.         debug_window_class(PTWindowsObject parent);
  133.         virtual void  SetupWindow();
  134.         virtual LPSTR GetClassName();
  135.         virtual BOOL  CanClear();
  136. }; // end of debug_window_class
  137.  
  138. //----------------------------------------------------------------------------\\
  139.  
  140. debug_window_class::debug_window_class(PTWindowsObject parent)
  141.     : TFileWindow(parent, "Debug", "")
  142. {
  143.  
  144.     Attr.Style = WS_POPUP | WS_OVERLAPPEDWINDOW | WS_VISIBLE;
  145. } // end of debug_window_class::debug_window_class()
  146.  
  147. //----------------------------------------------------------------------------\\
  148.  
  149. void
  150. debug_window_class::SetupWindow()
  151. {
  152.     int        width, height;
  153.     HGDIOBJ    font_handle;
  154.     HDC        device_context_handle;
  155.     HMENU      menu_bar_handle;
  156.     TEXTMETRIC text_metrics;
  157.  
  158.     TFileWindow::SetupWindow();
  159.     // set the font to be used in the debug window
  160.     font_handle = GetStockObject(ANSI_FIXED_FONT);
  161.     SendMessage(Editor->HWindow, WM_SETFONT, (WPARAM)font_handle, 0);
  162.     // find out the character size in the debug window
  163.     device_context_handle = GetDC(Editor->HWindow);
  164.     GetTextMetrics(device_context_handle, &text_metrics);
  165.     ReleaseDC(Editor->HWindow, device_context_handle);
  166.     // resize the debug window to about 25 rows of 30 characters
  167.     width = text_metrics.tmAveCharWidth * 32;
  168.     height = text_metrics.tmHeight * 27;
  169.     MoveWindow(HWindow, 0, 0, width, height, FALSE);
  170.     // create the 'Save...' menu entry
  171.     menu_bar_handle = CreateMenu();
  172.     AppendMenu(menu_bar_handle, MF_STRING, CM_FILESAVEAS, "Save...");
  173.     SetMenu(HWindow, menu_bar_handle);
  174. } // end of debug_window_class::SetupWindow()
  175.  
  176. //----------------------------------------------------------------------------\\
  177.  
  178. LPSTR
  179. debug_window_class::GetClassName()
  180. {
  181.  
  182.     return("debug_window");
  183. } // end of debug_window_class::GetClassName()
  184.  
  185. //----------------------------------------------------------------------------\\
  186.  
  187. // The 'CanClear()' member function is overridden so that closing of the
  188. // debug window will never be prevented, even if the output has not been
  189. // saved to file.
  190.  
  191. BOOL
  192. debug_window_class::CanClear()
  193. {
  194.  
  195.     return(TRUE); // always allow clearing of the edit area
  196. } // end of debug_window_class::CanClear()
  197.  
  198. //----------------------------------------------------------------------------\\
  199.  
  200. // The 'owldebug_class' provides a stream based output mechanism for debug and
  201. // trace messages from an OWL program.  It is derived from 'ostrstream', and
  202. // thus provides all stream output functionality (i.e., 'operator<<()' can
  203. // be used for all types on which it is defined).
  204. //
  205. // When the owldebug_class 'initialize()' member function is called, a
  206. // window is created, in which all subsequent stream output for that object
  207. // is displayed.  The function 'endod()' must be used to output the stream
  208. // to the debug window.  Don't use 'endl' in the stream output; 'endod' will
  209. // output a carriage return and line feed.
  210. //
  211. // Recommended use:
  212. //   - #include <filedial.dlg> in the '.rc' file for the project.  Without
  213. //     this, no 'save' of the output can occur
  214. //   - #include <owldebug.h> in any source unit which requires debug output
  215. //   - instantiate an 'owldebug_class' variable before any of the functions in
  216. //     the source unit which contains 'WinMain()'; declare the variable as
  217. //     'extern' in other source units
  218. //   - following the creation of the main window, in 'InitMainWindow()',
  219. //     initialize the 'owldebug_class' variable using the 'initialize()'
  220. //     member function.  The owldebug_class 'initialize()' function
  221. //     takes a window object pointer as its argument (e.g.,
  222. //     owldebug_var.initialize(MainWindow))
  223. //   - send output to the debug window using the stream output operator with
  224. //     the 'owldebug_class' variable.  For example:
  225. //         owldebug_var << "Load succeeded" << endod;
  226. //         owldebug_var << "Got message " << (int)message.LP.Hi << ','
  227. //                      << (int)message.LP.Lo << endod;
  228. //
  229. // If the 'owldebug_class' instance is not initialized (by calling the
  230. // 'initialize()' member function) there will obviously be no display of
  231. // messages - but this will not cause a failure in execution.  Whether
  232. // instantiated or not, output to an 'owldebug_class' instance will cause
  233. // degradation in performance.
  234. //
  235. // It is feasible to create multiple instances of the 'owldebug_class', and
  236. // to use the different instances for distinct purposes (e.g., varying
  237. // levels of debugging).
  238. //
  239.  
  240. class owldebug_class : public ostrstream
  241. {
  242.     public:
  243.         owldebug_class();
  244.         void initialize(PTWindowsObject parent_window);
  245.         friend ostream& endod(ostream& debug);
  246.     private:
  247.         debug_window_class* debug_window;
  248.         char                stream_buffer[500];
  249.  
  250.         void display_message();
  251. }; // end of owldebug_class
  252.  
  253. //----------------------------------------------------------------------------\\
  254.  
  255. owldebug_class::owldebug_class()
  256.     : ostrstream(stream_buffer, sizeof(stream_buffer))
  257. {
  258.  
  259.     memset(stream_buffer, '\0', sizeof(stream_buffer));
  260.     debug_window = NULL;
  261. } // end of owldebug_class::owldebug_class()
  262.  
  263. //----------------------------------------------------------------------------\\
  264.  
  265. void
  266. owldebug_class::initialize(PTWindowsObject parent_window)
  267. {
  268.  
  269.     debug_window = new debug_window_class(parent_window);
  270.     parent_window->GetApplication()->MakeWindow((PTWindowsObject)debug_window);
  271. } // end of owldebug_class::initialize()
  272.  
  273. //----------------------------------------------------------------------------\\
  274.  
  275. void
  276. owldebug_class::display_message()
  277. {
  278.  
  279.     if (debug_window != NULL)
  280.     {
  281.         debug_window->Editor->Insert(str());
  282.         debug_window->Editor->Insert("\r\n");
  283.     }
  284.     memset(stream_buffer, '\0', sizeof(stream_buffer));
  285.     seekp(beg);
  286. } // end of owldebug_class::display_message()
  287.  
  288. //----------------------------------------------------------------------------\\
  289.  
  290. ostream&
  291. endod(ostream& debug)
  292. {
  293.     owldebug_class* owldebug = (owldebug_class*)&debug;
  294.  
  295.     owldebug->display_message();
  296.     return((owldebug_class&)debug);
  297. } // end of endod()
  298.  
  299. //----------------------------------------------------------------------------\\
  300.  
  301. #endif // ifndef __OWLDEBUG_H
  302.