C PROGRAMMING

SD '95 East, ANSI C++, Framework Wars

Al Stevens

I spent a week last fall at Software Development '95 East in my hometown of Washington, DC. The show was a big improvement over the previous year--more attendees, more exhibitors, more parties. It's taken this long for the east-coast show to catch on. It was in Boston for a while and then in DC starting last year. I'm glad that DC worked out, because I like having an excuse to return home every year.

It's fun to take Judy to these shows. As one of the speakers, I can usually wangle a pass for her. They make a copy of my badge and substitute her name, which identifies her as an editor for DDJ. That gets her all kinds of attention from exhibitors who schmooze for favorable mention in this magazine. Judy and I go our separate ways among the exhibitors. I pick my route based on what's new for C/C++ developers: This booth is showing an interesting programmer's editor, another has a new compiler version, and still others have class libraries, debuggers, and so on. Judy plans her tour based on who's got the freebies: A yo-yo over here, fly swatters over there, T-shirts, buttons, ball-point pens, tote bags, an indoor boomerang. It's lucky we drove up from Florida in the minivan.

On Thursday evening, we went to the Symantec product-announcement party. The announcement presentation was a real yawner. The presenter got cute and tried an unrehearsed demonstration involving a lot of ad hoc code on the overhead screen. Members of the audience were calling out code corrections while the presenter fumbled and hemmed and hawed his way through the exercise, which never worked. Some of us slept through it. Everyone stayed, though, because the free beer and food wasn't available until the presentation was complete. They sure know how to hold onto an audience of programmers.

At the party, Symantec gave away C++ compiler CD-ROMs, which did not interest Judy. When they ran out of CD-ROMs, they gave away T-shirts and a promise to mail a CD-ROM. Judy went for the T-shirt. They wouldn't give her one. Turns out you needed a coupon to qualify, and she didn't have one. She puffed out her DDJ editor's badge and tried to look important but to no avail. The T-shirt official was immutable. She returned to the table empty-handed and vowed never again to wear last year's Symantec T-shirt, which she treasures because it was given to her personally by none other than Gene Wang. She said that if Gene had been there this year, she would have gotten one of those new T-shirts. It's not fun being around Judy when she's been snubbed this way. Fortunately, they soon ran out of beer and food, too. "It figures," she said, and we left along with everyone else. They were boxing up the remaining T-shirts as we walked out. Judy looked the other way. "I should've worn my new Borland T-shirt," she whispered.

Borland C++ 5.0

I spoke to one of the Borland C++ compiler developers about impending Version 5.0. It was a bit confusing, not because of the compiler, but because the developer was the one I used to talk to at Symantec. He switched to Borland at about the same time that Borland's chief compiler guy went to Microsoft. Gene Wang used to work for Borland and jumped to Symantec a couple of years ago amidst a flurry of ado about trade secrets and such. Before that, Borland's Brad Silverberg moved to Microsoft. As a result of all these shifts, we had to cancel our annual Benedict Arnold Loyalty award; the playing field got to be too even.

Anyway, back to Borland C++ 5.0. The Borland developer told me that the new compiler would have all the new ANSI features described in the April '95 draft specification, which would once again put Borland C++ ahead of Microsoft Visual C++ 4.0, which is missing most of the new stuff. I asked about support for Microsoft Foundation Classes in the Borland compiler. He said that the compiler would support but not distribute MFC, which means that if you can get hold of the MFC source code (distributed with Visual C++), Borland C++ will compile it. I expect that Borland's visual-development environment will work with MFC, too, but don't quote me. I asked why Borland would not distribute MFC as Watcom and Symantec do. The developer said that Microsoft refuses to license the MFC technology to Borland. The two companies have been arch rivals for years. Gates has been quoted as saying that he hates Philippe Kahn. Hates? If I had umpty-ump billion dollars, I'd love everybody--even Philippe. Wake up, Bill. Philippe went to another company. Borland has a significant slice of the compiler market. It would be nothing but good for Microsoft if all those programmers were using MFC.

SoftBoard

My favorite SD '95 East exhibit was not software at all, but a hardware device, one of those obvious things that, as soon as you see it, you wonder why you didn't think of it. SoftBoard from Microfield Graphics is a whiteboard interfaced to a PC. At first you don't believe it. A conventional-looking whiteboard sits on an easel next to the PC. Selecting a color dry-erase marker from the tray, you draw a picture or write something on the whiteboard. As you do, a Windows application in the PC, which has a white window the same shape as the whiteboard, displays what you mark. You can save the picture as a bitmap file.

How did they do it? First, the whiteboard is not conventional at all. It casts laser scanners in two axes across the face of the whiteboard. Second, the markers are not conventional either. Each marker has a band with a bar code just above the tip. The scanners read the bar code and send the coordinates and code to the PC through the serial port. The code identifies the color, and the application does the rest.

This is not only cool, it has lots of potential. In my C and C++ training classes, I often use the whiteboard to amplify a concept or answer a question. The students hastily copy the charts into their notes because I'll be erasing the picture to make room for the next explanation. How convenient, timesaving, and accurate it would be if students could push a button and save those illustrations electronically with all the information intact. This technology also solves one of my objections about remote training across a network, where the students have an audio/video window of the instructor but no way to clearly see or capture the whiteboard presentations. SoftBoard is not cheap. The smallest one is about $2800, but it solves several long-standing problems associated with training and presentations. If they had been handing out samples at the show, I'd have sent Judy to their booth right off.

JMcC's Keynote

The highlight of the show was the keynote address by Jim McCarthy, Microsoft's product manager for Visual C++. He addressed the problems of schedules, slippage, and shipping versions in large software projects. His observations hit the mark every time. One of them (my favorite) describes his answer to managers who find it unacceptable if a programmer does not know when something is going to be finished. Jim says that it is just too bad. They can't hire someone who knows; they can only hire someone who lies. He told the story of a Visual C++ feature, originally estimated to take four months, then estimated to take ten, causing it to miss the ship date for the next release. Jim wanted that feature and sent his programmers away to think about it. Two days later they came back. How long did they think it would take after giving it more thought? Forget it, they said, it's completed. In the course of reconsidering their estimate, they had just gone ahead and implemented the feature. His point? Those are the best programmers in the business, and they don't know the difference between a ten-month feature and a two-day feature. The practice of estimating software development is a long way from being understood, much less accurate.

ANSI C++ Innovations

The ANSI X3J16 committee published a draft specification in April 1995 for public review. Presumably, it was meant for public comment, too, but they failed to allow sufficient time for anyone to analyze the new language in any detail. Furthermore, if my experience is typical, comments from the public are stonewalled as soon as they are submitted. The committee wants to get on with its work and apparently does not want to be bothered by public reaction. I heard that the committee's position is that the public had sufficient time to react prior to the publication of this document. True enough if you are on the committee and privy to their deliberations. Those of us who are not had little opportunity between the document's availability and the cutoff date for comments for a comprehensive review.

It could be argued that anyone that interested should be on the committee, a participant rather than a critic. It can be counterargued that commentators, particularly in the press, must preserve the objectivity of the outsider looking in. An activist forms emotional commitments to issues based on not-always-equal quantities of ego and merit. Besides, I hate meetings.

The committee has made substantial changes in the area of templates and the standard library. We are correct to raise concerns about how well those changes have been tested and validated in so little time with no mature compilers available to implement them. The public deserves a better shot at a specification that is bound to change the way we work for the next several years.

There are many small changes to the language that have minor consequences. The mutable and typename keywords. The bool type. You can use them or ignore them. There are many substantial changes, too, such as the namespace feature and the specification of a standard exception hierarchy. These should not be ignored because they solve serious deficiencies in traditional C and C++.

But the committee wears two masks. If you suggest a change that they prefer not to consider, they fall back on the time-worn argument that the change breaks too much existing code, whether or not it does. But when they want to make a change, regardless of its consequences to existing code, they just up and make it. Well, maybe not in such a cavalier fashion, but they do it nonetheless. This tendency is not rampant, but it occurs enough to make me uncomfortable.

ifstream::read and ofstream::write

Case in point. I suggested that the ifstream::read and ofstream::write functions be changed from using char* and int parameters to using void* and size_t parameters. This would eliminate casts when the buffer argument was not a char array or pointer and would permit a program to read and write buffers greater in length than the limits of a signed integer (32K on a 16-bit int implementation). I discussed this situation with Bjarne Stroustrup, who agreed that the specification should change. He called it a bug. (Actually, we discussed only the int versus size_t part of the issue.) I submitted the recommendation to the committee. The answer came back that this change would break too much existing code. Not understanding how, I asked for an example. I'm still waiting for an answer. I suppose that an implementation that uses an int wider than its size_t could have problems, but do such implementations exist? I don't know. If I can allocate a record buffer with a size_t size argument, why can't I read a record into that block with the same argument?

A look at the specification reveals why the committee might not want to consider such a change. The code that would be broken is in the document itself, not in programs out in the real world. Because virtually everything in the standard library is now specified as a template implementation, the types for those parameters are not what they used to be in traditional C++. They are now specified with typedefs rather than intrinsic types. The buffer argument is a pointer to char_type, which is almost impossible to interpret because they left it out of the document's index. I found its typedef in a header file in the document. It's a charT, which is also missing from the index, but which turns out to be a template argument, so it could wind up being anything. The read and write functions' size arguments are of type streamsize. The streamsize type is typedefed as an INT_T, which is either int or wint_t depending on what CHAR_T is. If anybody suggested to me that I change something inside that mess as a volunteer and for no pay, I'd tell them whatever it took to make them go away.

A New for Statement

Here's a change the committee made. The new specification for declarations in the first expression of a for statement reveals an insidious problem. Example 1(a) is taken from the specification.

The specification states that "...the scope of the name(s) declared extends to the end of the for-statement." This is quite different from how the C++ language works now.

A lot of existing code will be broken by this change. In fact, the committee's example does not even compile with contemporary compilers because the declaration, under current rules, is in the same scope as the for statement. Example 1(b) shows the effective code and scope if you substitute an independent declaration and a while loop.

Clearly, you could not declare another i integer after the while loop because the first i integer is still in scope. And here's where the ANSI specification springs a leak. Earlier on the same page, the specification uses just such an example to demonstrate the operation of the for statement. C'mon guys, which way is it?

Example 1(c) shows a common idiom that uses the current rules to advantage. Change the rule, and a lot of similar code no longer compiles. If there happens to be another int i in an outer scope in Example 1(c), the program compiles okay under the new rule, but it stops working properly.

Bjarne Stroustrup prefers the new rule and wishes he had specified it that way originally. If he had, this issue would not exist today. He did not, however, and now he has successfully campaigned to have the committee correct what he perceives to be his earlier mistake. Over the years Bjarne's influence has led the committee down many paths, mostly good. His championing of STL and the template changes that STL needs, for example, was a major step forward for the language. But this change is, in my opinion, a mistake. That's because: 1. I really like the earlier usage; and 2. I don't think the change's benefits outweigh its consequences.

Bjarne disagrees. The decision to effect this change was not made lightly. According to him, the decision was debated and postponed for years because of concerns for existing code, teaching materials, compilers, and so on. His winning argument to the committee in favor of the change was:

You will have programmers complaining whatever you decide, but personally I will feel better defending the cleaner and--based on experience--more intuitive rule than apologizing for the old rule and explaining what code the change would break.

So it is what it is, and for a time we will be in transition until all compilers comply and all old programs are converted. During the interim I would encourage compiler vendors to include a compile-time option, perhaps a pragma, that retreats to the older rule. Better yet, a warning that alerts programmers about the potential change to what was an overriding scope. Programmers should code to the new rule, but if an older program fails to work properly with a new compiler, this new behavior might be the problem.

enum as a Type

I don't know if this next change is intentional or merely the result of one compiler vendor's interpretation of an ambiguous specification. It turned up when I ported the code in Example 2(a) from Visual C++ 2.0 to the beta Visual C++ 4.0. Example 2(b) is the message the compiler returned to me.

According to Microsoft's interpretation of the ANSI specification, you have to overload the arithmetic operators for enum types. The relational operators still work the way they always did. Example 2(c) shows the overloaded operator function for the postfix ++ operator. After that, you need the prefix ++ operator, the two decrement operators, the binary addition and subtraction operators, and the op-equal addition and subtraction operators. Finally, all those operators must be overloaded for any other enum types you define.

I pored through the ANSI draft specification, but I could not determine from the document with any degree of certainty whether Microsoft's so-called new behavior is proper or improper. Unless I missed something, which is possible given the complexity of the document, this behavior is totally implementation dependent, subject to the compiler builder's interpretation of the specification. If that assumption is correct, the portability of the language has been seriously compromised. If incorrect, and Microsoft is right, this change to the way that enum variables work might make for a more type-safe language, but it will certainly break a lot of existing code. If Microsoft is wrong, the committee needs to tighten up the specification.

MFC versus OWL

In a recent column, I expressed my opinion that the Microsoft Foundation Class (MFC) library is the best of the Windows program-framework class libraries. That opinion generated more heated response than anything I've said since I suggested that OS/2's installation procedure makes huge vacuum-cleaner noises. I seem to know the way to the heart of a programming religious issue. Mostly I heard from programmers who prefer Borland's Object Windows Library (OWL) over MFC. OWL is, they say, more object oriented than MFC. Maybe. Is that necessarily true and, if so, is it better? It's a matter of opinion. Does OWL encapsulate more of the Windows API than MFC? Perhaps. It seems to. OWL also seems to hide more of the API, making it more difficult to get at things either not supported or encapsulated in a way that the programmer wishes to override. These discussions, mostly online, went on for a time, devoting only the first few messages to the relative merits of either class library. After that, they concentrated on what constitutes a de facto standard, whether Microsoft is force-feeding us something, and whether the trade press (me) is being taken in by hype. More message bandwidth was given to Microsoft bashing and defending than to the technical issues, which might point to the true heart of the religious wars.

The two sides squared off and fired their volleys back and forth. It was a friendly exchange, and I won't go further into the details of the battle. Mostly I stood back to see which way it went.

It soon became evident that if you start with MFC, you like MFC; if you start with OWL, you like OWL. Most of the commentators had not spent a lot of time with both products, which is to be expected. You do not, in the course of making a living, dedicate substantial time to two competing, mutually exclusive tools. It is natural, then, to prefer the one you use. The respondents seemed to believe that C programmers who already knew the Windows C API preferred MFC, while C++ programmers with little or no exposure to Windows programming preferred OWL. That was not my experience. I am in the latter group and prefer MFC. But, having developed DOS application-framework function and class libraries (D-Flat and D-Flat++), my experience and bias might not be typical.

A recurring theme was that influential columnists are irresponsible when they state their opinions so unequivocally. That position is taken only by those who disagree with the opinions, of course. First, you overestimate my influence. I could write until my mug was cerulean about my favorite programmer's editor, and not one of you would switch. Second, what difference does it make if a few people decide to use what I use. If it works for me, it should work for them. Are programmers somehow diminished when their choice is the different (or same) drummer? I don't think so.

I expected objections from the vendors of the competing class libraries, but they didn't make a peep. Maybe they dismiss my opinions as the wild ranting of an uncontrollable and out-of-control programmer. Maybe they're right. On the other hand, maybe they secretly agree with me. I doubt it.

Those of you who cared enough to object got one thing right. My experience with OWL is probably not current enough to simply say that MFC is better. I like MFC better, so I use it, which accounts for my more current knowledge of its capabilities. I did not intend to give the impression that other class libraries are not worth a look.

Example 1: (a) New C++ for statement; (b) the effective code and scope if you substitute an independent declaration and a while loop; (c) common idiom that uses the current rules to advantage.

(a)
int i = 42;
int a[10];
for (int i = 0; i < 10; i++)
    a[i] = 1;
int j = i;      // j = 42;

(b)
int i = 0;
while (i < 10) {
    a[i] = i;
    i++;
}

(c)
for (int i = 0; i < maxentries; i++)  {
    // ...
    if ( ... )  // some loop-terminating condition
        break;
}
if (i < maxentries)
    // the loop was broken before reaching the end

Example 2: (a) Code being ported from Visual C++ 2.0 to the beta Visual C++ 4.0; (b) message returned from the compiler; (c) the overloaded operator function for the postfix ++ operator.

(a)
enum Color { red, green, blue };
int main()
{
    Color clr = red;
    clr++;
    return 0;
}

(b)
error C2676: binary '++' : 'enum 
Color' does not define this 
operator or a conversion to a 
type acceptable to the predefined 
operator (new behavior; please see help)

(c)
Color operator++(Color& c, int)
{
    Color temp = c;
    int cc = c;
    c = static_cast<Color>(++cc);
    return temp;
}