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

C Programming


C Programming

Al is a DDJ contributing editor. He can be contacted at [email protected].


Bill Allred called me the other day with a software problem. Bill is a jazz musician, bandleader, and musical contractor, and his entire information resource is stored in Corel's WordPerfect suite of applications. An address book application provides a single interface for Bill's contact database, and that part had shut down.

Bill was puzzled because the address book still worked on the old 486 that he had recently retired to be replaced by a new Pentium. The same data files and executable file worked fine on the old machine, but not on the new one. The symptoms he described puzzled me, too. You are supposed to be able to launch the address book from the word processor or as a stand-alone application. The word processor launch hung up the machine. The stand-alone launch displayed the splash panel and then nothing else happened.

Software users rarely describe their problems in sufficient detail over the telephone to enable a fast diagnosis. It isn't their fault; most serious computer problems defy verbal description. Effective support technicians have learned what questions to ask and directions to give to guide users through the steps that lead to a solution. I have never acquired that special skill and learned long ago to make a house call whenever possible to eyeball the problem in the flesh. (For the record, I don't do tech support, but I do help my friends with their computer problems when I can.)

Bill's description of his problem was accurate. The vital clue, however, was the window state that the stand-alone launch assumed. The application was indeed running, but it was minimized as a button on the Windows 95 taskbar. Clicking the button, which should restore the application window to a visible state, had no effect. I opened the button's right-click menu to find that, based on the grayed-out condition of the system menu commands, the window could not be restored. It could only be maximized. I maximized it, and up it popped, full of data, ready for use. Restoring the application window from this state returned it to the unusable, invisible minimized state. Nothing I did put the application in a mode that permitted it to be properly launched from the other applications in the suite, a mode that Bill uses extensively.

Remember I said that everything still worked okay on the old computer. Had the address book ever worked on the new one, I wondered. Yes it had, said Bill, until the previous day when this peculiar behavior had mysteriously started. He had tried reinstalling the suite, but that did not fix anything. Did he uninstall first, I asked. No, because it was not clear what effect uninstall would have on his substantial database of documents. He did not want to risk losing anything. Past experiences with various applications had prompted him to try something -- anything -- else, hence the call to yours truly. Why didn't he call Corel? He runs an older version, which always worked, and felt no compelling reason to upgrade, which he assumed would be their first suggestion.

I doubted my ability to be of much help, having never used the Corel suite myself. But, I figured, if all else fails we can save the data files and uninstall and reinstall the suite. As it turns out, that probably is what Corel's tech support would have recommended, and it probably would have worked. If we had chosen that approach first, Bill would be back in business, but we would never know the cause of the strange behavior, and I would not have learned the valuable lesson that is about to unfold.

The apparent illogical window state got me thinking about how Windows applications typically save such state information. I've often copied Window-state-saving source code from published examples into my own programs. Usually, the application class's ExitInstance member function retrieves the window's current position, size, and state, and uses the WriteProfile... functions to save those values in the Registry. The InitInstance function gets the values from the Registry and restores the application window to the settings it had when it last exited, choosing a default value the first time the program runs. I thought that if the address book application used a similar method, something in the Windows 95 Registry might shed light on the problem.

And so it did. After some searching, I found the Registry entries for the address book application. A Registry key named Position had variables named X, Y, CX, and CY, and those values looked suspicious. I wrote them down (just in case), deleted the key, and restarted the program in stand-alone mode. It worked. I tried launching the address book from the word processor, and that worked, too. Problem solved.

An inspection of the new Registry key values suggested that the previous errant values described a window whose upper-left coordinates were greater than its lower-right coordinates. The illogical window state resulted when the program tried to build its application window with those coordinates. A major clue to the problem was found in one of the X coordinates, which was 511 -- one less than a power of two -- suggesting some kind of overflow error, perhaps one that you incur only by positioning the window in a specific place with a specific size. If my guess is correct, Bill's bad luck was that he stumbled onto that configuration by coincidence. Maybe no one else ever did.

I don't know how those values got turned around, and Bill doesn't recall what he was doing just prior to the first failure, but obviously there's a bug somewhere in the software. I don't know if the uninstall/reinstall strategy would have worked; I've seen uninstalls that do not delete their application's Registry keys. Assuming for this discussion that this uninstall does not delete the Position key, and assuming that this particular symptom is not recorded in Corel's help-desk knowledgebase, this problem could have become one of those tech-support nightmares wherein the only solution is to reformat the hard drive and reinstall everything. Mind, I'm not pretending to know what the outcome would have been for this particular problem with this particular software package; I'm only hypothesizing in order to dramatize the lesson that programmers can take from this experience.

What can we learn from this episode? Even if the bug did not exist, there is always the possibility that Registry keys can get mangled unintentionally or otherwise. A program that assumes valid data variables from any external source is asking for this kind of trouble. It contains what you will soon learn is an "AntiPattern." In this case, the program should have tested those screen coordinate values to see if they described a valid, visible window in the current screen configuration and, if not, retreated to uninitialized default values. I'll be spending this weekend reviewing all my source code to ensure that none of my programs have the same insidious oversight.

AntiPatterns

Everyone knows about "design patterns" from the book Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1995), by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. A new book, AntiPatterns, by William Brown, Raphael Malveau, Hays McCormick III, and Thomas Mowbray (Wiley Computer Publishing, 1998), is a natural fallout of the original idea. I'll let them explain AntiPatterns with a quote from the original manuscript.

An AntiPattern is a literary form that describes a commonly occurring solution to a problem that generates decidedly negative consequences.

The book is due to be published in the Spring of 1998. The publisher permitted me an early look at the manuscript, so these quotes might be slightly different from the published version. The authors go on to say:

AntiPatterns is the first major software research initiative to focus on negative solutions.

The book identifies and describes programming idioms and practices that result in bad software. It further defines the motivation and justification for AntiPattern research with a quote from Jim Coplien:

The presence of "good" patterns in a successful system is not enough; you also must show that those patterns are absent in unsuccessful systems. Likewise, it is useful to show the presence of certain patterns (antipatterns) in unsuccessful systems, and their absence in successful systems.

The bug in Bill's Corel address book application might be what the book calls an "Input Kludge AntiPattern," defined this way:

Software that fails straightforward behavioral tests may be an example of an Input Kludge. An Input Kludge occurs when ad hoc algorithms are employed for handling program input.

Or perhaps the bug is the victim of the dreaded "Cut and Paste Programming" AntiPattern:

Code reused by copying source statements leads to significant maintenance problems.

The cut-and-paste AntiPattern would surely apply if the programmers had copied their window sizing code from one of the examples I've seen in books and help databases.

I read the AntiPatterns manuscript in one sitting and was constantly surprised by its insight and amused by its presentation. Many of the AntiPatterns the authors describe could have been taken from my own experience. The book's theme, to enumerate and encode error-prone programming idioms and practices, is a good idea. The authors have implemented this concept effectively and in a manner conducive to continued extension of the principle. I highly recommend this book.

PTL Version 2

Last month I presented the first installment in a new C Programming column project. PTL, the Persistent Template Library, is a library of template classes that implement the Standard Template Library containers as persist containers. A persistent container is one that retains the objects it stores across iterations of programs that use the container by using a disk file as a storage medium. This month continues the discussion of that project.

PTL persistent container classes are template classes that are derived from STL containers. I said last month that I might eventually overload all the STL member functions that can add objects to or delete objects from containers. The overloaded functions would maintain a flag that specifies whether the container should be written back to the disk file when the container is destroyed. There is no reason to rewrite a container if the program has not modified the container's contents.

Now that I've progressed (retrogressed, actually) with the project somewhat, I have discovered a significant problem: Not all the container-modifying operations can be specialized. For example, a vector of integers can be modified by dereferencing an object in the container through a nonconst iterator with the asterisk points-to operator or by using the allocator's reference type. However, in most -- perhaps all -- STL implementations, iterators for contiguous-object containers are actually C++ pointers and allocator::references are C++ references. Furthermore, you can refer to and change the contained objects by using real C++ pointers instead of STL iterators. C++ does not permit you to overload the behavior of operators for intrinsic types. Consequently, no way exists to intercept the replacement or modification of an object in a container when the change is made with operator* through an iterator or pointer.

The solution, I had hoped, would be to build custom iterators for the derived persistent container classes, ones that themselves overload operator*, and tell users not to use real pointers. This approach to the problem proved to be unworkable. The problem is found in the vector class, which uses C++ pointers as iterators. You cannot derive from an intrinsic type. The standard says that allocators must define pointer and reference types because the containers use those types to refer to the objects being contained. Typically, typedefs associate the names pointer and reference with C++ pointers and references to the parameterized type being contained. The vector class defines its iterators by using the allocator's pointer type. This is an efficient mechanism for accessing objects, but it defeats the objective of intercepting changes to mark a container to be rewritten to the persistent object store.

I have concluded from all this that PTL must be used in very specific ways. You can use iterators to retrieve copies of objects from containers, but you must not use them to replace objects in containers or to directly change objects in containers. In other words, treat all PTL iterators as if they were const_iterators. Otherwise, the automatic mechanism for sensing changes to the container might not work. You must use the STL container algorithms for inserting and deleting objects, making changes to private object copies that you maintain outside of the containers. I don't like this restriction, but I am willing to live with it considering the alternative, which would be to modify the STL template classes themselves.

Last month I similarly discovered that the STL definition does not provide for any communication between allocators and containers so that an allocator can serve as a persistent store mechanism. As a consequence, I implemented PTL as template classes derived from STL container classes rather than as a persistent store allocator that STL container class object declarations can specify. Inasmuch as STL is implemented entirely in header file template source code, you might wonder why I chose not to modify the original STL source code to support persistence. Modifying STL would have presented three problems. First, there is the task of maintaining the specialized container library, fixing it when bugs are found or when revisions to the standard are published, Heaven forbid. Second is portability; such an implementation might turn out to be compiler-dependent based on how much the modified STL depended on unique compiler features. Third, in most cases I could not distribute the source code without getting a special license from the compiler vendor. Although the original Hewlett-Packard distribution is freely distributable even with private modifications, the big compiler guys did not perpetuate that tradition. Borland and Microsoft, for example, each acquired third-party STL libraries, which are based on the HP version but with extensive modifications, and they include copyright and licensing restrictions.

PTL Version 2 is now available for download; see "Resource Center," page 3. It works just like last month's version except that containers are not always rewritten to disk when their destructors execute. Instead, the changed flag is set to False by default. Each derived container class specializes the behavior of the object insertion and deletion member functions to set the changed flag. Containers are rewritten only if they are changed or if the program calls the setchanged() member function.

Little Boy Blue

This week ends a long quest. Several years ago my son-in-law gave me an IBM PS/2 50 computer with no monitor, mouse, or keyboard. His company was about to throw it out. It was slow but reliable. I used it for a while to ensure that my programs would work on the slow beasts that some users might still have. One day, someone left a small IBM VGA eight-inch monitor at my office. When I suggested that he come get it, he said I could keep it or throw it out; it wasn't worth anything. Since the box and tube were both true-blue and with small footprints, I used them together on the workbench. It seemed fitting that my little test machine was not only from Big Blue, but that it came to me at no cost.

On another day, my friend Mike Herman was in my shop and asked about the little machine, now nicknamed "Little Boy Blue," or "LBB" for short. After hearing the story, Mike returned the next day with an IBM keyboard with a PS/2 connector. Out with the generic, in with the blue. Much later, observing how slow LBB was with Windows 3.1, another friend gave me a two-MB memory chip and the IBM PS/2 Reference Diskette -- an original, not a copy -- and LBB ran much better.

I don't know where all this stuff came from, but it was all free. Somewhere along the way a tradition naturally evolved dictating that I must never purchase an addition to LBB. It would not seem right. LBB and its history became a legend among my friends. It was still incomplete, however. The mouse was generic, not true-blue. It worried us all, like having a Porsche with a Jeep steering wheel. Yesterday LBB reached full closure. I was at a local computer store drooling over a Pentium-II 333. My friend Larry Robedeau pulled an antique two-button IBM PS/2 mouse out of his pocket and, with a grin, handed it to me. After several years, LBB is now a 100-percent bonafide true-blue IBM PS/2 Model 50 Z computer. Okay, so the serial numbers don't match up and the slow 286 processor won't run anything significant, but my goal has been attained. Too bad I no longer have any use for it. It takes its place on a shelf next to the Altair, the Imsai, the original IBM AT, and the copy of Turbo C 1.0. Is anyone thinking garage sale?

DDJ


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