Departments


We Have Mail


Letters to the editor may be sent via email to cujed@mfi.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 CUJ,

Just a quick note to let you know about a typo in my article titled "A Wrapper Class for Dynamically Linked Plug-ins" (CUJ, May 1999). In the article introduction, I indicated that I was using the GNU GCC compiler v2.8.2 on Solaris. In fact, the most recent version of GCC is 2.8.1. Sorry for any confusion.

Eric Roe

Thanks. — mb


Dear Editor,

I enjoyed Daniel Wisehart's article on "Debugging Embedded Systems" (CUJ, June 1999). I found the techniques for debugging multithreaded applications particularly interesting. However, I found one error that probably deserves comment.

Mr. Wisehart seems to have confused his descriptions of the DATA and BSS memory sections. He places uninitialized global data in the DATA section, and initialized global data in the BSS sections. Actually, the reverse is true. The DATA section contains initialized data, which is copied from ROM into RAM by the C startup code. BSS merely reserves memory for data that will be initialized at runtime (i.e., uninitialized data). Most C startup routines will fill the contents of BSS with zeros.

I thought at first that this might be a typo, but the author refers to these sections incorrectly several times in the "Understanding Embedded Memory" section, and in Figure 1. He also refers to constant BSS data, which does not exist. I believe that older (a.out?) executable file formats put constant data, like the first few lines in Figure 1, in the TEXT section. Newer formats will designate a separate memory section for this data, such as RODATA or STRINGS.

All of this can make for serious brain pain for programmers who haven't had to deal with it, but it is nevertheless important to understand. I look forward to seeing more CUJ articles on programming "close to the hardware."

With best regards,

Michael Elwood
mkelwood@qsicorp.com

Dan Wisehart replies:

Dear Mr. Elwood,

Thank you for your kind remarks on my article and for catching my error.

It has been a long time since I looked up the definitions of DATA and BSS. As you noted, I transposed their usage. I believe that if you reverse them in the article, the article stands otherwise correct.

I appologize for my error and again thank you for your comments.

Regards,

Daniel Wisehart

It's my job to catch things like this and I missed it. I do know the difference between DATA and BSS. Mea culpa. — pjp


Hi,

I am a regular reader of you magazine, and a professional C++ developer. I enjoy Chuck Allison's "import.java.*" column very much — it provides the right amount (and kind) of Java information that I need for my current level of interest in that language. I am writing to you because I have a strong disagreement with an opinion voiced in Chuck's column in the May 1999 issue.

In comparing C++ constructor/destructor semantics to the Java try/finally block, Chuck writes that "...Java doesn't have destructors, but it does have the finally clause, which is an even better solution..." I couldn't disagree more, and I bet most of your C++ readership will also disagree with such an odd statement.

C++ constructor/destructor semantics give a class author the opportunity to totally encapsulate resource allocation/release from the class user code. This greatly reduces the chance of errors due to class users forgetting (or misunderstanding) the implementation details of the resources being managed by the class. I have found it a fundamentally recurring theme in object-oriented programming that classes should control their own resources internally (especially under exceptional conditions), regardless of the programming language being used.

The fact that Java class user code must deal with the releasing of class resources during exceptions breaks encapsulation, almost forcing a Java class author to provide public access to initialize and restore resources that might otherwise be kept hidden from the users of the class. This causes exactly the kind of resource leaking maintenance code nightmares that programmers are consistently trying to avoid by defensive programming. The only advantage I see to the Java finally clause is that it gives the Java developer a chance to handle resource clean-up under exception handling, and it seems to me that it exists to solve the problems left over by the lack of destructors in Java, and not as a better solution, as Chuck implies in his column.

The sample source code accompanying the article even supports this — Chuck's own class user code shows that a publically accessible "close" method must be used for no other reason than to handle file resource deallocation under exception processing!

I hope Chuck's column continues to provide important cross-language commentary in spite of my disagreement on this one particular point.

Sincerely,

Allen Broadman

Chuck Allison replies:

I must admit I spoke rather loosely. What I was actually thinking is that Java's finally is an improvement over C++ exception handling control syntax, and that it is convenient in some situations to just use finally instead of having to create a wrapper object whose destructor deallocates a resource. But I didn't mean to say that finally was a replacement for object-based resource management, let alone an improvement. I appreciate your sound and thoughtful feedback.

Chuck Allison (The Harmonious CodeSmith)
Consulting Editor, C/C++ Users Journal
cda@freshsources.com


Dear CUJ,

Help! My C++ Bibles are totally out of date. What are some of the best ones published since C++ has been standardized?

Sharon Harvey
Normandale Community College
Bloomington, MN

It depends on what you look for in a "Bible." If you're looking for a book of Do's and Don'ts, get Scott Meyers' Effective C++, Second Edition. I'd recommend Chuck Allison's book, C&C++ Code Capsules, for a practical treatment of key C++ features. Matt Austern's Generic Programming and the STL is definitely New Testament, and worth having if you're going to do anything with STL. Finally, Bjarne Stroustrup's The C++ Programming Language, Third Edition, is as formidable as most bibles in terms of sheer page count, and makes a good all-around reference. Hope this helps. — mb.


Hi Marc,

I just got the June CUJ and read Mr. Bavestrelli's article. I hope you wouldn't mind if I shared a few criticisms with you — and him. I started reading the article with hope. I always considered assertions a powerful tool and I was expecting to find new and interesting things. I easlily passed over the slight inaccuracy right in the opening "certainly my favorite statement is assert." (assert is not a statement, it's a macro. But anyway that's food for language lawyers.)

All my hopes fell apart when I read the author's "favorite macro:"

#define SAFE(exp) (ASSERT(exp), (exp))

No matter how fond the author is about it, that's plain bad advice he's giving. Macros that double evaluate their argument are evil. To compound the problem, the macro evaluates the expression only once in release builds, yielding to subtly different behaviors. Read: incredibly hard to trace bugs, defeating the whole purpose of this "debugging" macro in the first place.

The proof is in the article itself. The author spends most of the article carefully explaining that you should use great care in exercising the macro. He tries to provide a useful aid in debugging, but the tool itself is at best fragile, comes with a pile of caveats, and relies on a lot of programmer discipline to be properly used. That's not a tool.

There are a few ways around it. It would have been impossible in spaceship-era C++ with templates, exceptions, and type inference to rely on such an abomination for getting decent debugging. The tools I like the most were written by Petru Marginean (margineanp@mmanet.com). I would certainly recommend you to follow up with him.

Reading the code, I also found some other problems. Basically I didn't like any of the macros, as the author relies heavily on them. In my humble opinion, macros in C++ should be at most thin layers that add preprocessor variables and a bit of syntactic sugar over function calls or very carefully-chosen constructs. Please allow me to explain my disagreements one by one:

1. ASSERT(v) inexplicably uses the ancient MFC constant THIS_FILE, where __FILE__ would have been much more portable and appropriate.

2. ASSERT_ONCE uses a bracketed statement that can be terminated by its user with or without a semicolon, leading to confusing constructs. For instance, the following code fails mysteriously to compile:

if (something)
    ASSERT_ONCE(blah);
else
    statement

As Alan Perlis would have put it, "Syntactic sugar causes cancer of semicolon."

What about a static inline template function taking __LINE__ as the template parameter and sporting a static variable inside?

3. ASSERT_LEN does not work correctly for simple pointers. The problem is, it doesn't signal this at compile time. Ironically, only a few months ago Bobby Schimdt hashed to death the lengthof pseudo-keyword that measures the length of a fixed-size array in a typesafe manner.

4. ASSERT_ARRAYINDEX relies on ARRAYSIZE which does not appear in the article. Given the macro aboundance, I guess ARRAYSIZE is nothing but the ill-recommended:

#define ARRAYSIZE(s) (sizeof(s) / sizeof(*s))

which again fails silently to work for dynamically-sized arrays. Again, ARRAYSIZE evaluates its argument twice. [No it doesn't. It evaluates it zero times. — pjp]

5. Going to the implementation file, I found a fixed-size buffer used with sprintf in a very risky way. The buffer isn't even generously allocated — 1,024 bytes for a long message containing a full path, an arbitrary long expression, and an arbitrary long appplication name. All this in a debugging library.

6. The log filename is hardcoded, making the design unappealing for a library.

7. The catch statement does not catch the dynamically allocated MFC exceptions (CException and derivees). Each time you'll have an exception in that code, a memory leak will occur. This, again, in a debug library.

For all these reasons, I would recommend CUJ readers not to use any of the macros in their actual form. Each and every of them is a vivid example on how badly macros can be misused. There are ways to do all what they (unsuccessfully) attempt to do and more, in a true C++, safe manner. The underlying code is also very brittle, especially for a debug library, and needs serious rework.

I hope this helps both the author and the magazine readers.

Best regards,

Andrei Alexandrescu

Giovanni Bavestrelli replies:

Dear Marc,

I knew it would happen some day, after the considerable very positive feedback I received, I got the first negative feedback to one of my articles (luckily, my third). Here is my answer to Andrei Alexandrescu's mail that you sent me:

Andrei Writes:

"Macros that double evaluate their argument are evil. To compound the problem, the macro evaluates the expression only once in release builds, yielding to subtly different behaviors. Read: incredibly hard to trace bugs, defeating the whole purpose of this ‘debugging' macro in the first place."

Andrei is right (and I warned about this in my article) but does he think the same of the standard and acclaimed assert macro? He says, "I always considered assertions a powerful tool." But the Standard C library (or the MFC one for that matter) assert macro has the exact same problem he so adamantly condemns. The macro does not evaluate the expression in release builds, but evaluates it once in debugging builds, opening up the door to all the same incredibly hard to trace bugs Andrei talks about.

Actually, for the standard assert macro the problems are more common. Any side effect in a standard assert can give problems, whereas only side effects that are different between one evaluation and the next of the expression within SAFE can give problems (as I stated in the article). Anyway, the difference in number of debug/release evaluations (2-1=1, 1-0=1) between assert and SAFE is the same, but I think in general assert has helped to find more bugs than it created, and so will SAFE if used appropriatelly. That's why I dedicated most of the article to explaining how it works and warning about the potential problem of the double evaluation in debugging builds, trusting programmers would learn how to use it to their advantage, as they have done with assert for so many years. It seems to me that assertions, as well as most tools (not just with C++) do indeed "rely on a lot of programmer discipline to be properly used." Such is life.

That said, there is indeed a much better way to define SAFE using templates, that does not suffer from the double-evaluation problem but maintains its expressiveness and usefulness. (Thanks to Mark Lee for being the first to point it out to me.)

#ifdef _DEBUG
   template<class T>
   inline T __safe(T expr,
      LPCSTR file, int line,
      LPCSTR err)
   {
       if(!expr)
           GioAssertFailedLine(file,
              line, err, "SAFE");
       return expr;
   }
   #define SAFE(expr)  __safe(expr, __FILE__, __LINE__, #expr)
#else
       #define SAFE(expr) (expr)
#endif

Why did it escape me? I played a lot with references (operator&) and const in my few template function attempts, if I well remember, but all led to some compiler errors and ambiguities (maybe because in those days I was using Visual C++ 4.2?), and I missed such a simple solution! I do deserve some criticism, indeed!

Regarding Andrei's seven points:

1. My code was for MFC, as the title said, and in MFC ASSERT still uses THIS_FILE (see AFX.H, line 223, in MFC 6.0). In the article, I suggested a definition of SAFE for users of the Standard C library that uses __FILE__ (page 38). My sample code will not compile outside MFC. (It uses CTime, CString, etc.) In MFC, THIS_FILE has its own advantages (mainly reduced code size, I think).

2. Andrei is right, his suggested solution would be better. I slipped in this macro at the last moment, and I should have tested it more first. I plead guilty.

3. I never considered this a problem because I knew it and never used ASSERT_LEN with simple pointers. But again, Andrei is right. And I must confess I missed Bobby Schimdt's article. (Sorry, Bobby.)

4 ARRAYSIZE was in my header file, is on the ftp site, was edited away, and I did notice but didn't mind. It is not my invention, I have seen it in too many places to mention. Though this does not mean it's a good macro, I imagine most programmers know about it (and maybe even admit they used it).

5. I have quickly increased it to 4,096, before my boss finds out.

6. I did not want to provide a library, but just humble ideas, with some code to help. Anybody who thought there were good ideas in the article (and some did), can use the method he wants to set the filename. The method to pick the file name is beside the point, in my opinion, and I kept it simple (fixed) because of this.

7. Here I totally agree with Andrei. I should have used MFC's TRY/CATCH instead of try/catch.

Thanks to Andrei for his suggestions, ideas and criticism, and my renewed compliments to him for his April 1999 CUJ article. I thought it was excellent (and I told him), though I haven't had a chance to look for any bugs yet.

With Respect

Giovanni Bavestrelli

P.S. Mama, why did you let me write an article on macros in 1999?

Like Bavestrelli, I don't think his SAFE macro is as "evil" as Alexandrescu makes it out to be. And some people still understand macros better than templates, which is important in a debugging tool. But if the article and subsequent feedback has brought a safer implementation to light, so much the better. mb