Departments


We Have Mail

Letters to the editor may be sent via email to cujed@rdpub.com, or via the postal service to Letters to the Editor, C/C++ Users Journal, 1601 W. 23rd St., Ste 200, Lawrence, KS 66046-2700.


Dear Dr. Plauger,

I was quite surprised by your February 1996 editorial in CUJ. Blown away, really. But it led me to a new understanding, which, in turn, leads me to make a request.

Your current situation, where you do not, and cannot, know "every byte" of the software on your computer, has always been the status quo for most programmers. This is the world in which we live and work. Welcome.

Ten years ago, when you knew your machines so well, I was working with machines running VM/CMS, VMS, and Tops-20. These systems were already so old and so heavily modified that I doubt any living human knew every byte of code. Not being an employee of either IBM or DEC, I certainly had no access to the source.

Slightly more than ten years ago, I was working at Bell Labs, using versions of UNIX from both AT&T and Berkeley. I did have access to the source code, but I had no time to study it, nor permission (on our shared machines) to change it. Since that time, most versions of UNIX have grown proprietary extensions, and an ULTRIX or SunOS kernel can easily exceed 8 MB. A full Linux installation, with source code, requires more than 100 MB. Who has time to study all this?

Most of us don't write embedded systems and small, stand-alone utilities. Most programming is maintenance work on pre-existing apps. Most new development is done in teams. C's deficiencies for programming "in the large" were already well known ten years ago and, as you know, were a big part of what drove the development of C++.

But C++ got sidetracked into the politics of OO, and its facilities for programming "in the large" have been given little importance in the standardization process (namespaces are a welcome exception). Software engineering is now a discipline separate from language methodology, and the engineers do not seem to have been well-represented on the committee.

So, my request: Please apply your new-found sense of shock and bewilderment to your work on the standards committees. Give a thought to us, floundering in our sea of APIs and black boxes, with our wall-charts depicting class hierarchies we will never understand, when designing language features. Maybe when our languages can deal with complexity, our operating systems will be easier to install.

Paul Hayslett
The Cow Bay Software Company
hayslett@iconn.net

I'll try. — pjp


Dear Mr. Plauger,

In reading the article "Selectable Default Constructor Arguments," in The C/C++ Users Journal, February 1996, it seemed to me that the approach described in the article is the wrong solution to a real problem. The article addresses the issue of initializing a large number of data members in the constructor(s) of a class. The main result is a technique for circumventing the standard rules for default parameters which rather cleverly allows for initializing new objects with arguments in arbitrary order with varying defaults, all without generating a factorial number of constructors.

My reaction is that while the code is admirable and even preserves type safety, it fails to address an underlying architectural problem. Any class that has too many data members to comfortably construct within the context of the language structure should probably be redesigned into a number of classes, improving encapsulation. Whether writing in C++ or another modular language, when a module becomes unwieldy, it is more a sign of a need for redesign into more, simpler modules than for a code fix that makes it easier (for a little while) to sustain an over-complicated design.

Thank you,
Tim Farnum
tfarnum@rpa.net

I basically agree with your approach to design. But the technique was so clever I couldn't resist running the article. — pjp


Dear CUJ:

I found it interesting to read Mr. Rod Doe's letter in the November 1995 "We Have Mail." He made several statements that I found telling. He said he had been "programming in ... C++ for three years." Then, he went on to complain about the (almost) OO nature of MFC (it was obvious which application framework he described) relative to the "old days" switch statement. He also mentioned that an application written in C++ wouldn't fit in a flash RAM, but when rewritten in C, it fit with "room to spare."

Together, these statements tell me that Mr. Doe doesn't have a terrific grasp of C++. Consider the issue of a massive switch versus small, object-related functions (most of which are not virtual in MFC, mind you). In the old way, one piece of code — the switch — must have intimate knowledge of everything else happening in the application. The new way ties that knowledge to the class that deserves it. In other words, an object is commanded to do something, and does whatever is appropriate to that command.

Encapsulating, for example, what a window object should do in response to a certain user action is far superior to one function knowing what that window object should do, and what each dialog, and each control on each dialog, and each toolbar button, and each menu item, etc. should do. The difficulty of maintaining such switch statements was what drove the design of the "labyrinthine" message routing in MFC. (I don't mean to say it was implemented as well as it could have been, or that there aren't alternatives, but it is more reasonable to manage.)

Consider a toolbar button. If there is an object that manages the toolbar, that object knows best how to translate a mouse click within its confines into a button-click message. It knows how many buttons it is displaying, how big they are, how much space exists between the buttons, etc. It can determine if the mouse click was within the confines of the toolbar aggregate, and it can determine which button the click affects, if any. It can then send the appropriate message to the enclosing frame for action, after taking any toolbar-specific action itself. If that enclosing frame cannot handle the message, it sends the message to its enclosing frame, and so on, until the message reaches some terminal point in which default behavior is provided or the message is ignored.

The alternative puts the mouse click decoding logic in the one switch statement, along with the decoding logic for managing all the dialogs, other windows, other toolbars, etc., in the application. It is easy to see how that one switch statement can get unwieldy and hard to manage. I wouldn't want to do things that way.

Mr. Doe indicated his naivete with C++ when he described a C++ program that wouldn't fit, while the C version would fit, some finite flash RAM space. C++ demands little more than does C. The compiler is stricter about things for C++ code, but otherwise, you can write C code with a C++ compiler. What you do beyond that is not the result of choosing to program in C++. If Mr. Doe's C++ program wouldn't fit in the available memory, perhaps he didn't design it as well as the follow-on C program (each rewrite of a program usually results in refinements). Perhaps Mr. Doe has much less facility with C++ than he'd like to think.

It's possible Mr. Doe tried to use too many wonderful "features" of C++ and bloated his code. You cannot do anything in C++ that you couldn't do in C. C++ just puts the burden on the compiler to implement many common features, making it easier to write what amounts to complex code. For example, virtual functions are nothing but a table lookup to locate a function pointer and invoke a function through that pointer. Folks have done that for years in C. In C code, it is easy to see the space requirement of the function table and the execution penalty of locating the correct pointer and then calling the function. In C++, all that code is hidden behind that virtual keyword.

Likewise, encapsulation is nothing but a compiler enforcing access rules the programmer chose to implement. You can do the same thing in C by choosing to use functions to access data rather than accessing the data directly. C just doesn't complain if you bypass the functions and manipulate the data directly. (C++ grants you the flexibility of using inline functions to reduce the headaches of preprocessor macros, but both work.)

In short, that the C++ program wouldn't fit in the space of a C program suggests that Mr. Doe wrote code in C++ that was less space efficient. It is not an indictment of C++ per se.

Neither application frameworks nor C++ (or OOP) are panaceas. They don't automatically make you write better code. Frequently, when misunderstood and misapplied, they can help you write worse code. Sure, C++ adds complexity to C. But, that complexity is more than outweighed by the advantages it brings. Well written C++ code can be every bit as efficient as C code. On the other hand, C++ invites you (and makes it relatively easy) to write code that might have proven too complicated for you to tackle in C.

Let's be careful where we put the blame in the future.

Robert Stewart

I quite agree with your last statement. We shouldn't blame programmers for misusing features that are easy to misuse. Yes, C++ can help control the complexity of a large and extensible application, just as you describe so succinctly. It can also inflict surprising overheads on someone who has less ambitious goals in mind. To me, it is a matter of choosing the right tool for the job. Some projects I would only do in C++, given the choice. Others I would only do in C. I have trouble seeing either language as "better" across the board. — pjp


Editor,

In the February 1996 issue of The C/C++ Users Journal, William Garces (wlgarces@uccs.edu) wrote about having the ink on the magazine cover come off on his fingers. I have had this problem for years, with most slick-surfaced magazines, and had assumed that everyone else had the same problem. Perhaps it is related to differences in skin chemistry (sweat pH? degree of oiliness?). The same thing happens with interior pages, but not as often, possibly due to the covers having a continuous layer of ink, whereas interior pages have more whitespace.

John F. Eldredge

Yeah, me too. Thanks for the reassurance. — pjp


Dear Mr. Plauger,

I just read your editorial in the March issue and I just had to write this — like right now!

You really hit the nail on the head. Management never understood that some projects were so complex that it took a day or two just to get the intricacies of the program back into the mind, so they didn't see any harm in interrupting for meetings (managers love meetings — that's the object of their being!). It is probably inherent in most (not all) managers that since their pay is the highest it obviously follows that their job is the most difficult, and therefore "obviously" all other jobs are less complicated — and it is thus impossible for them to understand that a job performed by an underling could actually be more complex than their own. This makes it impossible for them to comprehend true complexity.

Many of my projects were set aside for other "more urgent" projects which were in turn set aside for even more urgent projects. Can management comprehend the frustration of a creative programmer who is never permitted to complete anything? One project I was allowed to finish had a great deal of upper management pressure behind it because the system it was to replace was costing the company about $1,000,000 annually to run. My project cut the cost to $20,000 annually, a saving of $980,000 — for which the company showed its gratitude by giving me an ashtray set on edge in plastic and an invitation to an awards dinner held one block away from an area so bad that the police always patrolled in groups of two or more officers!

I held a second job for many years — as a police officer, primarily on street patrol. Believe it or not, it was a break from my programming duties! I retired from my programming job just over five years ago, and late last year dropped by the old "code foundry" for a visit. I ran into my former boss, and told him, quite honestly, that the stress was less as a police officer than as a programmer. My explanation: "At least, we're allowed to shoot back!"

Your truly,
J. W. Moorhouse

Hmm. Good point. — pjp


Dear Mr. Plauger,

I am CS student at LSU. I need your suggestions on building a C to C++ translator. I know C++ compiler can compile C code, if we can take care of C++ key words and other small annoyances. If developers using C want to switch to C++ and maintain and expand their C code in C++ what choices do they have? If they are going to mix C code with classes etc. of C++ what kind of design are they going to end up with? Is it a good idea to translate C code by combining function of C to form C++ classes and methods?

Here when I say translator I mean using some kind of reverse engineering approach and restructuring. Further, what if I just translate C code to C++ by making each C function a class in C++ and make the C function a C++ method of that class. Do you think that this will be better than mixing C and C++ code? Do you know of anyone who switched to C++ and is maintaining (adding new functionality) their old C systems in C++? How are they doing it (if you know)?

It will be helpful to me if you can e-mail your comments on my idea. I like your work in CUJ. Thank you in advance for your time and help.

Ramachenga Valasa vrr@bit.csc.lsu.edu

One fairly safe way to migrate from C to C++ is to first change the C code so that it compiles as C++. Tom Plum and Dan Saks call this common dialect "typesafe C." You can then replace selected groups of C functions with a class that encapsulates the functions as methods. Look for groups that operate on a common data structure, whose internal form does not need to be known to functions outside the group.

Don't expect to turn all existing C code into more advanced C++ forms. If there's enough old code, in fact, be prepared to keep it around as typesafe C for years to come. As you write new code, however, make a point of writing it in terms of well encapsulated classes. With time, you will find yourself becoming ever more "object oriented." But don't feel that everything you write has to look like textbook examples of C++ classes. Just keep it readable and maintainable. Good luck. — pjp


Dear Dr. Plauger,

I'm a self-taught C programmer (as well as various assembly languages) who has been reading CUJ for several years. Not being totally convinced of all the hyped merits of C++ and OOP, I've been concerned with the recent leanings toward the newer language. I am learning C++, if only to be better able to use the Borland compilers and some of the libraries, etc.

Two recent additions to your magazine, "The Column That Needs a Name" and "The Learning C/C++urve," have been wonderful additions — they're helping me learn C++, and teaching me new things about C. These are major enhancements, and I thank you and the authors. Finally, the "C/C++ Sources" column is turning into a nice resource as well.

Thanks for all the great work.

Sincerely,

Jim Sanford
wb4gcs@amsat.org

Thanks for the feedback. I agree that both Dan Saks and Bobby Schmidt make valuable contributions to the magazine. (Actually, Dan has been with us for many years. His column has just come through a name-identity crisis.) And since you also mention "C/C++ Sources," let's not forget to credit Victor Volkman, our intrepid web surfer, for the fine work he is doing. — pjp


Dear Editor,

Further to my previous letter about the November 1995 issue: I spotted a few more errors and debateable points in the December issue.

In Bobby Schmidt's column I found his discussion of lvalues to be a little misleading. This is best exemplified by his statement that in strcpy(s, "hello"), s is an lvalue and "hello" is an rvalue. Not only is this wrong but it is very confusing since it tends to imply that calling strcpy is somehow equivalent to an assignment, and that the array s is being assigned to. In fact s is converted to a pointer to its first element and only the characters pointed to are modified.

Of course, s, like any object, is an lvalue, but when used in the above statement it becomes an rvalue. An array is converted to a pointer to its first element (except when used in sizeof or when you take its address) and is not an lvalue (see the C Standard 6.2.2.1).

In Pete Becker's column he presents several not very useful alternatives to a perfectly acceptable goto out of a nested loop. Changing loop variables except in the loop control expressions is an extremely dangerous practice that should not even be contemplated. And using flag variables (called done in his code) should also be avoided if possible since it makes the flow of control difficult to follow and the code hard to maintain.

The only alternative that should be considered, is to place the whole thing in a function and replace the goto with a return. Of course, this is cheating since it does not make the control flow any easier to follow. But it does encourage the splitting of code into more functions (a problem in even the best code I have seen). Personally, I have never used a goto in twelve years of C programming, but that doesn't mean I wouldn't if the situation called for it.

Despite the above I think your publication is excellent, and this is not an obcomp (obligatory compliment). I have found it much more interesting and informative than any other magazine around.

Andrew Phillips
andrew@encom.mpx.com.au

Bobby Schmidt replies:

Ah, perception is reality; it all depends on your point of view.

Looking at the objects in their original form, outside the context of the call, s is an lvalue (which I got right), but so is the string literal (which I got wrong, as witnessed by 6.3.1 in the C Standard, and should have known better about, given the literal has an address and occupies storage). Within the context of the call to strcpy, as you say, both arguments decay into rvalue pointers to their respective first elements. From the perspective of strcpy itself, though, the first parameter is a local char * object (lvalue) and the second a local char const * object (lvalue), so in that context we are both wrong. In any event, my original assertion that the first is an lvalue and the second an rvalue is never true, so I get sent back down to AA ball, losing out on my dream of backing up Junior in center.

I think the abiding lesson here is less one of accuracy and more one of precision. The types and values of entities in C and C++ mutate easily and are quite dependent on context. In this particular case, I should have more clearly differentiated what an object or value is vs. the role it plays, a distinction I'll observe more closely in the future.

Oh, and as for your comment last month about my November column's unspecified behavior example, all I can say is, Saks and I both made several passes over that one before publication, and we still got it wrong. Guess he'll be shagging fly balls with me.

Your description of the strcpy call is technically more precise. I mostly agree with your observations about using goto and flags in loops, but I tend to be more tolerant of different styles. I ask primarily that the result be readable. Thanks for writing. — pjp


Dear Mr. Lansinger,

I've just finished reading your interesting article in CUJ ("An Embedded C++ Application," February '96), and enjoyed it very much. A small improvement to your code would be to use C macros for the far and volatile keywords problem. You can add a special include file c_cpp.h with the following code (and some more code for volatile):

#ifdef __cplusplus
typedef unsigned short int WORDfar;
#else
/* If needed */
typedef unsigned short int WORD;
#define WORDfar WORD far
#endif /* __cplusplus */

The important feature of this code is that it saves a pass on the code (no need to use a search and replace utility). However, I hope that by now you have a C++ compiler for your target microcontroller, which is the best solution :-)

Sincerely,

Eli Gur Mobix
Communications Ltd

Ed Lansinger replies:

I am glad you enjoyed the article. Thank you for your suggestion. Unfortunately, it appears not to work.

Your suggestion relies on the proper handling of the #ifdef.. #else..#endif conditional compilation section by cfront and the transferral of this section into the resulting C code. If this were to happen, it would be a simple matter to define __cplusplus before running cfront, enabling the first conditional section, and then run the C compiler on the output from cfront without defining __cplusplus, enabling the second conditional section and achieving the desired result.

Consider, though, that cfront does not include a preprocessor, a fact that I perhaps should have mentioned in the article. All #include, #ifdef, #define, etc. directives are handled in a separate preprocessing step that creates an intermediate file just before the running of cfront. cfront itself cannot handle these. The preprocessor, which is another component of the Glockenspiel C++ system that I used, removes these directives and replaces them with the appropriate text substitutions. The conditional section is thereby removed even before cfront runs and thus is not present in the resulting C code. I am afraid I do not see a useful and error-free way to reinsert your code or any similar code during this process in order to get this idea to work.

I hope I have understood your suggestion fully; please correct me if I overlooked something. Thank you again for your input.

Sincerely,
Ed Lansinger

Eli Gur replies:

Thank you for your reply.

You did understand my suggestion fully, and since cfront does not include a preprocessor it will not work. It might be simpler and faster to replace one line at the beginning of the souce file than to replace every occurence of a string throughout the file, e.g., replacing

typedef unsigned short int WORDfar;

with

/* If needed */
typedef unsigned short int WORD;
#define WORDfar WORD far

However, since the additional pass (for search and replace) is still needed, it is not a principal improvement.

I do hope that by now you have a C++ compiler for your target microcontroller... Again, thank you for the interesting article.

Sincerely,
Eli Gur Mobix
Communications Ltd