Dr. Dobb's Journal December 1997
Al is a DDJ contributing editor. He can be contacted at alstevens@ddj.com.It's time again to dip into the old mailbag and read some of my mail from readers of this column. From time to time the bag fills up, so I organize the letters by subject and respond to them here. The letters include corrections to my code, reactions to my comments, and some observations from readers on one and another column topic.
This one goes all the way back to December '96. I published a template class library that implements the common undo function that many applications use. Reuven Lax found a bug:
I was browsing through some back issues of DDJ and noticed what seems to be a bug in some of your code, specifically, a memory leak. In the December 1996 issue, you provided code for a generic undo class library. In order to limit the number of undo operations to 300, you stored the list of UndoNode pointers in a circular list. When the list is full and a client tries to add an UndoNode, the UndoData::AddUndoNode [function] effectively removes the first pointer from the undo list. However, when this takes place, that pointer is not deleted! As a result, as soon as the number of alterations a user performs exceeds one hundred, there will be a memory leak for each additional action he performs.
This was a timely message. I was about to release a program with that bug. Thanks Reuven.
My column about Borland's C++Builder generated more mail than any other single "C Programming" column. Many readers wrote to discuss Delphi, and I addressed that issue a few months ago. These letters are specifically about my comments on C++Builder.
I said that C++Builder did not seem to sense divide-by-zero exceptions. Mark Bracey straightened me out by explaining that the compiler optimizes out statements such as a=b/0 that don't do anything.
...I've found I have to put in some complex statement after the offending statements to trick the optimizer not to eliminate them. I do these to aid in debugging. The linker will even eliminate a property's set and get functions if the property is never accessed.
Harold Howe explained a procedural problem I was having with C++Builder's IDE:
You...saved your form CPP file as Amort.cpp. You then tried to save the project MAK file as Amort.MAK. C++Builder won't let you do this because the project requires both a MAK and a CPP file. For example, if I save a project as HELLOWORLD.MAK, C++Builder also generates a HELLOWORLD.CPP file. This source file contains the WinMain function. My form has to be saved as something else (MAINFORM.CPP).
When I complained about how C++Builder uses pointers instead of references or data members for form members, Charles Calvert of Borland explained why:
All the VCL classes have to be treated as pointers because the underlying VCL (Object Pascal) code is designed that way. In Delphi, the decision was made to turn all references to classes into pointers. In Delphi, it is simply impossible to create a class without creating it on the heap. This was done for performance and memory allocation related reasons, and also because it let us simplify notation, as explained in these bullets points:
All Delphi classes are allocated on the heap.
Therefore we could treat them all with the same syntax.
Since pointer syntax is confusing, why not let the user reference those pointers with dot notation, as if they were static classes?
In other words, since all classes had to be pointers, we had the luxury of letting you act as though they were static references, since the compiler knew it had to be a pointer, even if you didn't use pointer notation. Furthermore, most allocation and deallocations for these classes are handled automatically by the VCL.
Unfortunately, there was no simple way to give the same syntax to C++ programmers, since the rules of ANSI C++ wouldn't let us change the meaning of pointer syntax globally, the way we did in Object Pascal. Of course, allocation and deallocation for classes is still usually handled automatically, as it is in Delphi. However, in Borland C++Builder, you must use pointer notation when referencing VCL classes.
Ken White also dealt with my pointer objections:
I really like having the VCL components accessed by pointers. To me, it feels like I'm "really programming." It's a psychological lift to go from a RAD environment in which pointers, like idle time processing, are considered the devil's work to one in which pointers are bandied about like a beach ball at an outdoor concert. We're really in C++!
I guessed about how Borland had implemented the Delphi Pascal VCL in C++Builder. Russ LeMaster corrected my guess and added even more insight into the pointer controversy:
You mentioned that you thought Borland had placed a wrapper around the Pascal VCL. Actually, they didn't even do that. It turns out that all they did is generate C++ header files that allow C++Builder apps to use the components from the Delphi VCL. Delphi's components are stored in a huge DLL, [which] has an extension of DCL for the Delphi version and CCL for the C++Builder version. These files are nothing more than DLLs that have all of the components defined for the VCL bundled into it. When you compile the application, the code is pulled out of the DLL and placed into the executable.
Now, what does all this mean? Well, it turns out that the VCL for C++Builder is exactly the same as that for Delphi. If you will notice, when you look at the header files and constructors for forms you build in C++Builder, there are no auto constructors. The "Objects" that are components are seemingly never created. Actually, they are being created dynamically when the forms are created and built. This is the reason we are stuck having to use pointers to dereference these instead of the good old ".".
There is another side effect of the VCL being still Pascal. It turns out that Object Pascal has no concept of operator overloading. Therefore, you can derive from those objects and use operator overloading.
My C++Builder experimental project was a loan-amortization program. I had not figured out how to make the program print the amortization schedule report. Michael Snodgrass not only figured it out, he sent me the project files to update my project.
The amortization program includes an algorithm that implements the amortization formula. I confessed that I had lifted the algorithm from an earlier program and really didn't understand it. Ke-Ming Ma took pity on me and provided the derivation for the formula:
(1) The sum of the sequence 1, k, k2, k3, ..., kn is (1-k(n+1))/(1-k).
(This is easily shown. Let the sum of the same sequence be represented by S(n). Then S(n)-k× S(n)=1-k(n+1). We solve for S(n).)
(2) The sum of the sequence k, k2, k3,..., kn is k× S(n-1)=
k× (1-kn)/(1-k)(=S(n)-1)
* * *
Suppose proceeds of a mortgage, in dollar amount P, are disbursed at time 0, the beginning of month 1. The repayment stream is a sequence of n monthly payments, PMT, beginning at the end of month 1 (or the beginning of month 2). The present value must equal P and is obtained by summing each payment discounted by the factor 1/(1+i)k, where i is the monthly interest rate and k is the month for which the payment is made.
The convention in interest rate mathematics is to represent /(1+i) by v. Note that 1/v
1=i.
Thus P=PMT× v+PMT× v2+...+PMT× vn
=PMT× (v+v2+...+vn)
=PMT× [v× (1-vn)/(1-v)] by (2)
Solving for PMT,
PMT =P× (1-v)/[(1-vn)× v]
=P× (1/v-1)/(1-vn)
=P× i/(1-vn), which is implemented
as double Mortgage::Payment().
Thanks Ke-Ming. It all makes sense now.
Not everyone approves of what I write and how I write it. Someone named Tels (no other name provided) wrote:
[I] read your writing about C++Builder for Win. I don't know what is really new in it, because you didn't write it clearly (or I didn't understand your text).
If you ever have used Visual Builder [in IBM's VisualAge for C++], you will see that this little program [that] you coded in a day can be "drawn" and coded in one hour. (OK, OK, 1.5 hours, allowing coffee break.)
Tomas Ponzi pulled no punches.
I cannot hesitate to tell you that your article in May 1997 is simply pathetic. I have never read an article where I felt so genuinely embarrassed on behalf of the writer and the magazine. I hope you make an effort in future, or at least tackle subjects which you are mildly interested in.
I wrote to Tomas asking for specifics, but he did not respond.
The following month, I took advantage of a vacation trip to the Florida Keys to look into Optima++, a product that is similar in concept to C++Builder. Savas Savvides took issue:
Your review of Optima++ 1.5 in the June issue of Dr. Dobb's Journal was, in my opinion, very unprofessional. Just like I would not like a surgeon doing surgery on me while he or she is on vacation, similarly I would not like reviewers like you to do a review while on the road and away from their tools -- tools that measure portability of legacy code, tools that measure compilation times, tools that measure execution speed, etc. I'd rather that you delayed the review and gave it a professional all-around kick-all-tires kind of review instead of what you have done. I have been using Optima++ 1.0 and 1.5, and they are excellent products. Yes, they have their share of problems, but I worked my way around them and was very impressed with Optima's approach to programming. I think it is a very serious contender and you should treat it as such by seriously reviewing it. Not to mention that half of your Optima++ 1.5 review talked about the Florida Keys. It is bad that the Keys are headed for an ecological disaster. All we need is more people going there.
I'm glad that Mr. Savvides has had success with Optima++. My review was mostly positive, so I'm not sure why he objects. There is one inaccuracy in Mr. Savvides' criticism. Journalists measure the length of editorial content in inches. The Optima++ review took approximately 30 inches. The Key West portion used three of those 30 inches. That's a bit off the half mark given the way Miss Minnie Moore taught me to cipher at Lorton Elementary School.
In the March 1997 column, I presented a datascope program that uses two serial ports to read the output from each of the two devices being monitored, and pass the byte stream on to the other device. Rainier Lamers describes a problem and an alternative approach:
A potential problem is the fact that you are routing the comms through the PC. This results in a retransmission delay of at least one character time. This can cause problems that you are trying to find with a protocol to go away or cause new ones.
Much simpler is to simply not disrupt the "link under test" at all. Just wire the two transmission lines to the receive inputs of your monitoring PC. Your software will only have to receive, never transmit. It is perfectly legal to connect more than one receiver to an RS232 transmitter. You can make yourself a nice little adapter cable for this. Try two back-to-back D-9 male and female (to insert into the comm lines) plus a bit of wire and two additional plugs to go to the monitoring PC.
I used the following line of Perl in a CGI article to demonstrate, tongue-in-cheek, that Perl is no less cryptic than C:
$s=~s/.*\.//;
Ivan Griffin explains the code for the rest of us:
The "$s" is a variable, in this case a string of some sort. The "=~" implies that some operation is going to be performed on the string, and the result stored back into the original variable. The s/<regular_expression>/replacement/<scope> is a standard UNIX search and replace syntax (dates from ed editor I think, present in sed, vi, and now Perl).
It searches the string for the regular_expression and replaces it with the replacement text -- in this case, it will cause matched data to be removed from the string. If the scope is not specified, only the first match will be replaced. A scope of "g" implies all matches in the string should be replaced.
"." matches any character. The "*" means zero or more of the previous character. Finally the "\." matches a period. The pattern matching algorithm is greedy -- it tries to match the longest string which fits the regular expression. Therefore, this will try to match the longest substring which ends in a period and remove it from the original string.
In July, I discussed the class and struct keywords in standard C++, giving my criteria for when to use which one. Michael Lee Finney responded:
May I suggest a third policy: Never use "class," because it is not the "is-a" relationship by default, whereas "struct" adheres to the idea of "is-a" since inheritance is public by default. My policy is that private and protected inheritance will not be used without a very good reason, and I haven't encountered a good reason in day to day programming yet (there are theoretical reasons to allow private and protected inheritance, but they occur pretty rarely in practice and probably could be dispensed with entirely with no significant loss of capability). Since I also have a policy that the private/protected/public attribute for member variables and functions must be explicitly stated and not defaulted, the difference between class and struct for me reduces to the use of "isa" for inheritance, which is the entire point of inheritance (and classes). I have been known to define a macro to change "class" into "struct", but that is generally a bad idea because it silently changes the language semantics. I have also heard that Bjarne Stroustrup has said that making inheritance private by default was his biggest mistake in designing C++. I don't know if it was his biggest mistake, but certainly it is up there among the top ten!
Not wanting to leave Bjarne Stroustrup out of this exchange, particularly since Michael read something that attributed an unusual position to Bjarne, I asked him about it. Bjarne responded:
Making private inheritance the default wasn't the biggest mistake...not shipping a better library with release 1.0 was my biggest mistake. Furthermore, the mistake with inheritance was to have a default at all; I should have required :private, :protected, or :public in all cases. If we had to have a default, private inheritance was consistent with the overall aim of defaulting to something that localizes information.
Alan Langford offered his opinion about class and struct:
On class and struct: for me, struct is a useful notation. I've been moving a number of libraries for Computer Telephony to C++ from C. struct tells me that although I may have added a method or two for expediency, the underlying class hasn't really been examined in terms of what role it plays in the new class hierarchy. Typically, my old structs wind up becoming more complex, deriving some functionality from new base classes that have emerged instead of remaining monolithic. That's the real power of C++, it encourages you to reuse ideas rather than cut and paste the code and diddle some details. struct tells me a class dates from the "cut and paste" era.
Chris McKillop commented on my position that we no longer need to teach the stdio function library to non-C programmers who learn C++:
...I have yet to use a C++ compiler on a PC that has a thread-safe streaming library, so whenever threads get involved one must use the stdio family of IO routines to keep data from being messed up. Although this isn't a feature of the C++ stream library..., it is a feature that most compilers support! And since the printf family of IO routines are a little, well, strange at first, I still think they are a good thing to teach the newbie C programmer that is using a C++ compiler.
Edward Diener comments on the ANSI namespace feature, which I addressed in a discussion of the standardization procedure in the September 1997 column:
My guess is that when the C++ Standard finally is finished, if it ever is, all the Windows compiler vendors will have a switch to enable the putting of the C and C++ libraries into the std namespaces, and that this switch will be off by default. Eventually that will change, and the IDEs will turn on that switch by default, but this will only happen after a few more versions. It will be interesting, because of the different mangled names scheme by every compiler vendor, to see the further confusion that entails as people try to use DLLs created by one compiler vendor with EXEs and DLLs created by another compiler vendor. The tower of Babel all over again. However, at a much deeper level, diversity, not uniformity is the essence of life and art.
Tarjei T. Jensen lobbied for fairer treatment of ADA in DDJ:
I think it would have been valuable to contrast the standardization process of Ada 95 and C++. The foreword of the "Ada 95 Rationale" describes the organization that did the work. It looks like it pays to let professionals do the work.
Ada is often unjustly scorned as a language designed by a committee. In fact, Ada has always had a chief designer in charge of the language. There is of course public review, but the chief designer decides on what goes in.
Despite Dr. Dobb's Journal's attempt at burying Ada, the language is alive and well. The vendors say that the amount of civilian use of Ada is steadily increasing. Perhaps the civilians have started to think about how long...the code will be around needing maintenance? Perhaps they worry what the next C++ standard will do to their source code investment?
It does not hurt that Ada reads well.
Marc Bejerano is also an Ada programmer who is bewildered by the C++ standardization process.
After reading your article in the current issue of DDJ I have to admit I am baffled by how long this is taking. I wonder, also, why the Standards committee is taking what was an essentially streamlined and efficient OO language and adding a kitchen sink.
My day-to-day job requires me to write in Ada (both 83 and 95) and I have to say that I'm glad the language is "done" for the time being. Kind of frightening what C++ is becoming.
I got several letters about my reference to the first and second amendments to the Constitution. Howard Mark had this to say:
In the September 1997 issue of DDJ, Al Stevens uses the first amendment to the Constitution to poke fun at ANSI/ISO for warning users not to refer to the proposed Standard under discussion...as a Standard until approved by an accredited Standards body."
Well, Al and the other users had better take that warning seriously, before they find themselves in (legal) hot water, because the organization will not stand behind anything that they have not officially and finally approved. Sure, the first amendment protects your right to say anything you want, but just as with the famous dictum about shouting "FIRE!" in a crowded theater, you're still responsible for what you say.
Now, I'm not a lawyer, but I have been involved with a Standards-setting organization (ASTM) for a good number of years, and the way I understand it, it works like this: If X just tells Y that a something is a Standard (of ANSI, let's say), but it's not, then sure, nobody much cares. But if Y then takes some action based on that statement, and suffers any damage or loss (presumably financial), then he's going to look around for someone to sue. First he'll try to sue ANSI, for promulgating a defective Standard, but ANSI will come back by saying "What do you talking about? That's not any Standard of ours. X was lying to you."
Guess who Y turns to next? X is responsible (and liable) under whatever laws of torts, contracts and other civil laws apply to the case, just as if X told any other lie that resulted in harm. If X stood to benefit from Y's action, it is conceivable that he might even be liable under criminal law, for fraud.
Furthermore, depending on exactly what he revealed of the proposed Standard, and how he revealed it, he might be liable for copyright infringement; ANSI itself could then take action against him on that basis.
So it's important to be careful about what you tell who, in these matters, and what you call it, first amendment notwithstanding.
Gordon Weakliem added these thoughts:
I'd like to thank you for not only the several good points you make regarding the evolving C++ Standard -- your point regarding the attitudes concerning "deprecated C++" vs. "endorsed C++" rings very true. However, the logic behind your statement that implied that the ANSI/ISO committee has no authority to enforce the statement the "we may not refer to this document "...as a Standard until approved by an accredited Standards body" is somehow a violation of our rights as outlined by the first amendment under the US Constitution entirely escapes me. Setting aside the question of authority, the first amendment reads "Congress shall make no law...abridging the freedom of speech, or of the press..." The key word here is Congress, and by extension, the US government, and while it is fashionable to argue against the repression of one's free speech as a first amendment violation, these arguments are simply a trivialization of the intent of the Constitution, which was to protect the citizens of the US from their government's excesses. Without this distinction, I fail to see how copyright law, for example, could be enforced; isn't it my "right" to publish whatever I want? There is not a constitutional right to copyright, yet this is a well recognized legal principle. In any case, I've found that one is generally free to speak one's mind freely, at the risk of being held as a fool or worse...
Only Brian Burton seemed to realize that it was mostly a joke:
I did want to point out...that the second amendment protects the right to own arms to preserve one's own rights, not to use them to deprive others of their rights. So I suppose you are safe from the ANSI Standards police.
My trip to Jim McCarthy's Bootcamp in the August issue generated many letters. I can always count on my friend Edward Diener to bring a different perspective to a subject:
I think that the emphasis on teamwork to achieve a desired goal is fine. I also have a healthy respect, because of my background in the arts, for individual creativity. Most computer software is done as a team due to the complexity of the finished product. The ideals which Mr. McCarthy espouses are good ones and are often more important to the success of software development than much smaller issues that take precedence. I am especially glad that this one-week encounter teaches people to have more enthusiasm for the goal of producing quality software in a timely manner.
The real world intrudes. I have worked as a programmer and consultant for 19 years in the software industry for a plethora of corporations from large to relatively small. I do not recall a single job in all that time where the members of a software development team were encouraged to get together as a team and work out the technical issues involved in producing quality software in a reasonable time frame. I recall numerous times where once a week, meetings were held so that management could fill the software team with company news, views, and various issues totally unrelated to the real success of the product as a team development issue. In those meetings the merest hint of any real technical discussions of various issues important to succeeding in the goal of quality software was almost immediately followed by twitches, nervous looks, and a fairly quick response about discussing these things offline. The reason? The sad fact of it is that in corporate life the leaders, that is, managers and project leaders, invariably know little to next to nothing about the technical issues involved.
A reader who identified himself or herself with only an e-mail address offered a unique opinion, one that merits some attention:
I believe the central problem in software development is the fragmented mind. Ideally only one mind should be involved with any software project. Then it might have a chance of being entirely consistent and correct. Multiple minds, no matter how harmonious emotionally and psychologically, tend to be error prone when dealing with complexity.
Gary Gregg appreciated my application of shared vision to the bandstand.
As a jazz saxophonist and an information systems examiner/manager with the Board of Governors of the Federal Reserve System, I fully appreciate your DDJ article on TeamworX. Not only from the software development side, but also from the solo/support side of playing jazz.
Right/left side brain efforts cannot fully explain the satisfaction of seeing/hearing a well placed line of code/notes work against the program/harmony structure. My first thought was to wish that many of the CDC (Fed-speak for: century date change) folks could use this course if it were available. Think of the many millions of dollars that will be part of the "wasted effort" to get our computer programs and operating systems Year 2000 (Y2K) ready.
DDJ