C PROGRAMMING

A Pensive Look at Pens and C++

This article contains the following executables: DFLT13.ARC D13TXT.ARC

Al Stevens

This month is my fourth anniversary as DDJ's "C Programming" columnist, and it is also the annual C issue. For this special time, I'm going to suspend work on D-Flat and talk about some C++ issues and a potential pen-based D-Flat library. Both subjects are forward-looking, I hope, and appropriate for this issue. Next month wraps up the continuing saga of D-Flat. After that, we'll start looking at D-Flat++, the rewrite of the CUA interface library in C++.

After four years of almost exclusive coverage of C-language programming in this column, I am thinking that the growing popularity of C++ needs more attention from this forum. I have been using C++ in my own development for quite some time and am happy with and confident in my personal preference for it. The time I devote to D-Flat++ and your reaction to it will chart the course of this column for the future. If you are predisposed to resist, I ask that you bear with me. Let me show through the comparison of D-Flat and D-Flat++ how the notational improvements of C++ improve the craft. I might not be able to demonstrate it in this column, but I have learned, too, that a design process that concentrates on the objects first rather than the procedures is more intuitive and produces a sounder and more maintainable software system. This is not news. Others have been saying this for years. But now there are C++ compilers at every level, and the rest of us can find out for ourselves.

PenD-Flat

Pen-based computers are here for a while, and we should be aware of what that means to software development. A pen-based development environment will resemble one for an embedded system, where you develop on something other than the target hardware. No one would want to squint and scratch their way through a small clipboard-sized, handheld, compile-and-debug session with Borland C++, for example. I guess you could do your programming while you ride the exercise bike, lounge at the beach, or take that nature walk, but you'd soon lose patience as you grappled with the pen and pad to change code, set breakpoints, and look at variables. Besides, not many of the little critters will have the megabytes of disk and RAM to install the new breed of gargantuan compilers. No, the pen-based platform is not ideal for software development. You still need all the tools that you need for traditional programming, but you also need something hooked to the AT that simulates the pen and pad.

This should give you a clue about what kind of applications are best served by pen-based software and what kinds are not. The notion that pen computers will proliferate in executive offices in the hands of keyboard-shy CEOs is silly. Today's executives might be less than computer literate, but that is changing. Before pen-based technology is good enough for serious word processing and spreadsheets, it will be overtaken by two things--voice-based computing and a new generation of technology-savvy executives. Therefore, do not plan to launch a pen-based word processor product. No one will buy it. Pen computing will wear a blue collar and will embrace vertical applications. It will realize its potential in walk-around applications where mobility and freedom from cabling are important and where there are some data to be entered that cannot be scanned. The UPS person in my hometown uses a pen tablet to record deliveries and recipient signatures. It's an experimental program. In some cities, you can already get a traffic ticket written and printed by a pen-based computer--RoboCop. Combine a pen with a bar-code reader, and you have an excellent warehouse-inventory input device. Scan the inventory number's bar code and write the quantity-on-hand. Aluminum siding, screen enclosure, driveway paving, and roofing salespeople could use small pen-based CAD applications to design and cost home improvements while sitting in the customer's living room. Vertical applications every one. Opportunities for programmers.

Most pen-based systems have graphical operating environments at their foundation, and you need a GUI development environment. Microsoft PenWindows is one example. PenDOS, from Communications Intelligence Corporation (Redwood City, California), is a pen-based operating environment that runs in text mode on top of MS-DOS. When used along with a cooperating digitizing tablet and pen, PenDOS provides a pen interface to text-mode DOS applications. Typical DOS applications need little or no modification to run with PenDOS. Most mouse and keyboard operations are handled by the environment as if you entered them with traditional devices. PenDOS emulates mouse actions by the movement and tapping of the pen on the screen's surface. It emulates keyboard input by popping up a Writing Window into which you manually write text with the pen. The environment uses character-recognition algorithms to translate your scribbles into keyboard characters. Your entries are displayed in a text-entry window, and when you like what you've written on a line, you tap the Send button. PenDOS stuffs the characters into the application as if you had typed them. It works well, recognizing a variety of handwriting styles and requiring little practice to become comfortable with its use.

Even a pen-based application needs a user interface. I was not sure that the interfaces that work with the keyboard, screen, and mouse would be meaningful on the pen/tablet platform, so I decided to use D-Flat and PenDOS to experiment with a pen-based CUA interface. In theory, a D-Flat application would run under PenDOS with little or no trouble. This combination would offer the developers of pen-based applications a CUA interface that uses pen taps and movements instead of mouse actions and hand-written pen text input instead of keyboard input.

I used the Wacom HD-648A Handwrite Digitizer to test PenDOS with D-Flat. This device has a VGA-compatible LCD screen and a serial pen-input device. The drivers that come with PenDOS work with the Wacom. The pen emulates a serial mouse. This particular Wacom device is intended for developers who would test their programs with traditional PC hardware. Once debugged, the applications could be embedded into specialty pen-based DOS computers. The ideal testing configuration has the Wacom connected to the VGA port and the system console assigned to a monochrome monitor--the typical two-monitor testing platform. That allows you to use the keyboard and monochrome screen to debug while the Wacom VGA device displays the application screens.

With PenDOS and the Wacom digitizer installed, the display satisfactorily emulated the LCD VGA. I tried out some DOS commands through the Writing Window. It works, but users will probably not like entering DOS commands that way. You'll need to boot into the embedded application, I think. I ran D-Flat's Memopad example program unmodified under PenDOS. The pen properly selected menus, chose menu commands, pressed command buttons, and selected items from list boxes, check boxes, and radio buttons. I could move and resize windows easily with point-and-drag operations. Double-clicking is double-tapping with the pen. If anything, the CUA mouse operations are easier and more intuitive with a pen pointed directly at a horizontal screen than they are with a mouse on the desk moving a cursor on a vertical screen.

(A hint. If you lose pencils, combs, car keys, eyeglasses, and your TV's remote control with predictable regularity the way I do, then you'd better tie the pen to the pad with a strong string. Until I did, that pen disappeared at least daily and then showed up in the darnedest places.)

All D-Flat needs to make it a complete pen-based user interface is an edit-box control that uses handwriting input similar to the PenDOS Writing Window, and the PenDOS "gesture" characters for text editing with a pen. I have to return the borrowed Wacom digitizer, so there is not time to do that, but I might get another crack at it later, perhaps as a part of the D-Flat++ project.

C/C++ in Action

I attended the C plus C++ in Action conference in Teaneck, New Jersey during the week of April 27. This conference, which has a London edition in June and one in Santa Clara, California in September, is organized and operated by the Wang Institute of Boston University. Although it aims at both C and C++, the strong emphasis of the conference and the attention of the attendees was definitely on C++. I sat on a panel that discussed C and C++ as a family of languages. Christopher Skelly, the technical chairman of the conference was moderator. The panel included Bjarne Stroustrup, Jim Brodie, Dmitry Lenkov, P.J. Plauger, Jim Coplien, and me. Bjarne, of course, designed C++. Brodie is chairman of the X3J11 ANSI C Standards committee. Lenkov is chairman of the X3J16 ANSI C++ Standards committee. Plauger is a well-known author and speaker on C and a member of X3J11 and its ISO counterpart. Coplien is an AT&T researcher and instructor and the author of Advanced C++ Programming Styles and Idioms. As you might expect, this motley panel contained overlapping camps with overlapping agenda--language designer, language standardizers, authors about language, and language users.

The panel discussed several concerns, among them the feeling in the C/C++ community that the independent efforts to standardize and extend the two languages could make them less compatible than they are now, creating a watershed between C and C++ and polarizing rather than assimilating the two groups. The panel's consensus was that C and C++ should become no less and no more compatible than they are today.

There was a lot of attention given to what might happen with C and C++ in the next several years, and less given to what programmers in the audience had to deal with on the following Monday morning when they went back to work. The questions from the audience indicated--to me, at least--that they were as interested in the latter issue as in the former. The panel's discussion was directed more to the former.

One member of the audience asked why, in a session he attended, most of the other attendees held up their hands when asked who was a C++ programmer, yet few of them admitted to understanding or using object-oriented design and programming. Wasn't OOP, asked the programmer, the primary advantage that C++ had over C? And, if so, how come so few C++ programmers understood or practiced it?

Most of those programmers have learned the notational extensions that C++ adds to the C language. They know about C++ comments, references, global scope resolution, default function arguments, inline functions, mixing declarations and procedural statements, const, anonymous unions, unnamed function parameters, and structs and enums as discrete types. And they agree that C++ is easy to learn and use--as long as you don't have to design and use classes. The gray areas in C++ get darker when you get into classes and inheritance, and that is where many programmers have the most trouble.

And that, in a nutshell, is the number one failure of the C++ evangelism, I believe. The most powerful feature of C++ is the one that is most difficult to learn to use, and we are not moving fast enough to correct the learning problem. There are real advantages to using C++ over C. There are few programming circumstances where C++ will not do a better job. One of those circumstances is when the programmers do not understand the advantages and are not sold on giving it a try. It is a public relations problem as much as anything else. Programmers are immunized to hype.

The question was asked, "When should you decide to use C and when should you decide to use C++?" Most of us had seen or heard about projects where the decision to use C++, and particularly object-oriented design, was made because someone in a position of influence had been told that C++ is not only trendy but a panacea as well. In virtually every such instance, the project failed. If the problem domain is already supported by a mature library of C functions, then C is a good choice. If the programming staff has neither experience with OOP/C++ nor the inclination to learn, then C is still a good choice. If neither condition exists, then C++ is a good choice. But the one compelling decision point is the bent and the ability of the programmers to use C++, and that is not always foregone.

To further illustrate, let me retreat to an earlier conference session where Jim Coplien described some advanced ways to make C++ do things that you might want it to do. This was an intense, informative, and entertaining session. Jim's voice and delivery remind me of TV news-journalist Michael Kinsley, who baits his co-host and guests on "Crossfire" every week, although Jim gets his point across without making you want to punch him in the nose. I'll discuss two of his C++ techniques without trying to explain how they work. In the first place, I might get it wrong. That notwithstanding, there is a much better source for this information, and I highly recommend Jim's book, Advanced C++ Programming Styles and Idioms, which addresses these two methods and much more. I'll discuss the book later in this column.

The first technique, called the "counted pointer," shares data values between the first instantiation of an object and any copies you might make through assignment. It overloads the -> operator so that you can use it to get at the common representation. With the class defined according to Jim's specification, you can write the code in Example 1.

Example 1: The counted-pointer technique.

  String s1;
  String s2 = "Hi";
  s1 = s2;
  int len = s1->Length();

Note that although s1 is not a pointer, the notation treats it like one. Never mind why you would want to do this. I'm not sure I would, but Jim gave examples of where this approach solves a particular problem.

The second technique laments the lack of a virtual constructor in C++ and uses what is called the envelope/letter idiom to give the effect of virtual constructors. The using program deals only with objects of an envelope class, yet the objects themselves take on different characteristics, depending on the context in which they are constructed, as defined in letter classes. This goes a bit farther than simply overloading constructors, because you can add contexts later without modifying the envelope class, thus giving the effect of a virtual constructor mechanism. Example 2 shows some code that uses objects of a class defined with the envelope/letter idiom.

Example 2: Objects of a class defined with the envelope/letter idiom.

  Number a, b, c;     // 3 Number variables
  a = 1;              // a is integral
  b = 2.3;            // b is real
  c = (Complex) a+b;  // c is complexm

All three variables are of type Number, yet each has a different data structure and behavior. Once again, don't worry about understanding why a programmer would want to do this. The point is that you are being told that it is possible with C++.

Now, imagine a discussion between a C++ guru and a C programmer who has been told that C++ is the future. The guru, having attended Jim's session or read Jim's book, foolishly begins to explain some of these methods to the C programmer expecting these features to be a strong selling point to a C++ rookie.

Guru: You can overload operators in C++. You can even overload the addition operator to perform subtraction on your numerical classes.

Programmer: That's good?

G: No, that's bad. It would not be intuitive and would be poor programming practice. You might write a program that no one else could read.

P: So overloading operators that way is always bad.

G: No, sometimes it's good. There are times when you want to overload an operator in nonintuitive ways. Consider the counted pointer. Its -> operator is overloaded so that you use it to the right of an object that is not a pointer.

P: I don't understand why one example is good and the other is bad.

G: Because finding a solution to the problem outweighs the need to write intuitive code. If it offends you, don't use it.

P. Then why are you telling me about it?

G: Because you might need it some day, and to show you another less-than-obvious way that C++ is extensible--how it lets you go beyond the original language design.

P: What are some other ways?

G: Consider the letter/envelope idiom that gives you the effect of virtual constructors. You can instantiate three numerical types by saying, Number a, b, c; and then you can say a = 123, and a will be an integer. You can say b = 456.78, and b will be a float. You can cast a+b to a complex number, assign it to c, and c will be complex. There you have three variables, all of type Number, and each one has a different internal data representation and different behavior, based on the context in which you assigned something to it. Neat, huh?

P: (Getting hot) Wait a minute. I could do that 15 years ago with Basic and I didn't have to jump through all these stupid envelope/letter-class hoops. I want to use C++ because it has strong typing, and they tell me that's what I need. What happened to all that strong typing?

G: (Smugly) With C++ you have the freedom to bypass it.

P: If I want freedom, I'll stick with C.

And there, dear hearts, is where you lose a convert. This is typical of the hype that obscures C++. Bjarne observes that programming shops have a lot of reasons not to switch to C++, and he effectively counters each one of them. But his message is delivered to a kindred audience. The attendees of these conferences are predisposed to like Bjarne and the language that sprang from his labors, even when they don't fully understand it. But there is a strong C constituency out there, and, until recently at least, that constituency was growing. Many of them don't live, breathe, and sleep programming and every new paradigm shift, and they don't go to the conferences to worship at the graven C++ image. Those are the folks we need to reach, and they are mainly skeptical.

Consider what C programmers see when they look at source code. Unless there are some tricky preprocessor macros, what they see is what they get. The C source code reveals everything the C compiler is doing for the programmer. If a function is going to be called, the programmer must call it. If a variable is going to be declared, the programmer must declare it. Not necessarily so with C++, where a lot goes on under the surface. The simple statement that assigns one object to another can launch a barrage of constructor functions and hidden temporary objects. Until the programmer is comfortable with that sort of semi-organized chaos, there will be some furrowing of beaded brows when he or she steps through the code with a debugger.

Then there are the many hidden features in C++ that you cannot reveal up front, lest you scare the pants off the programmer the way our C++ guru did a few paragraphs back. There is a lot to learn after you learn the language. First you solo. Then you get your pilot's license. Then you learn how to fly. Learning C++ is a process of discovery. Teaching C++ is managing that process.

For example, I want to call a derived virtual function from within a base constructor. Why not? It seems like a good idea. I'm stepping through the program now. How come I'm executing the base's function instead of the overriding virtual derived one? It's right there in the code. The compiler knows about it. (Slow down. Let it sink in. The base class's constructor is running before the derived class has been constructed. There is no derived virtual function yet.) What's that you say? There is, too. I compiled it myself and it's in the memory map. Don't tell me it doesn't exist yet. (Well, yes it does, but not in the name of the object you are instantiating at this time. Virtual functions are called through the object's pointers to the derived class's virtual function table, and your object hasn't built those pointers yet.) How was I supposed to know that? (Now you know.)

Here's another example. I'll just call a constructor function from another constructor function for the same class. One of them does most of the work, so I'll let the others do their unique processing and then call the principal one. What's this? It doesn't work. The principal constructor never executes. So, I step through the code with the debugger. There's the constructor. I'm executing it. How come what it is constructing right here before my very eyes never gets constructed in my object? Well, it's done. Now what? I'm in the destructor function! What am I doing here? (Slow down again. When you called that constructor function, you instantiated an unnamed, hidden, temporary object. That's why the original object does not get constructed properly; you are constructing one you never see. When you exited from the original constructor function, the temporary object went out of scope, and its destructor was called.) Why can't I just call that constructor and get it called the way I want? (Because the syntax for calling a constructor function is one of the several ways that you instantiate an object.)

These are just some of the traps and pitfalls that await the unwary fledgling C++ programmer. Word about them leaks out, and programmers get intimidated. You have to discover every one of them for yourself. They are neither intuitive nor obvious in the language syntax, and even when an instructor or tutorial book tells you about them, you forget until they bite you. Programming is supposed to get easier with each new shift, not harder. Hah.

Coplien's Book

The literature of C++ and object-oriented design and programming is improving. As promised, here are my reactions to James O. Coplien's Advanced C++ Programming Styles and Idioms (Addison-Wesley, 1992). To begin with, do not use this as your first book on C++. Its title should tell you that. The book assumes that you already know the syntax of the C++ language, and its emphasis is on programming idioms, which use the features of C++ to "express functionality outside the language proper, while giving the illusion of being part of the language." This is extensibility to the extreme. C is extensible in that you can add functions that modify data values and program flow. C++ is more extensible in that you can add data types with their own behavior and representations. The programming idioms that Coplien advances extend C++ further still, apparently changing the behavior of the language to solve problems not addressed in the language design. The book begins by discussing some traditional idioms that even the newest C++ programmer will recognize--copy constructors that solve the memory-management problem and iostreams that use familiar overloaded insertion and extraction operators, for example. But gradually, the book gets into the more arcane, such as the counted pointer and envelope/letter classes discussed earlier.

That you can do these tricks with C++ and that the practice gains credibility by endorsements such as this book are what give pause to many an outside observer. Some of this stuff is the bungee jumping of programming. Programming on steroids. If you do not like C and you do not want to like C++, this book will reinforce your bias. If you like C and C++, this book will reinforce those feelings, too. Congratulations, Jim, you've written a book that everyone will like.

Coplien's close association with C++ since its inception allows him to provide personal insight into some of its history and how parts of the language evolved. Even if you think you already know C++ pretty well, I'd recommend this book. There is always more to learn, and the book is well written -- a delight to read, in fact -- and may very well be the most important programming book since K&R.


Copyright © 1992, Dr. Dobb's Journal