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 Programming


OCT93: C PROGRAMMING

Last month I described C++ exception handling and voiced some of my concerns about it. Since writing that column, I've tested the new Watcom C/C++ 9.5 compiler, which is the only MS-DOS implementation of exception handling that I know about. Some of those concerns go away. Others are reinforced. We'll discuss them this month.

First some reflections on the Watcom C/C++ 32 9.5 compiler. As its name implies, you use it for developing 32-bit applications, which means that compiled DOS programs must run under a DOS extender. There is no compiler in this package for 16-bit real mode executables, although Watcom has a product named "Watcom C/C++ 16" for that purpose.

Watcom supports an impressive number of platforms with this one product: DOS, OS/2, Windows, NT, NetWare NLMs, AutoCAD, and ADS/ADI. The DOS platform support works with several DOS extenders, and Watcom includes a run-time copy of the Rational Systems DOS/4GW extender.

The installation procedure asks you which options you intend to install. Installing everything takes 27 Mbytes of disk storage. I installed the C and C++ compilers for DOS development with nothing extra, and it took only about 7 Mbytes.

Watcom comes with a nice package of tools and utilities. Included are the usual make, linker, librarian, source debugger, and profiler files. There are no visual programming tools to speak of, and no Windows API class library. There are a DOS graphics C library, container classes, iostreams, and a Complex class. The C++ language reference documentation consists of a reprint of Bjarne Stroustrup's The C++ Programming Language, Second Edition, (Addison-Wesley, 1991)--a book you should have anyway.

My tests of the Watcom compiler were not comprehensive. I was mainly interested in seeing if it would handle the code in my C++ books, learning how well the compiler implements exception handling, and allaying my concerns about exception handling. For a cursory test, I compiled all of the source code from the two C++ books I've written. Watcom's iostream implementation is different from the other compilers, but that's no surprise. None of the iostream libraries from four different C++ compiler products agree. One of my books includes a GUI class that implements a "generic user interface" with iostreams and the ANSI.SYS device handler. That code works okay with four other compilers but not with Watcom C++.

Two exercises in the C++ tutorial book abort when they use this statement after being compiled with Watcom C++: sptr = new char[strlen(s)+1];.

I have no clue as to why this statement blows up. It happens in the call to strlen. Other programs with similar statements do not abort. Other than for these small problems, the compiler performed well and the code executed as it should have.

Watcom C++ includes support for templates too, and all of my template code compiled and ran without a hitch.

Exceptions

My concerns about exceptions fall into three categories: First, I am concerned that users of third-party libraries will be unsure about whether those libraries are compiled to support exceptions and, if so, whether or not the library functions will themselves throw exceptions. Second, I worry about the absence of any standards for identifying exceptions, a concern that extends to the potential for collisions of exception codes from different library authors. Third, I worry about the overhead associated with support for exceptions. The first two concerns shall remain unsettled until the use of exceptions is widespread. Remember, only one MS-DOS compiler supports exception handling at this time. The third concern, that of performance, is one that I can look at now. Watcom, being the first out of the gate, must bear up under the first microscopic examinations. Nothing else exists with which to compare their implementation.

To test Watcom's exception handling implementation, I wrote short C++ programs that call functions with automatic objects of classes that have constructors. I used the Watcom Video debugger to run these programs, display the assembly language, and step through the code. This technique enabled me to see what is compiled and what the systems functions do.

One of the problems I foresaw last month was that when C++ functions called extern "C" functions, that called C++ functions through callback pointers, the unwinding of the stack might not be properly coordinated. I did not know how any particular implementation would achieve this unwinding. Watcom's approach neatly avoids the problem. Here's how.

When a brace-enclosed C++ block is entered, the compiled code first builds a stack frame that includes whatever space a normal stack frame would include plus some extra space. That extra space is a block that consists of 8 bytes at the bottom, plus 12 more bytes for each automatic class object that has a constructor. Then the run-time code loads the address of the frame, as well as another address in registers, and finally calls a system function named __wcpp_1_block_open__. This final function adds the frame block to a global, singly-linked list of blocks. There is a separate linked list for each try block. Each automatic constructor then posts its destructor's address, and its successful completion to a flag in the block. The flag lets the system determine if the throw was made from within an incomplete constructor. When the block is about to exit, it calls __wcpp_1_block_close__, which calls each of the destructors in turn and then removes the block from the linked list.

When a function throws an exception, the system walks through the linked list and calls all of the destructors in all of the blocks in the list. At the end of the list, the system discards the list and uses longjmp to get to the try block's appropriate catch handler.

This system of linked lists means that all of the C++ functions that were compiled by the C++ compiler properly participate in the stack unwinding even if they are called from other functions that do not support exceptions, such as extern "C" functions. Inasmuch as this is Watcom's first C++ compiler, you can be sure that all of the C++ library functions are participating, assuming that they are compiled with a Watcom compiler. That isn't an unreasonable assumption. Many compilers produce code that is compatible only with libraries that are compiled by that same compiler.

Now for the shock. You can't compile a source module whose functions do not support exception handling. The Watcom compiler has a command-line option (/xs) that enables exception handling. If you don't use the option, you may not code try blocks or catch handlers, and you may not throw exceptions. Otherwise, the compiled object code for nonexception handling programs is exactly the same as for exception handling programs. The linked executable program is smaller, meaning that a program linked without exceptions does not include the system functions to support it. Nonetheless, object modules compiled without the /xs option use the same code as those compiled with it, and the overhead is significant.

It's difficult to compare a Watcom C++ 32 object file with that from a compiler such as Borland's, because Watcom's is a 32-bit protected-mode compiler and Borland's is 16-bit, real-mode. The register architectures and parameter passing conventions are different. Watcom has no previous C++ compiler with which to compare, either. But in examining the assembled code, you can readily see a lot of overhead involved in processing the __wcpp_1_block_open__ and __wcpp_1_block_close__ functions, overhead that does not appear in code compiled by nonexception handling compilers. The overhead takes its toll somewhat in code size but mostly in execution time, and there is no way to turn it off.

Why am I surprised by all this? The Watcom implementation solves the potential stack unwinding problems I discussed last month. What's it take to satisfy a columnist, anyway? This isn't, however, a criticism of the Watcom compiler. They've found an effective way to implement exception handling, the code bloat notwithstanding. I fear, however, that other compilers will follow suit with this non-negotiable overhead, and that we will no longer have the option to build lean and mean programs with C++. This is the kind of fix that sends programmers scrambling back to classic C.

Conclusion: On first appearance, exception handling is going to be a very expensive feature.

Learn C++ on the Macintosh? Not!

Addison-Wesley sent me an advance galley of Learn C++ on the Macintosh by Dave Mark. The book is scheduled for release by the time you read this column. Usually I don't waste space on negative reviews, but this book merits mention for three reasons. First, because it is a good idea that is badly executed; second because it has very little competition, and Macintosh programmers who want to use their computers to learn C++ could be short-changed by this book; and third, because there might be one good unrelated reason to buy it. More about that later.

This book represents a good idea, although not a new one. Macintosh C programmers need C++ as much as the rest of us, and they've been pretty much ignored in the popular press. The book includes a small C++ compiler on disk, a great idea that was pioneered several years ago in Microsoft Press's Learn C Now, which included a bare-bones copy of Microsoft's QuickC compiler for DOS. The advantage to that approach is that the reader has a compiler environment guaranteed to run the source code in the book. From one who has published a lot of would-be generic source code, be assured that the biggest headaches come when readers try to use the code on compilers other than the one that the author used. The book/disk/compiler package solves that problem. The readers aren't bogged down by the tedious task of tweaking and cajoling their environments and/or the example code, which makes for a happier and healthier learning environment.

The execution of this book is, however, not as good as it could be. Why? Although the text is well-written and the book is well-produced, the author simply doesn't know his subject well enough. For reasons known only to them, Addison-Wesley apparently decided to publish a C++ book without giving it a sound, technical review. This is not typical of their efforts.

An aside: You are justified in being suspicious of my motives here. I too am the author of a C++ tutorial book. I hope and believe that my objectivity is intact, because my book does not target Macintosh programmers and is not competition for this one. I'll give you some examples of what I didn't like and let you decide for yourself.

The first four chapters show promise as Mark tells how to get Thin C++ running and discusses some of the syntactical improvements that C++ brings to C. But in Chapter 5, "Object Programming Basics," things start to fall apart. He begins with an Employee class with no access specifiers, which means that all of its members are private. Then he provides code fragments that use the class as if the members were public. This practice proceeds for about 12 pages during which he adds a constructor, a destructor, and other members, all of them private. Then, at last, he introduces access specifiers, tells you that a class without them would be useless (ignoring the possibility of abstract base classes), but does not bother to confess that all of the code that he just taught you would not even compile. The author tells you that every instance of a class, every object, gets its own copy of the data members, which is true, and its own pointers to member functions, which is definitely not true, at least not in a reasonable C++ implementation. He tells you that "_a call to a member function must originate with a single object." Not until 160 pages later do you learn about static members where this absolute rule is found to be false.

Throughout the book, the author uses the this pointer for the redundant dereferencing of members from within member functions, a style that is widely shunned by experienced C++ programmers. Not only that, but he tells you to do the same, saying that it makes the code "a little easier to read."

The following quote is typical of the book's lack of understanding of C++.

Notice that the constructor is declared without a return value. Constructors never return a value. Thus, you won't want to call any functions that do return a value inside your constructor. As an example, it's not a good idea to allocate memory inside your constructor. [italics added]

He then goes on to describe a kludge called the two-stage construction designed to get around the obvious problems created by the silly rule he just formed. There are occasions for such a construction, such as when overloaded constructors share common construction code, but the common code is usually private and hidden from the class user rather than public and called by the class user after construction as taught by this book.

The discussion on access specifiers uses an example where the private specifier prevents unauthorized employees from giving one another a pay raise. The discussion of inheritance suggests that you would derive a Sales class from the Employee class to describe employees in the sales department. The multiple inheritance discussion derives an Object class from a HasColor class and a HasShape class. This example is almost funny because even the names of the classes show the book's lack of understanding of sound object-oriented design practices. Apparently the author has not learned that inheritance should be used for "Is A" relationships while "Has A" relationships are best represented by embedded objects, pointers, and references. All of these examples suggest that they were formed by a C programmer who has just learned the basics of C++ and who has little or no object-oriented programming experience. Lest you doubt, read this quote:

"_a derived class inherits all of the nonprivate data members and member functions from its base class."

Mark persists in this mistaken notion throughout the discussion on inheritance. C++ programmers know that a derived class inherits everything, interface as well as behavior, from the base class. The private, public, and protected access specifiers define which of the base class's members may be accessed by the derived class's member functions but not what is inherited.

There are even examples of bad C code in this book. Some of the exercises have fixed-length character arrays as data members and use strcpy to initialize the arrays from constructor parameters. There is no bounds checking whatsoever. At the very least the initialization should use strncpy to assure memory integrity in case the class user tries to initialize an object with an oversized character array. There are more technical errors in the book, but I feel like I'm beating it to death, so I'll stop here.

Don't expect from the title that this book will teach you Macintosh desktop C++ programming. It is not about that. It is about C++, and it happens to use the Macintosh as its development computer. All of the examples use iostreams for user input and output. A short appendix discusses the Macintosh Toolbox and the MacApp, Think, and Bedrock class libraries, but no details are given.

The best part of this book is an appendix that contains a reprint of "Unofficial C++ Style Guide" by Dave Goldsmith and Jack Palevich, from develop, The Apple Technical Journal (Issue 2, April, 1990). It documents the C++ style conventions used by Apple programmers. I don't agree with all of the conventions, but an organization of cooperating programmers needs something, and not everyone agrees with everything. Here's an example of one of the better ones: "One of the most powerful features of the C and C++ languages is the C preprocessor. Don't use it."

I like that. By the way, this appendix adds some essential information that the main body of the text leaves out--virtual destructors, for example.

Don't blame the author for the quality of this book. Blame the publisher. The author is guilty only of ignorance. Like most novice C++ programmers, he doesn't yet know what he doesn't know. The publisher is guilty of irresponsibility. The same author has an earlier book called Learn C on the Macintosh. That book did well. Knowing publishers as I do, I can just see them pressuring him to go with the trends and do a quick C++ version. He should have held them off until he knew the subject better. They should have had the text reviewed by an experienced C++ programmer and teacher. At the very least, they owed him that.

Now for the third reason that I broke my own rule and chose to discuss a not-so-good book. The book comes with a disk and a coupon. The disk contains Thin C++, a stripped-down version of Symantec C++ for the Macintosh and a clever knockoff name based on Symantec's Think C compiler. The coupon gets you a discount on the complete Symantec C++ development environment. The advanced galley that Addison-Wesley sent me doesn't provide the cost of the book or the amount of the discount, but check it out in the bookstore. If the book cost is less than the discount, and you want the Symantec product, buy the book, clip the coupon, keep the diskette as a scratch, and use the book for a doorstop.


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