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++

Symantec C++ Professional


JAN94: C PROGRAMMING

A few months ago, I wrote about the tech support that Symantec provides on CompuServe and some problems that I had. In response to that column, Symantec's technical staff ambushed me at Software Development '93 in Boston. At least three tech supporters and one vice president cornered me. My message came through loud and clear, they said. I have since tried using their tech support again. Let me set the record straight by saying that they have addressed the problem and are solving it. When you sign onto the Symantec C++ Professional forum (GO SYMDEVTOOLS) you are treated to tech support from the fountainhead.

Symantec C++ has a long genealogy going back to the mid-eighties. Walter Bright created Northwest C. Northwest C begat DataLight C, which begat Zortech C++, which begat Symantec C++ Professional. Walter is now one of the tech supporters on CompuServe. I don't know how long they can keep it up or how long Walter will be content doing tech-support penance. For now, though, your problems get attention and action, not only from Walter, but from a complement of other Symantec developers as well. Symantec CEO Gordon Eubanks himself fielded a question one day. How much more attention could you want?

Symantec C++ Professional is a full-featured compiler that supports DOS, extended DOS, Windows 3.1, Win32, and NT development. My first interest in it involved the DOS compiler. An earlier version had fatal template problems, and I could not use it to compile the template exercises in my C++ tutorial book.

The new version has corrected those problems, but some new ones have crept in. I can, however, code workarounds, which I could not do before. For instance, Example 1(a) will not compile in SC++. The compiler insists that you code the member pointer with the <T> qualification like Example 1(b). I don't mind doing that, but three other compilers, Borland C++, Watcom C++, and Comeau C++, accept the first idiom as well, and, as near as I can tell, either way is correct.

The second problem involves template functions. The code in Example 2 does not compile. The compiler complains that it finds no function to match Foo(int *) even though the Foo template function is there to match it.

Of all the C++ compilers I have used, SC++ is the only one that declares a fatal compile error if a non-void function fails to return something. All of the others, including cfront, allow the program to compile, although some of them issue a warning. The SC++ compiler will not compile programs such as Example 3(a).

On the surface, it would appear that SC++ is the only correct compiler in this respect, because the C++ Annotated Reference Manual (ARM) says that falling out of the bottom of a type-returning function is illegal. However, the ARM contradicts that rule when it says that Example 3(b) is a "correct and complete" program, yet SC++ will not compile it. Most C++ books, including Stroustrup's The C++ Programming Language, Second Edition, contain many such programs with implicit int main functions that do not return anything and that SC++ will not compile.

There are two ways around the problem: One is to always return something from main; the other is to declare main as void. I don't like either one, although I have used the latter. The compilers let you get away with declaring main as void for two reasons. First, the ARM says that main is not "predefined" by the compiler and that its type is "implementation dependent." Second, a lot of prior art exists because AT&T's cfront compiler allows it.

On the other hand, I don't like adding superfluous return 0; statements to the main function in every program that I publish. The majority of compilers do not insist on the practice, and many respected C++ programmers and writers don't use it. The extra statements add unnecessary lines of code to books and articles that need an economy of code to make their points and teach their lessons. To get around the SC++ error message, I use this statement in the makefiles:

sc "-Dmain=void main" $*.cpp

The statement coerces a main function into a void main function, and SC++ compiles the program.

By using that compile command and changing the template exercises to avoid the pointer idiom that SC++ does not like, I was able to compile and run all of the exercises in my tutorial with SC++. There are some differences in the way that SC++ implements iostreams, but that's nothing new. All compilers implement them differently.

Programmers don't care as much about the correctness of code as programming writers do. The programmer has a job to do, and if there is a workaround to a problem, the programmer is glad to use it. Issues of portability can be postponed in the interests of expedience. Writers, on the other hand, try to write code that works in all of the various platforms readers use. At the same time, we want it to be correct. C++ is not a standard language. The ARM's specification, which is the base document for the ANSI committee's deliberations, has ambiguities. Different implementors make different interpretations; no one is right, and no one is wrong.

I was not able use SC++ to build the persistent-object database code from another book. The program compiles fine, but locks the computer when I run it. Execution does not get beyond the startup code. I put error traps at the beginning of main and in the constructor of the only class with a global object, and neither trap executed. I built the program by including debugging information according to the docs and ran it under CodeView, but CodeView failed to recognize the source code and behaved as if it were debugging a machine-language program without source code. The SC++ documentation for compiling mentions a Symantec debugger, but I could not find a DOS debugger in the package. The new, improved Symantec tech support assures me that all such problems will be addressed. They could be fixed by the time you read this--Symantec posts patches on CompuServe.

Support Hose

There's no better pipeline for answers, workarounds, and bug fixes than public, online forums such as those on CompuServe. It is almost the perfect solution to most tech-support problems. The phone is never busy, and you can post your questions without waiting for a qualified tech-support person to become available. Everyone participates at the most convenient time for them. No one checks your serial number to make sure you registered. Your question is seen by a host of experts, including other users who can help. The vendor's quality of support is on display for their customers to evaluate. Patches and upgrades are there to be downloaded. Other than for your connect time, there is no charge. What could be better?

A word of advice for programmers who use tech-support forums: patience. It takes the vendor a while to get around to fixing problems. They have to react to unexpected bugs and at the same time manage their technical resources within the framework of release schedules, which are usually driven by marketing considerations. That's what keeps them in business. Therefore, if you submit a problem and don't have a patch to fix it within a day or two, don't get upset. If the problem is a show-stopper (most are not) and you need to return the product and switch to another vendor, do so, but don't publicly whine at and chastise the tech-support people. Chances are, you'll have similar but different problems with the product you switch to.

Tutorial Torture

The exercises in my C++ tutorial book are a minor-league C++ torture test, and if I were a compiler vendor, I'd get a copy of the book and make sure that my compiler worked with those exercises. That way I wouldn't have to read about me in my column when my compiler came out. The book is one of three on the market named, Teach Yourself C++. One of the others was written by my pal, Herb Shildt, who made off with my title in the middle of the night. The third is subtitled _in 21 Days. I haven't read it, but the title makes about as much sense as C++ for Dummies would. (Oh, no, don't tell me that they're actually going to_.)

False Rumor

I picked up a false rumor at Software Development '93 in Boston. The grapevine said that Borland's new C++ compilers, now in beta, will support Windows development only--not DOS development. I called Borland for verification. The rumor is not true. Borland told me that in trying to effectively apportion their development resources, they released betas with support for Windows development only. That, they say, is probably where the rumor started. The released product will support 16-bit development for DOS as well as 16- and 32-bit Windows, Win32, and NT development.

I don't know how much of a market there is for unadulterated DOS C++ code these days, so I cannot presume that anyone would make a marketing mistake by dropping support for DOS development. I do know, however, that if you want to develop code for a plain-vanilla iostream environment, perhaps to port all over the place, and you have a PC to do it on, you need a DOS compiler. I will always want to do that because much of the code that I write is about C++ itself, not about Windows. The programmers who use the code do not all have Windows. You shouldn't need a Windows-hosted compiler and run-time system to run the C++ code from a book. Furthermore, when I do write a Windows program, I write and test the application algorithms in DOS and keep the user interface separate. Borland's was always my favorite DOS C and C++ compiler with clearly the best source-level debugger. I'm happy to report that nothing has changed.

See PopPop

I wish Bjarne had named it something else. It's hard to pronounce. Speakers at programmers' conventions make the microphone go "pop-pop" when they say the cheek-puffing, lip-smacking "plus-plus." If you sit in the front row at one of those sessions, you can count on being sprayed.

TED and the D-Flat++ Editor

I wrote some of this column with the TED text editor that I built to check out the D-Flat++ applications model. Every now and then, TED forgets that he is in word-wrap mode and starts scrolling the text to the left as a line gets longer and longer. Then after a time, he remembers and properly wraps the stuff. I think it has to do with whether or not I am typing a space when the program gets to the right margin. Sometimes I'll delete a character to correct one of my many typographical errors. TED deletes the character but then displays the wrong line of text. I'm doing a lot of document saves.

As I find these problems, I fix them. The tab-expansion logic still has problems, but I know where they are and what to do about them. Unfortunately, fixing the problem risks a performance penalty that I haven't figured out how to avoid. Well, if the big boys can release incomplete and buggy software, so can I.

D-Flat++ has two text-editor classes. The first is the EditBox class, the single-line editor that you typically see in dialog boxes. The File Open dialog box uses one for the filename text-entry control. The second class is the Editor class, which is derived from EditBox. The Editor class is the multiline text editor. It wraps words and forms paragraphs.

Listing One, page 134, is editbox.h, the header file that describes the EditBox class, which is derived from the TextBox class. The EditBox class adds three data members: a text column number, a flag to indicate the insert/overwrite mode, and a flag to indicate that the text has been changed by the user. There are new member functions for moving the cursor from character to character and word to word, and for inserting and deleting characters in the buffer. The class overrides some of the TextBox's member functions, because they will have different behavior, and adds some functions of its own.

Listing Two, page 134, is editbox.cpp, the source code that implements the EditBox class. It is surprising how little code it takes to implement a text editor when much of the behavior is already defined in the base class. The EditBox class intercepts the SetFocus, ResetFocus, Paint, Move, and ClearText methods to turn the keyboard cursor on and off. The Keyboard method processes keys unique to the EditBox and those different from the TextBox.

Next month, we'll discuss the Editor class, which is derived from the EditBox class. Features I haven't built yet include the ability to mark blocks and do clipboard operations. When those are finished, D-Flat++ will be completed.

Access Specifiers

Reviewing the D-Flat++ source code, I see that it has evolved in ways that resemble many medium and large design projects. It needs a number of design improvements, particularly among the class-member access specifications. You can look at most class declarations and see public members that should be private or protected. Some protected members should be private with protected member functions to support access to them.

It isn't that the design does not work. It works. But parts of the design that should be hidden are exposed. In a class design of any size and consequence, such lapses in principle are inevitable. When something starts working, you tend not to revisit it. But that doesn't mean that you can't do something about it. Later, when time and schedule allow, I intend to overhaul the access specification of all the classes. (I can still hear my mother's caution about pavement and good intentions.)

The D-Flat++ Source Code

D-Flat and D-Flat++ are available to download from the DDJ Forum on CompuServe and on the Internet by anonymous ftp. Page 3 of this issue has the details. If you cannot get to one of the online sources, send a diskette and a stamped, addressed mailer to me at Dr. Dobb's Journal, 411 Borel Ave., San Mateo, CA 94402. I'll send you a copy of the source code. It's free, but if you care to support my Careware charity, include a dollar for the Brevard County Food Bank. They support some of the needs of our hungry and homeless citizens.

Migrant Mouse

I'm doing a lot of traveling lately and taking a laptop along. With more and more of my work being done in Windows, a mouse has become a necessary traveling companion. If you've ever tried using a mouse on an airplane you know that the space on a typical tray table is limited, and there isn't enough room to maneuver your mouse. I checked out those clip-on trackball devices and found them too hard to use. So, to solve the problem, I slide a note binder into my lap under the tray table and run the mouse around on the binder. But now, when I sit there with my hand in my lap, out of sight and moving around in circles, the other passengers and the flight attendants tend to stare. I stopped trying to explain. It only makes it worse when I tell them that I'm manipulating my mouse.

Example 1: The compile problem in (a) is dodged by using the member pointer with the <T> qualification

in (b).

<b>(a)</b>
     template <class T>
     class Foo   {

         Foo *nextfoo;
         // ...
     };


<b>(b)</b>
     Foo<T> *nextfoo;


Example 2: The template function.

template <class T>
void Foo(T *tp)
{
    *tp = 321;
}
template <class T>
void Bar(T& tr)
{
    Foo(&tr);
}

main()
{

    int x = 123;
    Bar(x);
}

Example 3: The compiler declares a fatal compile error if a non-void function fails to return something in (a) even though this contradicts the ARM rule in (b).

<b>(a)</b>
     #include <iostream.h>
     main()
     {
         cout << "Hello, Timna";
     }


<b>(b)</b>
     extern f();
     main() { }


[LISTING ONE]


// -------- editbox.h
#ifndef EDITBOX_H
#define EDITBOX_H

#include "textbox.h"

class EditBox : public TextBox    {
    void OpenWindow();
protected:
    int column;       // Current column
    Bool changed;     // True if text has changed
    Bool insertmode;  // True if in insert mode
    virtual void Home();
    virtual void End();
    virtual void NextWord();
    virtual void PrevWord();
    virtual void Forward();
    virtual void Backward();
    virtual void DeleteCharacter();
    virtual void InsertCharacter(int key);
public:
    EditBox(const char *ttl, int lf, int tp, int ht, int wd, DFWindow *par=0)
                        : TextBox(ttl, lf, tp, ht, wd, par)
            { OpenWindow(); }
    EditBox(const char *ttl, int ht, int wd, DFWindow *par=0)
                        : TextBox(ttl, ht, wd, par)
            { OpenWindow(); }
    EditBox(int lf, int tp, int ht, int wd, DFWindow *par=0)
                        : TextBox(lf, tp, ht, wd, par)
            { OpenWindow(); }
    EditBox(int ht, int wd, DFWindow *par=0) : TextBox(ht, wd, par)
            { OpenWindow(); }
    EditBox(const char *ttl) : TextBox(ttl)
            { OpenWindow(); }
    // -------- API messages
    virtual Bool SetFocus();
    virtual void ResetFocus();
    virtual void SetCursor(int x, int y);
    virtual void ResetCursor();
    virtual void SetCursorSize();
    virtual unsigned char CurrentChar()
            { return (*text)[column]; }
    virtual unsigned CurrentCharPosition()
            { return column; }
    virtual Bool AtBufferStart()
            { return (Bool) (column == 0); }
    virtual void Keyboard(int key);
    virtual void Move(int x, int y);
    virtual void Paint();
    virtual void PaintCurrentLine()
            { Paint(); }
    virtual void ClearText();
    virtual void LeftButton(int mx, int my);
    Bool Changed()
            { return changed; }
    void ClearChanged()
            { changed = False; }
    Bool InsertMode()
            { return insertmode; }
    void SetInsertMode(Bool imode)
            { insertmode = imode; ResetCursor(); }
};
inline Bool isWhite(int ch)
{
    return (Bool)
        (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r');
}
#endif

[LISTING TWO]


// ------------- editbox.cpp
#include <ctype.h>
#include "desktop.h"
#include "editbox.h"

// ----------- common constructor code
void EditBox::OpenWindow()
{
    windowtype = EditboxWindow;
    column = 0;
    changed = False;
    text = new String(1);
    BuildTextPointers();
}
Bool EditBox::SetFocus()
{
    Bool rtn = TextBox::SetFocus();
    if (rtn)    {
        ResetCursor();
        desktop.cursor().Show();
    }
    return rtn;
}
void EditBox::ResetFocus()
{
    desktop.cursor().Hide();
    TextBox::ResetFocus();
}
// -------- process keystrokes
void EditBox::Keyboard(int key)
{
    int shift = desktop.keyboard().GetShift();
    if ((shift & ALTKEY) == 0)    {
        switch (key)    {
            case HOME:
                Home();
                return;
            case END:
                End();
                return;
            case CTRL_FWD:
                NextWord();
                return;
            case CTRL_BS:
                PrevWord();
                return;
            case FWD:
                Forward();
                return;
            case BS:
                Backward();
                return;
            case RUBOUT:
                if (CurrentCharPosition() == 0)
                    break;
                Backward();
                // --- fall through
            case DEL:
                DeleteCharacter();
                BuildTextPointers();
                PaintCurrentLine();
                return;
            default:
                if (!isprint(key))
                    break;
                // --- printable keys processed by editbox
                InsertCharacter(key);
                BuildTextPointers();
                PaintCurrentLine();
                return;
        }
    }
    TextBox::Keyboard(key);
}
// -------- paint the editbox
void EditBox::Paint()
{
    TextBox::Paint();
    ResetCursor();
}
// -------- move the editbox
void EditBox::Move(int x, int y)
{
    TextBox::Move(x, y);
    ResetCursor();
}
// --------- clear the text from the editbox
void EditBox::ClearText()
{
    TextBox::ClearText();
    OpenWindow();
    ResetCursor();
}
// ----- move cursor to left margin
void EditBox::Home()
{
    column = 0;
    if (wleft)    {
        wleft = 0;
        Paint();
    }
    ResetCursor();
}
// ----- move the cursor to end of line
void EditBox::End()
{
    int ch;
    while ((ch = CurrentChar()) != '\0' && ch != '\n')
        column++;
    if (column - wleft >= ClientWidth())    {
        wleft = column - ClientWidth() + 1;
        Paint();
    }
    ResetCursor();
}
// ---- move the cursor to the next word
void EditBox::NextWord()
{
    while (!isWhite(CurrentChar()) && CurrentChar())
        Forward();
    while (isWhite(CurrentChar()))
        Forward();
}
// ---- move the cursor to the previous word
void EditBox::PrevWord()
{
    Backward();
    while (isWhite(CurrentChar()) && !AtBufferStart())
        Backward();
    while (!isWhite(CurrentChar()) && !AtBufferStart())
        Backward();
    if (isWhite(CurrentChar()))
        Forward();
}
// ---- move the cursor one character to the right
void EditBox::Forward()
{
    if (CurrentChar())    {
        column++;
        if (column-wleft == ClientWidth())
            ScrollLeft();
        ResetCursor();
    }
}
// ---- move the cursor one character to the left
void EditBox::Backward()
{
    if (column)    {
        if (column == wleft)
            ScrollRight();
        --column;
        ResetCursor();
    }
}
// ---- insert a character into the edit buffer
void EditBox::InsertCharacter(int key)
{
    unsigned col = CurrentCharPosition();
    if (insertmode || CurrentChar() == '\0')    {
        // ---- shift the text to make room for new character
        String ls, rs;
        if (col)
            ls = text->left(col);
        int rt = text->Strlen()-col;
        if (rt > 0)
            rs = text->right(rt);
        *text = ls + " " + rs;
    }
    (*text)[col] = (char) key;
    if (key == '\n')
        BuildTextPointers();
    Forward();
    changed = True;
}
// ---- delete a character from the edit buffer
void EditBox::DeleteCharacter()
{
    if (CurrentChar())    {
        String ls, rs;
        unsigned col = CurrentCharPosition();
        if (col)
            ls = text->left(col);
        int rt = text->Strlen()-col-1;
        if (rt > 0)
            rs = text->right(rt);
        *text = ls + rs;
        changed = True;
    }
}
// ---- position the cursor
void EditBox::SetCursor(int x, int y)
{
    desktop.cursor().SetPosition(
        x+ClientLeft()-wleft, y+ClientTop()-wtop);
}
// ---- left mouse button was pressed
void EditBox::LeftButton(int mx, int my)
{
    if (ClientRect().Inside(mx, my))    {
        column = max(0, min(text->Strlen()-1,
                          mx-ClientLeft()+wleft));
        ResetCursor();
    }
    else
        TextBox::LeftButton(mx, my);
}
// ---- set the size of the cursor
void EditBox::SetCursorSize()
{
    if (insertmode)
        desktop.cursor().BoxCursor();
    else
        desktop.cursor().NormalCursor();
}
// ---- reset the cursor
void EditBox::ResetCursor()
{
    SetCursorSize();
    if (visible)
        SetCursor(column, 0);
}
End Listings


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.