Channels ▼


Examining Symantec C++ 7.0

Source Code Accompanies This Article. Download It Now.

AUG95: Examining Symantec C++ 7.0

Fast linking, 32-bit support, and distributed builds top the features list

Ira is a software developer who specializes in Windows and Motif. He can be contacted on CompuServe at 70711,2570 or at 301-924-0596.

Without question, Windows developers have benefited from the so-called C++ wars, which have produced compilers that generate highly optimized code, provide robust tools and utilities, and sport sophisticated development environments--all at a reasonable cost. The most recent release of Symantec C++ (Version 7.0) continues this trend, adding a visual programming environment, class and hierarchy editors, a new feature that allows you to distribute build tasks over a LAN, upgraded Microsoft Foundation Classes, a 32-bit multithreaded linker, better integration of the Multiscope debugger, support for Windows 95 resources, and a set of visual tools called "Express Agents." And with 7.0, Symantec has also enhanced its implementation of the C++ language, adding support for exceptions and run-time type identification.

Integrated Development Environment

The Symantec C++ 7.0 Integrated Development and Debugging Environment (IDDE) takes a Visual Basic-like approach to placing its windows on the Windows desktop (as opposed to the conventional Multiple Document Interface approach). This makes it easier for you to use Windows Help and other applications in conjunction with the IDDE. The IDDE also features a set of workspaces--accessed through customizable tabs in the main control region--that let you individualize your work environment. Drag-and-drop is used extensively to allow different views of code and data. Key components of the IDDE include the following:

Editor/browsers. Two of the most innovative features of the development environment are the hierarchy and class editors, which work together to provide a smooth visual method for program design and coding. These are true editors, not just browsers. The Symantec approach is unique in that it uses a parser to extract information about code structure without compiling and linking the project first. Code is parsed as it is developed, so the information in the hierarchy and class editors is always up to date. This makes the development environment a pleasure to work in, compared to the typical C++ compiler hierarchy and class browsers, which extract their information during compilation and link times. This information is most useful during initial program development, before the project reaches error-free compilation and linking.

The hierarchy editor can be used to build a complete class hierarchy for the application; the class editor can then fill in the class members and provide the required program functionality. You can add classes to the hierarchy, then connect them in the desired hierarchy by dragging the connecting lines. As the connections are changed, the source code and header files change automatically and the class editor fills in the necessary code. This editor is a three-paned window: The left pane contains a list of classes in the application; the right pane shows the members, data, and typedefs for the class; and the bottom pane shows the source. When you select a class in the left pane, the members of the class appear in the right pane. Clicking on a member name brings up the member source code, which can then be edited directly in its pane. Adding a member is easy: Just click in the right pane with the right-mouse button and select "Add" to bring up the necessary dialog box.

The class editor's only downside is that it can't handle the mapping between Windows messages and member functions; this requires the Class Express tool. It would have been much more convenient to do this directly from the class editor. In any case, the ability to develop code on a class and function level instead of dealing with individual source and header files is the most innovative feature of Symantec C++ 7.0.

ResourceStudio. This program includes a set of resource-editing tools for creating dialog boxes, icons, bitmaps, and the like. Also included are a version editor, which provides version information for install programs, and a string-table editor. Together with the Class Express tool, the ResourceStudio integrates the creation of dialogs and the subsequent building of the necessary code.

The project facility. The project facility utilized by Symantec 7.0 provides a complete set of features and has links to Intersolv's PVCS version-control system, allowing check-in/out of files from the IDDE. By adding subprojects to a project, you can maintain all of your libraries and DLLs within the same project as your .EXE files and have them automatically rebuilt when changes are made. The project facility includes automatic dependency checking for inclusion of header files. You can process files using external makefiles based on processors such as lex and yacc. You can build either a debug or release version of the application by checking a box in its settings dialog. The debug version includes debug information included in the object, and enables assertions and additional error checking desirable for debugging. Another useful feature of the project facility is the use of option sets for saving all of the options associated with a project, allowing option sets to be easily switched on a project.

Linker. Symantec C++'s Optlink linker (which was always fast) is now a native, 32-bit application. Consequently, it can take advantage of multithreading to improve link time and provide support for both 16- and 32-bit applications. (To further improve the linker's performance, Symantec has eliminated CVPACK.) Compiling and linking is processed in the background, even under Windows 3.1, and other tasks performed during the build phase have surprisingly good system response.

NetBuild. This utility allows distributed builds over networks with a NetBIOS interface. This lets you off-load parts of the build onto cooperating machines, thus speeding up builds on large development projects. Symantec claims that even if one of the remote machines goes down during build processing, the build will still complete successfully.

Creating an Application

The Symantec compiler can target DOS, Extended DOS, and 16- and 32-bit Windows. To explore the Symantec tools, I developed an OLE 2.0 container application under Win32s. (Complete source code for this project is available electronically; see "Availability" on page 3.) This option produces a 32-bit application that can run under either Windows 3.1 (with Win32s DLLs), Windows 95, or Windows NT. I first used AppExpress to automatically generate much of the framework code using MFC 3.0. AppExpress guided me through a few dialogs and then created five source files, six classes, associated header files, and a resource file.

Building the project produced a reasonable-looking SDI application with a dockable toolbar. I was able to insert a Microsoft Graph object and then save and retrieve the file. It even created a skeleton About box. All of this was accomplished without writing a single line of code--not bad for 15 minutes of work. However, the window wasn't scrollable and there was not yet a way to select objects, size them, or move them about.

To make the window scrollable, I made CScrollView the base class of CcontainView (the class representing the client-view window) instead of CView; see Listing One. I did this by dragging the connecting line in the hierarchy editor from CView to CScrollView. The header files were then automatically updated. I added a bit of code to create and initialize the scroll bars and handle resize events, and I was off and running. The scroll bars came up beautifully, but did not respond to the mouse. I figured out that when I had replaced the base class for CcontainView, the hierarchy editor had not changed the message-mapping macros accordingly. MFC normally handles messages in the derived class. If a message handler is not found there, the message gets passed up to the base class for processing. The message is passed up the class hierarchy until either it is processed or it reaches the top-most class, whereupon it is processed by the default window procedure. To make this scheme work, the BEGIN_MESSAGE_MAP macro associates a derived class with its base. If BEGIN_MESSAGE_MAP does not point to the correct base, messages will not be handled properly.

Once this problem was solved, the scroll bars worked properly. AppExpress added some drawing code for the view, but it could draw only one item, which always appeared at a hardcoded position within the document. MFC provides C++ classes to serve as wrappers for most of the Windows structures. A CRect class item, rctPos, was added as a member of the client-item class, CcontainCntrItem, to allow the objects to be moved around in the window. Another problem with scroll bars is that the item's position in the window does not correspond to its position within the document. Therefore, I added the member function GetViewRect, which converts document position to window position given the position of the scroll bars. Dummy functions were created to do this for all of the methods that dealt with position and to pass the information to the corresponding function in the base class. The item's position within the document was saved into the file by modifying the Serialize member of CcontainCntrItem.

Once the scrolling and drawing code was working correctly, I turned my attention to moving and sizing the objects. MFC provides a CRectTracker class that has most of the necessary code. CRectTracker lets you draw several styles of bounding box, plus sizing handles. It also sets the cursor, showing the move cursor or sizing cursors, as appropriate. Deriving a CRectSelect class from CRectTracker provided the required functionality for the application (CRectTracker even tracks the cursor).

Debugging the Code

When I tried to build and link the application, several error messages scrolled by in the output window. Clicking on the error message took me to the offending spot in the source code, and after a few cycles of correct and build, a clean build was produced. Full of enthusiasm, I clicked on the Execute Program menu item. The Symantec IDDE minimized itself to an icon, and the program began running. Then I used the program to insert a Paintbrush object. This worked fine.

However, although I clicked all around the object, I still couldn't get the bounding box to appear. Finally, I clicked on another part of the window and a bounding box appeared. Clearly it was time to fire up the debugger, which uses Multiscope's Windows-based debugging technology. Like any Windows-based tool, Multiscope lets you open a plethora of windows and simultaneously look at lots of code, data, and debug information. However, since the debugger windows are just like any other windows under Windows, the debugger introduces paint events, mouse-move events, and so on, which may interfere with the very events you are trying to debug. In this respect, previous-generation debuggers (such as Borland's Turbo Debugger) are easier to use.

Nevertheless, the Symantec debugger was full featured, providing many ways to view and modify data and the ability to easily step through the code and set both absolute and conditional breakpoints. MFC uses assertions and C++ exception handling extensively, and this provides valuable clues when the program blows up during debug. These debug features are conditionally compiled into the code and turned off when a production build is selected through the Project Settings dialog. The debugger allows full control of threads and adds a Threads View to inspect multiple threads in your application. From the Threads view, you can drag a thread over to the source view to display the code for that thread. You can also drag C/C++ code from the Source View to an Assembly View. The code is disassembled on the fly. Finally, the new hardware watchpoints include Pentium support for Windows NT and Windows 95.

Benchmarking with the Migration Tool

Microsoft and Symantec have jointly developed the "MFC Migration Tool," a utility that helps C programmers migrate existing Windows code to MFC. As preparation for the migration process, you must first compile your project with all compiler warnings turned on and ensure that the code is as clean as possible. Next, run your code through the Migration Tool, which compares your code to a set of migration guidelines and reports on potential migration problems within your code. The tool will step you through the code, and even let you edit your source. Finally, you must create an MFC skeleton (using AppExpress) and move the C code into MFC classes. Tasks here include moving your WinMain() code to MFC's WinWain(), dropping your WM_Paint code into the OnDraw member function of the appropriate view class, converting your WndProc's switch statements to a view-class member function, and moving the WM_COMMAND code into a member function. Message handlers must also be converted from switch statements to MFC handler functions. The tool comes with complete help files on steps to further integrate your application with MFC.

Symantec provides a set of timing benchmarks, one of which uses the source code from the MFC Migration Tool. The timer test measures the time it takes to build an executable. This test was performed on a 486/66 with 16 MB RAM under Windows NT 3.5. The source code to the MFC Migration Tool was compiled using four scenarios: compiled for speed, compiled for size, debug information included, and debug information included but no .MAP file generated. Table 1 presents the results of the timings along with the size of the generated .exe files. The entire project, complete with source code and makefiles, is available electronically. If you're interested in how other compilers stack up, you should be able to compile this code with any development platform that supports MFC, including Visual C++, Watcom C/C++, and MetaWare's High C/C++.


MFC provided a quick start in writing the code and allowed the use of thousands of lines of prewritten code. The result: I produced an OLE application with a minimal knowledge of the OLE API, since most of the difficult coding work was embedded within the MFC framework.

Symantec C++ 7.0 is a well-integrated set of tools that maximizes programming productivity by letting you work in the realm of classes and functions in a hierarchical arrangement, rather than flipping through source files trying to make sense of a dizzying array of classes and functions. While some of the tools that come with it have a few rough edges, the package provides a powerful environment for developing C++ applications.

For More Information

Symantec C++ 7.0


10201 Torre Avenue

Cupertino, CA 95014-2132


Optimizing C++ Code

Walter Bright

Walter, the original author of the Symantec compiler, can be contacted at [email protected]

It's generally accepted that the more C++ features you use in a class, the slower your code will be. Fortunately, you can do a few things to tip the scales in your favor. First, don't use virtual functions, virtual base classes, destructors, and the like, unless you need them. Some compilers will return structs that fit into 1, 2, 4, or 8 bytes in the general-purpose registers, provided that no constructor is declared. Compile Listing Two with and without the constructor and see the difference in the code your compiler generates.

C++ exception handling is another feature to be wary of. The jury is still out on whether it makes your code faster, but just using it adds overhead that is roughly proportional to the number of automatic objects of classes with destructors in the program. So if you need exception handling, try to cut down on automatic objects. Instead, use referenced objects and design them so that they don't need a destructor. Objects without destructors needn't be cleaned up by the exception-handling code, so no overhead will be added to keep track of them. Note that an empty destructor (such as ~X() { }) is not good enough--there must be no destructor to avoid the overhead.

Another source of bloat is multiple inheritance, especially when using virtual base classes. For faster code, stick with single inheritance.

Virtual functions add bloat because they need extra code to be called and their class data contains an extra pointer, which guarantees you'll have a constructor. So for a complex class hierarchy with only one or two virtual functions, consider removing the virtual aspect, and maybe do the equivalent with a test and branch.

Memory Allocation

Most complex programs spend a lot of time allocating and freeing large numbers of small objects. The usual storage allocators (malloc, free, new, and delete) are general purpose and rather slow. Constructing a storage manager for your specific needs can speed things up a lot.

For instance, if your program allocates a bunch of objects, but never frees them, malloc's extra bookkeeping is unnecessary and expensive. Instead, you can use a simple heap allocator; see Listing Three. If you are allocating and freeing a large number of identically sized objects, a custom allocator that deals with fixed-size objects can be a lot faster.

Virtual Memory

Most modern programs for any system more advanced than 16-bit DOS run under virtual memory. But a program cannot execute or read data directly from disk; it must be loaded into memory first. If the program accesses many different areas of its address space, the operating system must continually exchange memory and disk, and it is fairly easy to construct a program that thrashes itself to the point where the system slows to a crawl. Optimizing minimizes this thrashing so that whatever is needed is nearly always already in memory. There are two aspects to this: code and data.

The worst case is a heavily used loop that calls a function in each page of the program, which will minimize performance. You need to do the reverse: Control the ordering and location of functions in the program so that strongly connected functions (that call each other a lot) are grouped together, hopefully in the same virtual-memory page. This can have a side benefit of localizing the code into a fast memory cache. The problems in doing this are:

  • Code tends to be written so that statically--not dynamically--related functions are grouped together (like all the member functions of a particular class).
  • Modern code projects tend to be extremely large, and it can be next to impossible to manually group functions in an optimal way. Unfortunately, very large projects are most likely to benefit from function-order optimization!
The solution, of course, is to use a profiling tool which finds the dynamic relationship among functions by gathering statistics at run time, then computes a reasonably optimal link order.

Note that modern operating systems do not actually read your whole program into memory before executing it. They simply add the disk image of the program into the virtual-memory system, and the program is then swapped into memory as it is executed. So, the speed at which a program loads strongly depends on the number of pages that must be swapped in to execute the initialization code. Grouping all the initialization code, therefore, will speed up program loads. Grouping rarely used routines together means that only in rare cases will they actually ever be read off of the disk, so you can bloat your programs with impunity!

Optimizing virtual-memory performance for data is not quite as easy. If your program has to regularly search a large, in-memory data structure, an algorithm that accesses many of the pages to perform a common operation will be terribly slow. The data structures need to be organized so that lookups and other accesses are done directly with as little searching as possible. Think of the file-system structure on a floppy disk: If, to read a bit of the floppy, your program had to read the floppy each time, it would be unusably slow. The file-system data structure on the disk minimizes the amount of disk that must be read for any particular access. Organize your data structures with the same thought. Keep in mind that address space is not the same as real-memory use. Address space is essentially free, so burn it in favor of reducing page accesses. Address space that is allocated but never accessed costs you neither memory nor hard disk. Also, with virtual memory, a read/write to memory will cost you twice what a read would. This is because when the virtual-memory system is swapping, if a page was not written to, the system can discard it; otherwise, the system must write that page back to disk. So don't write to your data structure unless you have to, and design it so that modifying the structure involves writes to as few pages as possible.

Table 1: Timings (in seconds) and .exe file sizes for the MFC Migration Tool.

Configuration     Build Time     Executable Size
With Debug Info   283.72         298,436
Debug, No map     260.05         298,436
Speed             333.42         106,496
Size              318.54         108,544

Listing One

// contaivw.cpp : implementation of the CcontainView class
// Copyright (c) Ira Rodens , 1995. All Rights Reserved.
#include "stdafx.h"
#include "contain.h"
#include "contadoc.h"
#include "cntritem.h"
#include "contaivw.h"
#include "crectsel.h"
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
//////// CcontainView ////////
IMPLEMENT_DYNCREATE(CcontainView, CScrollView)
BEGIN_MESSAGE_MAP(CcontainView, CScrollView)
    // Standard printing commands
////// CcontainView construction/destruction ////// 
    : CScrollView()
    m_pSelection = NULL;
    m_pSelectRect = NULL;
    if (m_pSelectRect) delete m_pSelectRect;
/////// CcontainView drawing /////// 
void CcontainView::OnDraw(CDC* pDC)
    CcontainCntrItem*   pItem;
    CcontainDoc* pDoc = GetDocument();
    POSITION pos = pDoc->GetStartPosition();
    CPoint  ptScroll = -GetScrollPosition();
    while ((pItem = (CcontainCntrItem*)pDoc->GetNextClientItem(pos)) != NULL) {
        pItem -> Draw(pDC,ptScroll);
    if (m_pSelectRect != NULL) {
        m_pSelectRect->Draw(pDC, ptScroll);
void CcontainView::OnInitialUpdate()
    SIZE    size;
// Set size for 8.5 X 11 inch paper    
    const double width = 8.5 ;
    const double height = 11 ;
    m_pSelection = NULL;    // initialize selection
    if (m_pSelectRect) {
        delete m_pSelectRect;
        m_pSelectRect = NULL;
// set for standard 8.5 X 11 inch paper   
    CWindowDC cDC(NULL); = (int)(width * (double)cDC.GetDeviceCaps(LOGPIXELSX)); = (int)(height * (double)cDC.GetDeviceCaps(LOGPIXELSY));
/////// CcontainView printing /////// 
BOOL CcontainView::OnPreparePrinting(CPrintInfo* pInfo)
    // default preparation
    return DoPreparePrinting(pInfo);
void CcontainView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
    // TODO: add extra initialization before printing
void CcontainView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
    // TODO: add cleanup after printing
/////// OLE Client support and commands /////// 
BOOL CcontainView::IsSelected(const CObject* pDocItem) const
    // The implementation below is adequate if your selection consists of
    //  only CcontainCntrItem objects.  To handle different selection
    //  mechanisms, the implementation here should be replaced.
    return pDocItem == m_pSelection;
void CcontainView::OnInsertObject()
    // Invoke the standard Insert Object dialog box to obtain information
    //  for new CcontainCntrItem object.
    COleInsertDialog dlg;
    if (dlg.DoModal() != IDOK)
    CcontainCntrItem* pItem = NULL;
        // Create new item connected to this document.
        CcontainDoc* pDoc = GetDocument();
        pItem = new CcontainCntrItem(pDoc);
        // Initialize the item from the dialog data.
        if (!dlg.CreateItem(pItem))
            AfxThrowMemoryException();  // any exception will do
        // If item created from class list (not from file) then launch
        //  the server to edit the item.
        if (dlg.GetSelectionType() == COleInsertDialog::createNewItem)
            pItem->DoVerb(OLEIVERB_SHOW, this);
    CATCH(CException, e)
        if (pItem != NULL)
// The following command handler provides the standard keyboard
//  user interface to cancel an in-place editing session.
void CcontainView::OnCancelEdit()
    // Close any in-place active item on this view.
    COleClientItem* pActiveItem=GetDocument()->GetInPlaceActiveItem(this);
    if (pActiveItem != NULL)
    ASSERT(GetDocument()->GetInPlaceActiveItem(this) == NULL);
// Special handling of OnSetFocus and OnSize are required for a container
//  when an object is being edited in-place.
void CcontainView::OnSetFocus(CWnd* pOldWnd)
    COleClientItem* pActiveItem= GetDocument()->GetInPlaceActiveItem(this);
    if (pActiveItem != NULL &&
        pActiveItem->GetItemState() == COleClientItem::activeUIState)
        // need to set focus to this item if it is in the same view
        CWnd* pWnd = pActiveItem->GetInPlaceWindow();
        if (pWnd != NULL)
            pWnd->SetFocus();   // don't call the base class
void CcontainView::OnSize(UINT nType, int cx, int cy)
    CView::OnSize(nType, cx, cy);
    COleClientItem* pActiveItem= GetDocument()->GetInPlaceActiveItem(this);
    if (pActiveItem != NULL)
////// CcontainView diagnostics /////// 
#ifdef _DEBUG
void CcontainView::AssertValid() const
    ASSERT (!((m_pSelection && !m_pSelectRect) || 
        (!m_pSelection && m_pSelectRect)));
void CcontainView::Dump(CDumpContext& dc) const
CcontainDoc* CcontainView::GetDocument() // non-debug version is inline
    return (CcontainDoc*)m_pDocument;
#endif //_DEBUG
/////// CcontainView message handlers /////// 
void CcontainView::OnLButtonDown(UINT nFlags, CPoint point)
    CcontainCntrItem*   pNewSelection;
    CcontainCntrItem*   pItem;
    COleClientItem  *   pActive;
    CcontainDoc* pDoc = GetDocument();
    if ((pActive = pDoc->GetInPlaceActiveItem(this)) != NULL) {
        pActive -> Close();
        ASSERT (pDoc->GetInPlaceActiveItem(this) == NULL);
    CPoint  ptScroll = GetScrollPosition();
    CPoint  ptMouse ;
    ptMouse.x = point.x + ptScroll.x;
    ptMouse.y = point.y + ptScroll.y;
    if (m_pSelectRect) {
        if (m_pSelectRect->HitTest(point) != CRectTracker::hitNothing) {
            CRect rctOld;
// Find original rectangle          
// Resize/move object           
// Redraw original location 
// Draw object in new location 
                CClientDC   dc(this);              
                m_pSelection -> Draw(&dc,ptScroll);
                m_pSelectRect -> Draw(&dc,ptScroll);
    pNewSelection = NULL;
    POSITION pos = pDoc->GetStartPosition();
    while ((pItem = (CcontainCntrItem*)pDoc->GetNextClientItem(pos)) != NULL) {
        if ((pItem->GetRect()).PtInRect(ptMouse)) {
            pNewSelection = pItem;
BOOL CcontainView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
    BOOL bAns = FALSE;
    if ((pWnd != this) || (nHitTest != HTCLIENT)) {
    else if (m_pSelectRect) {
        CPoint ptScroll = GetScrollPosition();
        bAns = m_pSelectRect->SetCursor(pWnd,nHitTest,ptScroll);
    if (!bAns) {
    return TRUE;
void CcontainView::OnEditCut()
void CcontainView::OnEditCopy()
    m_pSelection -> CopyToClipboard();
void CcontainView::OnEditPaste()
    CcontainCntrItem *pObject = new CcontainCntrItem(GetDocument());
    if (!pObject -> CreateFromClipboard()) delete pObject;
void CcontainView::DeleteSelection()
    if (m_pSelection != NULL) {
        CRect   rctObj;
        m_pSelectRect -> GetTrueRectangle(rctObj,-GetScrollPosition());
        delete m_pSelection;
        m_pSelection = NULL;
        delete m_pSelectRect;
        m_pSelectRect = NULL;
        InvalidateRect ((LPRECT)&rctObj,TRUE);
void CcontainView::ChangeSelection(CcontainCntrItem *pNewSelection)
    if (pNewSelection != m_pSelection) {
        CPoint  ptScroll = GetScrollPosition();
        if (m_pSelectRect) {
            CRect   rctOld;
            delete m_pSelectRect;
            m_pSelectRect = NULL;
        m_pSelection = pNewSelection;
        if (m_pSelection) {
            m_pSelectRect = new CRectSelect(m_pSelection);
                CClientDC   dc(this);
void CcontainView::OnUpdateEditCut(CCmdUI *pCmdUI)
    pCmdUI -> Enable(m_pSelection != NULL);
void CcontainView::OnUpdateEditCopy(CCmdUI *pCmdUI)
    pCmdUI -> Enable(m_pSelection != NULL);
void CcontainView::OnUpdateEditPaste(CCmdUI *pCmdUI)
   BOOL bFlag=CcontainCntrItem::CanPaste() || CcontainCntrItem::CanPasteLink();
   pCmdUI -> Enable(bFlag);

Listing Two

extern struct X { int member;
    X();        // try commenting out this line
} x;
struct X test() { return x; }

Listing Three

class Heap {
static void *heap;
static size_t heapleft;
static inline void *malloc(size_t size)
{   void *p;
    if (heapleft < size)
    {   size_t newsize = (size + 4095) & ~4095;
    p = malloc(newsize);
    if (!p) return p;
    heapleft = newsize;
    heap = p;
    p = heap;
    *(char **)&heap += size;
    heapleft -= size;
    return p;
static inline void *calloc(size_t size)
{   void *p;
    p = malloc(size);
    return p ? memset(p,0,size) : p;
static inline void free(void *p) { }

Copyright © 1995, 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.