Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Examining Symantec C++


JUN94: Examining Symantec C++

Examining Symantec C++

Updating the PT periodic-table program

Michael Yam

Michael is an independent consultant and has served New York's financial district since 1984. He can be reached on CompuServe at 76367,3040.


Symantec's C++ Professional Compiler is an eclectic collection of tools that includes the Symantec C++ compiler (formerly Zortech C++), SLR Systems' Optlink linker, the Multiscope debugger, visual tools from Blue Sky and the Whitewater Group, and version 2.0 of the Microsoft Foundation Class (MFC) library. Furthermore, Symantec provides access to (but does not bundle) Intersolv's PVCS toolkit for team development and version control.

Of course, simply collecting a set of powerful programming tools such as these is one thing; integrating them into a cohesive working environment is another. In particular, I was curious as to how the compiler handles the subtleties of MFC, since I've done quite a bit of MFC 2.0 development. In "Examining MFC 2.0" (DDJ, June 1993), for instance, I created a Windows application, called PT, which displays a periodic table using a modeless dialog box as its main window. PT allows the user to point and click on atomic elements in the periodic table, then displays edit fields containing the name, symbol, atomic number, and atomic weight of that element. In this article, I'll revisit PT and examine some of the changes required to get it running under Symantec C++ (SC++) 6.x. Finally, I've used Symantec C++ 6.0 in this project. Symantec has since issued version 6.1, a maintenance release available free of charge to registered 6.0 users. For the purposes of porting PT, I've noted the differences between versions.

The SC++ Environment

If you plan a complete installation of SC++, be prepared to give up about 53 Mbytes of disk space. For that, you'll get a complete environment that supports DOS and Windows development. I found that a workable configuration which supported Windows development, the large memory model, MFC libraries, help files, and sample programs, required 38 Mbytes. If you use a CD-ROM drive, SC++ can be configured to occupy only 12 Mbytes of your hard disk. To run SC++, you'll need at least a 386 system and 4 Mbytes of RAM. For practical purposes, however, you really should have a 33-MHz/486 with 8 Mbytes of RAM, although I found that even that was slow when compiling with full debugging information. (Full debugging information is necessary if you want to use Multiscope to browse class libraries, such as MFC, and to debug DLLs.)

I also recommend a Super VGA display. The integrated development and debugging environment (IDDE) can get cluttered, and the extra surface area can save you from getting lost. To minimize this clutter, SC++ implements five virtual screens to separate editing, source-level debugging, low-level debugging, compiling, and output viewing (Figure 1). The IDDE is not a classic MDI application--the windows "float," much like those in Microsoft's Visual Basic. It's a matter of personal taste, but after working in the IDDE for a couple of days, I discovered I liked floating windows.

The IDDE, however, is not without problems. It lacks, for instance, context-sensitive help. If you highlight strcpy in the editor and press F1, both the Microsoft and Borland environments will bring up a description of that topic. With Symantec's IDDE, however, you must open the proper help file and request a search. The editor could also be improved. For example, while it allows Shift+Del to cut text, it requires Ctrl+V to insert text. Finally, when restarting SC++, the IDDE intelligently reloads your workspace but neglects to reload your project.

Drawing Hydrogen

In dusting off PT, I couldn't resist the temptation to enhance the program. With the help of the MFC device context class (CDC), this version of PT can display the atomic structure of hydrogen; see Figure 2. The model is simple, though it's possibly incorrect in the context of quantum mechanics. (A more accurate rendering would describe hydrogen with a nucleus surrounded by an electron cloud, or electron-density distribution corresponding to a wave function.) The class CATOM (Listing One, page 100) stores drawing methods and structural information (the number of electrons and the number of shells). The instantiation and drawing of hydrogen takes place in a modeless dialog box. Details about the dialog box are encapsulated in the CATOMDialog class, and the object is created when the user clicks on the "Show Atom" bar. When the CATOMDialog object receives a WM_PAINT message, control goes to CATOMDialog::OnPaint(); see Listing Two, page 100.

With SDK techniques, you would need to place your painting code between BeginPaint() and EndPaint() calls. With MFC, however, you can use the CPaintDC class, as shown in Figure 3(a). This isn't much better because you still wind up sandwiching your code. A better solution would be to create the CPaintDC object on the stack; see Figure 3(b). When your painting routine is complete, pdc is automatically deleted and the device context freed.

With the device context established for the dialog box, the CATOMDialog object has the CATOM object draw itself. To define the drawing area, I used a group box which is a CWnd object in MFC. Doing so required that I obtain another device context. MFC provided a CClientDC class for just such a purpose, and I declared the object on the stack, like CPaintDC, mentioned earlier. Device contexts are a precious resource (Windows 3.1 permits five), but I used an extra one because it was easier to draw in an area exclusively reserved for an atom. It also generalized the drawing routine to write to any CWnd object.

The remainder of the painting code uses ellipses to draw the hydrogen atom. Note that Ellipse() produces a solid figure; the interior is filled with the current brush. To prevent the electron shell from partially eclipsing the electron and totally covering the nucleus, I drew from the outside-in, or back-to-front: first the electron shell, then the electron, and finally, the nucleus. The nucleus and electron appear solid because their ellipses are filled with a BLACK_BRUSH. To make the electron shell appear transparent, I employed a HOLLOW_BRUSH to fill the ellipse using the background color.

Building PT

Compiling was straightforward, although I had to replace the Microsoft-specific _stricmp() with the standard stricmp(). Also, in one case, I called MessageBox() only to have the compiler tell me it "cannot implicitly convert from: char _near * to: unsigned." This meant that one of my four arguments was of an incorrect type. I double checked them and they appeared correct. Puzzled, I compiled the identical source using Microsoft Visual C++ and received this message: " 'MessageBox' : function does not take four parameters." Microsoft's message was clearer because it reminded me that I was using MFC's MessageBox(), which accepts three arguments, not the SDK's MessageBox(), which takes four.

Upon linking, I was presented with a list of unresolved externals because the linker could not automatically locate the MFC libraries. I had to edit the project file by adding two libraries: LIBW.LIB and LAFXCW.LIB. I also discovered that to work with MFC, I needed the --k flag to keep segments in .DEF order.

Curiosity made me peek at the makefile SC++ produced. I was pleasantly surprised to find that the makefile reproduced in Listing Three (page 100) was comprehensible, unlike those of Microsoft and Borland. In these days of integrated environments and project files, readable makefiles may not seem very important. But, they do offer a small degree of flexibility, especially if you deal with cross-platform development.

Moving PT's resources over to Symantec's Resource Toolkit required only a modicum of effort. The Resource Toolkit did not permit me to load PT.RC directly; it wanted the compiled resource, PT.RES. I had to load PT.RC from the IDDE, where it gave me a choice of editing the resource file visually or as text. Selecting the visual approach compiled PT.RC, producing PT.RES, which was then loaded into the Resource Toolkit. After building PT, running it corrupted the USER kernel.

Postmortem

SC++ comes with MED, an execution monitor reminiscent of, yet more powerful than, Microsoft's Dr. Watson or Borland's WinSpector. MED will trap run-time errors as well as general-protection faults. Also, in the event of an infinite loop or a hung program, pressing Ctrl+Alt+SysRq will force MED to dump to a file. To access this additional debugging power, you need to compile your programs with a MED header file and link with a MED library. I could not test this feature with 6.0 because the compiler could not locate MEDW.H. It turns out that this file was missing in 6.0; it is, however, included in 6.1.

A related debugging tool, the Crash Analyzer, reads the postmortem dump created by MED and helps you determine the point of failure at the source level. Think of it as an interactive version of Borland's WinSpector Assistant (DFA.EXE). Again, I couldn't test this tool because of the missing header file--unfortunate because the Crash Analyzer would have been useful in tracking PT's crash problem.

After compiling and linking with debugging options, PT ran successfully. Since enabling debugging disabled all code optimizations, I hypothesized that PT crashed because of faulty compiler optimizations. Once I recompiled with both debug information and code optimizations set to "none," PT ran successfully. SC++ provides 11 optimization flags. To determine which option was causing the problem, I built and tested versions of PT with only one flag enabled at a time. As it turned out, any of the options will cause PT to die. (I did not test this under 6.1.) I should mention that an MFC sample program, CHKBOOK, compiled and ran successfully with all optimizations enabled. Perhaps PT's unorthodox approach of using a dialog box as the main window was giving the SC++ optimizer problems. (VC++, however, didn't have problems building an optimized PT.)

Extending PT

When I first wrote PT, I took the lazy approach and stored the element names, symbols, atomic numbers, and atomic weights in a structure stored in memory. This worked well because the information was static. Since this updated version of PT only draws hydrogen, I have left the data in memory. If you plan to extend PT to incorporate atomic drawings for all the elements, you should consider maintaining the information in a database. The constructor to CATOM accepts the name of the element, which can be used as a lookup key. If you overload the constructor to accept the atomic number or atomic weight, you can also use them as lookup keys. You can't, however, overload the constructor to accept the atomic symbol because both symbol and element names are character strings.

Hydrogen is instantiated from the CATOM class because hydrogen is a kind of atom. When creating instances of other atoms, resist the temptation to derive atoms from other atoms. For example, you wouldn't want to derive helium, which has two electrons, from hydrogen, which has one. Helium is not a kind of hydrogen, and should be instantiated from CATOM. Similarly, avoid deriving "down" the periodic table: Don't derive silicon from carbon, for example. This is tempting because carbon and silicon are in the same family of elements (valence of four) but differ in behavior--carbon is the stuff of organic life, silicon the stuff of computer life. Yet, can silicon be considered a kind of carbon? If a particular derivation sparks a philosophical debate, it must be an ambiguous object, and thus, can only serve your program in an ambiguous fashion. There is little to discuss when stating carbon or silicon is a kind of atom.

Conclusion

Symantec C++ is a natural upgrade path for Zortech users. The Zortech compiler has traditionally appealed to an elite group, gaining special capabilities usually before Microsoft and Borland, such as 16- and 32-bit versions, DOS-extender support, cross-platform capabilities, and native C++. Under Symantec, Zortech users will have access to a useful development environment, debugger and linker, class library, and visual-programming tools. SC++ users upgrading to 6.1 will also benefit from new features such as support for debugging templates, syntax-directed color highlighting, an improved project manager, and a 32-bit version of MFC 2.0 (CD-ROM version only).

However, the advantages for Microsoft and Borland users are less clear. SC++ is no longer the only package sporting a 32-bit compiler for Windows. With Microsoft's Visual C++ 32-bit edition and Borland C++ 4.0, there is less of a compelling reason to make the switch. Additionally, visual tools, GUI class libraries and Windows-hosted development environments have become standard fare. Symantec does offer an advantage to developers by including the Multiscope debugger and Optlink linker, which are superior tools. Microsoft and Borland developers, however, have access to these same tools as third-party add-ons.

Figure 1: SC++ implements five virtual screens to separate editing, source-level debugging, low-level debugging, compiling, and output viewing.

Figure 2: Updated version of PT.

Figure 3: (a) MFC's CPaintDC method; (b) creating the CPaintDC object on the stack.

<b>(a)</b>
CPaintDC *pdc = new 
CPaintDC(this);
    [painting routine here]
delete pdc;

<b>(b)</b>
CPaintDC pdc(this);
[painting routine here]

[LISTING ONE]



//----- PTATOM.H - Declares class interface for Periodic Table ---

#ifndef __PTATOM_H__
#define __PTATOM_H__

#include "ptdefs.h"

class CATOM
{
private:
    char Name[PT_NAMELEN+1];
    int NumberOfElectrons;
    int NumberOfShells;
public:
    CATOM (char *szName);
    ~CATOM();
    int DrawAtom (CWnd *Parent);
};
class CATOMDialog : public CDialog
{
private:
    CATOM *Atom;
public:
    CATOMDialog (char *AtomName);
    ~CATOMDialog ();

    //{{ AFX_MSG (CATOMDialog)
    afx_msg void OnPaint();
    afx_msg void OnOK();

    //}} AFX_MSG

    DECLARE_MESSAGE_MAP()
};
#endif


[LISTING TWO]



//------ PTATOM.CPP - Periodic Table for Windows -------

#include <afxwin.h>
#include <windows.h>

#include <string.h>

#include "resource.h"
#include "ptatom.h"

//--------------------------------------------------------------------------
// CATOMDialog Constructor -- Creates a modeless dialog box to display an
//   atom. Also sets dialog caption to atom name and creates atom object.
//--------------------------------------------------------------------------
CATOMDialog::CATOMDialog(char *AtomName)
{
    if (stricmp (AtomName, "Hydrogen"))
    {
        MessageBox ("This version of PT can only draw Hydrogen", "SORRY",
                    MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
        return;
    }
    Atom = new CATOM (AtomName);
    if (Create ("ATOM") == FALSE)
        MessageBox ("Cannot create modeless dialog box.", "ERROR", MB_OK);
    else
        SetWindowText (AtomName);
}
//--------------------------------------------------------------------------
// ~CATOMDialog Destructor -- Destroys atom object and modeless dialog box.
//--------------------------------------------------------------------------
CATOMDialog::~CATOMDialog()
{
    delete Atom;
    DestroyWindow();
}
//--------------------------------------------------------------
// OnOK -- User pressed OK button.  Destroy the dialog box.
//--------------------------------------------------------------
void CATOMDialog::OnOK()
{
    delete this;
}
//---------------------------------------------------------------------
// OnPaint -- Received a WM_PAINT message. Get the device context and
//      draw the atom inside the GroupBox.
//---------------------------------------------------------------------
void CATOMDialog::OnPaint()
{
    CPaintDC pdc(this);     // paint device context on stack
    CWnd *GroupBox = GetDlgItem (IDD_GROUPBOX);
    if (GroupBox != NULL && Atom != NULL)
        Atom->DrawAtom (GroupBox);
}
//--------------------------------------------------------------
// CATOM Constructor
//--------------------------------------------------------------
CATOM::CATOM (char *szName)
{
    strcpy (Name, szName);
}
//-----------------------------------------------------------------------
// CATOM Destructor -- No handling necessary.  Included for completeness.
//------------------------------------------------------------------------
CATOM::~CATOM ()
{
}
//-------------------------------------------------------------------------
// DrawAtom - This method only draws Hydrogen atom. Output goes to CWnd object.
//   Drawing is done from outside-in: electron shell, electron, then nucleus.
//-------------------------------------------------------------------------
int CATOM::DrawAtom(CWnd *Parent)
{
    RECT rc;
    int HorzSF, VertSF;     // scale factors

    CClientDC pdc(Parent);  // client device context on stack

    // scale down rectangle.  Use ellipse() to describe
    // electron orbits.around nucleus.
    Parent->GetClientRect (&rc);
    HorzSF = rc.right/5;
    VertSF = rc.bottom/5;

    rc.left = HorzSF;
    rc.top = VertSF;
    rc.right -= rc.left;
    rc.bottom -= rc.top;

    pdc.SelectStockObject (HOLLOW_BRUSH);
    pdc.SelectStockObject (BLACK_PEN);
    pdc.Ellipse (&rc);      // electron orbit

    // set up to draw electron.  Easier if we position
    // at 12 o'clock.
    rc.left = rc.left + (rc.right - rc.left)/2 - 4;
    rc.top -= 4;
    rc.right = rc.left + 8;
    rc.bottom = rc.top + 8;

    pdc.SelectStockObject (BLACK_BRUSH);
    pdc.Ellipse (&rc);      // electron

    // scale down rectangle to draw nucleus.
    Parent->GetClientRect (&rc);

    rc.left = HorzSF*2;
    rc.top = VertSF*2;
    rc.right -= rc.left;
    rc.bottom -= rc.top;

    pdc.SelectStockObject (BLACK_BRUSH);
    pdc.Ellipse (&rc);      // nucleus

    return 0;
}
//--------------------------------------------------------------
//    MESSAGE MAP
//--------------------------------------------------------------
BEGIN_MESSAGE_MAP (CATOMDialog, CDialog)
    //{{ AFX_MSG_MAP (CPTATOMDialog)
    ON_WM_CLOSE ()
    ON_COMMAND (IDOK, OnOK)
    ON_WM_PAINT ()
    //}} AFX_MSG_MAP
END_MESSAGE_MAP()


[LISTING THREE]



ORIGIN      = Symantec C++
ORIGIN_VER  = Version 6.0
VERSION     = DEBUG

PROJ        = SCPT
APPTYPE     = WINDOWS EXE
PROJTYPE    = EXE

CC          = SC
MAKE        = MAKE
RC          = RCC
HC          = HC
ASM         = SC
DISASM      = OBJ2ASM
LIBR        = IMPLIB
LNK         = LINK
CVPK        = CVPACK

DLLS        =
HEADERS     = pt.h resource.h ..\..\..\sc\mfc\include\afx.h  \
        ..\..\..\sc\mfc\include\afxver_.h  \
        ..\..\..\sc\include\windows.h  \
        ..\..\..\sc\include\shellapi.h  \
        ..\..\..\sc\mfc\include\afxres.h  \
        ..\..\..\sc\mfc\include\afxcoll.h  \
        ..\..\..\sc\include\win16\print.h  \
        ..\..\..\sc\mfc\include\afxmsg_.h  \
        ..\..\..\sc\mfc\include\afxdd_.h  \
        \sc\mfc\include\afx.h  \sc\mfc\include\afxver_.h \
        \sc\include\windows.h  \sc\include\shellapi.h  \
        \sc\mfc\include\afxres.h \sc\mfc\include\afxcoll.h \
        \sc\mfc\include\afxmsg_.h \sc\mfc\include\afxdd_.h  \
        \sc\include\win16\windows.h \
        ptatom.h ptdefs.h
LIBS        = ..\..\..\sc\lib\libw.lib \
        ..\..\..\sc\mfc\lib\lafxcw.lib \
        LIBW.LIB COMMDLG.LIB SHELL.LIB
DEFFILE     = pt.def
CFLAGS      =  -Jm -ml -C -W1 -s -2 -c -g -gh -gf
HFLAGS      =  $(CFLAGS)
LFLAGS      =  /CO /LI /NOI /INF /RC -k :pt.RES
MFLAGS      =
RESFLAGS    =
AFLAGS      = -c
HELPFLAGS   =

MODEL       = L
DEFINES     =
RCDEFINES   =
LIBDIRS     =
INCLUDES    = -I\SC\INCLUDE -I\SC\MFC\INCLUDE

OBJS        =  pt.OBJ  ptatom.OBJ
RCFILES =
RESFILES    =  pt.RES
SYMS        =  pt.SYM  resource.SYM
HELPFILES   =
BATS        =

.C.OBJ:
    $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -o$*.obj $*.c
    $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -o$*.obj $*.cpp
.CXX.OBJ:
    $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -o$*.obj $*.cxx
    $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -o$*.obj $*.cp
.H.SYM:
    $(CC) $(HFLAGS) $(DEFINES) $(INCLUDES) -HF -o$*.sym $*.h
.HPP.SYM:
    $(CC) $(HFLAGS) $(DEFINES) $(INCLUDES) -HF -o$*.sym $*.hpp
.HXX.SYM:
    $(CC) $(HFLAGS) $(DEFINES) $(INCLUDES) -HF -o$*.sym $*.hxx
.C.EXP:
    $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -e $*.c -l$*.lst
    $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -e $*.cpp -l$*.lst
.CXX.EXP:
    $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -e $*.cxx -l$*.lst
    $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -e $*.cp -l$*.lst
.ASM.EXP:
    $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -e $*.asm -l$*.lst
.OBJ.COD:
    $(DISASM) $*.OBJ >$*.cod
.EXE.COD:
    $(DISASM) $*.EXE >$*.cod
.COM.COD:
    $(DISASM) $*.COM >$*.cod
.OBJ.EXE:
    $(LNK) $(LFLAGS) @$(PROJ).LNK
.OBJ.COM:
    $(LNK) $(LFLAGS) @$(PROJ).LNK
.DLL.LIB:
    $(LIBR) $*.LIB $*.DLL
.DEF.LIB:
    $(LIBR) $*.LIB $*.DEF
.RTF.HLP:
    $(HC) $(HELPFLAGS) $*.HPJ
.ASM.OBJ:
    $(ASM) $(AFLAGS) $(DEFINES) $(INCLUDES) $*.ASM
.RC.RES:
    $(RC) $(RCDEFINES) $(RESFLAGS) $(INCLUDES) $*.rc
.DLG.RES:
    echo \#include "windows.h" >$$$*.rc
    echo \#include "$*.h" >>$$$*.rc
    echo \#include "$*.dlg" >>$$$*.rc
    $(RC) $(RCDEFINES) $(RESFLAGS) $$$*.rc
    -del $*.res
    -ren $$$*.res $*.res

all:        $(PROJ).$(PROJTYPE) done
$(PROJ).$(PROJTYPE):    $(PROJS) $(OBJS) $(RCFILES) \
                        $(RESFILES) $(HELPFILES) $(BATS)
            $(LNK) $(LFLAGS) @$(PROJ).LNK
            $(CVPK) $$SCW$$.$(PROJTYPE)
            -del $(PROJ).$(PROJTYPE)
            -ren $$SCW$$.$(PROJTYPE) $(PROJ).$(PROJTYPE)
done:
        -echo $(PROJ).$(PROJTYPE) done
buildall:   clean   all
clean:
        -del $(PROJ).$(PROJTYPE)
        -del SCPH.SYM
        -del pt.OBJ
        -del ptatom.OBJ
        -del pt.SYM
        -del resource.SYM
cleanres:
res:        cleanres $(RCFILES) link
link:
        $(LNK) $(LFLAGS) @$(PROJ).LNK
        $(CVPK) $$SCW$$.$(PROJTYPE)
        -del $(PROJ).$(PROJTYPE)
        -ren $$SCW$$.$(PROJTYPE) $(PROJ).$(PROJTYPE)
pt.OBJ: \
        pt.cpp  \
        \sc\mfc\include\afx.h \
        \sc\mfc\include\afxver_.h \
        \sc\include\windows.h \
        \sc\include\shellapi.h \
        \sc\mfc\include\afxres.h \
        \sc\mfc\include\afxcoll.h \
        ..\..\..\sc\include\win16\print.h \
        \sc\mfc\include\afxmsg_.h \
        \sc\mfc\include\afxdd_.h \
        resource.h \
        pt.h \
        ptdefs.h \
        ptatom.h
ptatom.OBJ: \
        ptatom.cpp  \
        \sc\mfc\include\afx.h \
        \sc\mfc\include\afxver_.h \
        \sc\include\windows.h \
        \sc\include\shellapi.h \
        \sc\mfc\include\afxres.h \
        \sc\mfc\include\afxcoll.h \
        ..\..\..\sc\include\win16\print.h \
        \sc\mfc\include\afxmsg_.h \
        \sc\mfc\include\afxdd_.h \
        resource.h \
        ptatom.h \
        ptdefs.h

Copyright © 1994, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.