From: aking@trees.az05.bull.com (Andy King)
Subject: Re:Implementing a 'message' class
Reply-To: aking@trees.az05.bull.com (Andy King)
Organization: Bull HN Information Systems Inc., Phoenix Product Division
Date: Mon, 23 Nov 92 23:59:54 GMT


I received two responses to my original questions, from Skip Carter and
Steve Clamage.  These responses 'inspired' the code which follows at the
end of this article.

I am posting this follow-up to both the C++ group and the windows
programming group.  The code is actually more appropriate to a
Borland newsgroup, but I don't know of one.

Basically, I wanted a facility for displaying debug or trace messages
in a GUI based environment, sending these messages to a window, instead
of to standard or error out.  Additionally, I wanted the option of
writing the output to a file, in addition to the display window.

My solution is actually for the Borland OWL (Object Windows Library)
environment - it won't work with other GUIs.  But the approach should
adapt easily to other environments.  There are two facilities
within the include file at the end of this article: the first is a
redefinition of the 'assert()' macro; the second is the message display
('owldebug') class definition.

The 'assert()' macro is overridden to display a message box, and provides
the option to continue with execution of the program, or to exit.  This
doesn't have any relationship to the original request, but I thought
it was appropriate.

There are comments in the code describing how to use the 'owldebug'
class.  I have not separated the declaration and definition sections
(into '.cpp' and '.h' files).  An additional note to these comments is
that the output window can be updated from the keyboard while the program
is running; I had considered disabling input, but in retrospect decided
that it may be useful.  For example, it facilitates annotation of the
displayed values.

Some questions:

  - is it safe to just 'exit()' a Windows program?  Almost certainly
    not, since destructors will not be called.  So, what should be
    done, instead of using 'exit()'?

  - how do I reset the stream buffer - at present, I call 'memset()'
    and 'seekp(beg)', but the 'memset()' seems unnecessary (the results
    were unreliable without this call).

  - how do I set the text position to the end of the 'Editor' buffer?
    At present, output will occur at the current cursor position.

  - are there any Borland-specific newsgroups?  (We don't receive
    most of the 'alt' groups, but I could request them).

If anyone finds this code interesting or useful, I'd appreciate an
email message, with just a 'thanks!'.  If you have questions, comments,
or suggestions, I'd welcome those, too.

Thanks,

Andy King.

email: aking@trees.az05.bull.com


//----------------------------- owldebug.h -----------------------------------\\

#ifndef __OWLDEBUG_H
#define __OWLDEBUG_H

#include <strstream.h>
#include <filewnd.h>

//----------------------------------------------------------------------------\\

//
// The following macro and function definitions provide an alternative 'assert'
// mechanism, for use in a Windows program.  When 'assert()' is used, this
// function will display a message box, with the standard assert information.
// An option is provided to 'exit()' the program, or to continue.
//

#ifndef NDEBUG
#    ifdef assert
#        undef assert
#    endif
#    define assert(expression) if (!(##expression)) \
                                   winassert(#expression, __FILE__, __LINE__)
#endif

void
winassert(char* expression, char* filename, long line_number)
{
    int        response;
    char       message_buffer[200];
    ostrstream message(message_buffer, sizeof(message_buffer));

    memset(message_buffer, '\0', sizeof(message_buffer));
    message.seekp(ostream::beg);
    message << "Assertion failed: " << expression << endl;
    message << "File: " << filename << "  Line: " << line_number;
    if (errno != 0)
        message << "  errno: " << errno;
    message << endl;
    message << "Do you want to exit the program?" << endl;
    response = MessageBox(NULL, message.str(), "Failed Assertion",
                          MB_YESNO | MB_ICONHAND | MB_SYSTEMMODAL);
    if (response == IDYES)
        exit(1);
} // end of winassert()

//----------------------------------------------------------------------------\\

//
// The 'debug_window_class' provides the output window for the 'owldebug_class'.
// It is implemented as a 'TFileWindow', which provides both the 'EditWindow'
// facilities (e.g., automatic scrolling) and the facility for saving the
// output to a file.
//

class debug_window_class : public TFileWindow
{
    public:
        debug_window_class(PTWindowsObject parent);
        virtual void  SetupWindow();
        virtual LPSTR GetClassName();
        virtual BOOL  CanClear();
}; // end of debug_window_class

//----------------------------------------------------------------------------\\

debug_window_class::debug_window_class(PTWindowsObject parent)
    : TFileWindow(parent, "Debug", "")
{

    Attr.Style = WS_POPUP | WS_OVERLAPPEDWINDOW | WS_VISIBLE;
} // end of debug_window_class::debug_window_class()

//----------------------------------------------------------------------------\\

void
debug_window_class::SetupWindow()
{
    int        width, height;
    HGDIOBJ    font_handle;
    HDC        device_context_handle;
    HMENU      menu_bar_handle;
    TEXTMETRIC text_metrics;

    TFileWindow::SetupWindow();
    // set the font to be used in the debug window
    font_handle = GetStockObject(ANSI_FIXED_FONT);
    SendMessage(Editor->HWindow, WM_SETFONT, (WPARAM)font_handle, 0);
    // find out the character size in the debug window
    device_context_handle = GetDC(Editor->HWindow);
    GetTextMetrics(device_context_handle, &text_metrics);
    ReleaseDC(Editor->HWindow, device_context_handle);
    // resize the debug window to about 25 rows of 30 characters
    width = text_metrics.tmAveCharWidth * 32;
    height = text_metrics.tmHeight * 27;
    MoveWindow(HWindow, 0, 0, width, height, FALSE);
    // create the 'Save...' menu entry
    menu_bar_handle = CreateMenu();
    AppendMenu(menu_bar_handle, MF_STRING, CM_FILESAVEAS, "Save...");
    SetMenu(HWindow, menu_bar_handle);
} // end of debug_window_class::SetupWindow()

//----------------------------------------------------------------------------\\

LPSTR
debug_window_class::GetClassName()
{

    return("debug_window");
} // end of debug_window_class::GetClassName()

//----------------------------------------------------------------------------\\

// The 'CanClear()' member function is overridden so that closing of the
// debug window will never be prevented, even if the output has not been
// saved to file.

BOOL
debug_window_class::CanClear()
{

    return(TRUE); // always allow clearing of the edit area
} // end of debug_window_class::CanClear()

//----------------------------------------------------------------------------\\

// The 'owldebug_class' provides a stream based output mechanism for debug and
// trace messages from an OWL program.  It is derived from 'ostrstream', and
// thus provides all stream output functionality (i.e., 'operator<<()' can
// be used for all types on which it is defined).
//
// When the owldebug_class 'initialize()' member function is called, a
// window is created, in which all subsequent stream output for that object
// is displayed.  The function 'endod()' must be used to output the stream
// to the debug window.  Don't use 'endl' in the stream output; 'endod' will
// output a carriage return and line feed.
//
// Recommended use:
//   - #include <filedial.dlg> in the '.rc' file for the project.  Without
//     this, no 'save' of the output can occur
//   - #include <owldebug.h> in any source unit which requires debug output
//   - instantiate an 'owldebug_class' variable before any of the functions in
//     the source unit which contains 'WinMain()'; declare the variable as
//     'extern' in other source units
//   - following the creation of the main window, in 'InitMainWindow()',
//     initialize the 'owldebug_class' variable using the 'initialize()'
//     member function.  The owldebug_class 'initialize()' function
//     takes a window object pointer as its argument (e.g.,
//     owldebug_var.initialize(MainWindow))
//   - send output to the debug window using the stream output operator with
//     the 'owldebug_class' variable.  For example:
//         owldebug_var << "Load succeeded" << endod;
//         owldebug_var << "Got message " << (int)message.LP.Hi << ','
//                      << (int)message.LP.Lo << endod;
//
// If the 'owldebug_class' instance is not initialized (by calling the
// 'initialize()' member function) there will obviously be no display of
// messages - but this will not cause a failure in execution.  Whether
// instantiated or not, output to an 'owldebug_class' instance will cause
// degradation in performance.
//
// It is feasible to create multiple instances of the 'owldebug_class', and
// to use the different instances for distinct purposes (e.g., varying
// levels of debugging).
//
 
class owldebug_class : public ostrstream
{
    public:
        owldebug_class();
        void initialize(PTWindowsObject parent_window);
        friend ostream& endod(ostream& debug);
    private:
        debug_window_class* debug_window;
        char                stream_buffer[500];

        void display_message();
}; // end of owldebug_class

//----------------------------------------------------------------------------\\

owldebug_class::owldebug_class()
    : ostrstream(stream_buffer, sizeof(stream_buffer))
{

    memset(stream_buffer, '\0', sizeof(stream_buffer));
    debug_window = NULL;
} // end of owldebug_class::owldebug_class()

//----------------------------------------------------------------------------\\

void
owldebug_class::initialize(PTWindowsObject parent_window)
{

    debug_window = new debug_window_class(parent_window);
    parent_window->GetApplication()->MakeWindow((PTWindowsObject)debug_window);
} // end of owldebug_class::initialize()

//----------------------------------------------------------------------------\\

void
owldebug_class::display_message()
{

    if (debug_window != NULL)
    {
        debug_window->Editor->Insert(str());
        debug_window->Editor->Insert("\r\n");
    }
    memset(stream_buffer, '\0', sizeof(stream_buffer));
    seekp(beg);
} // end of owldebug_class::display_message()

//----------------------------------------------------------------------------\\

ostream&
endod(ostream& debug)
{
    owldebug_class* owldebug = (owldebug_class*)&debug;

    owldebug->display_message();
    return((owldebug_class&)debug);
} // end of endod()

//----------------------------------------------------------------------------\\

#endif // ifndef __OWLDEBUG_H
Newsgroups: comp.lang.c++