We Have Mail


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

I'm sure you'll get a few annoyed responses from Java programmers about your editorial in the October 2002 edition of C/C++ Users Journal complaining about the Java code sample for a word count program being unfairly complex in comparison to the C++ version. It is unfairly complex, but for a more fundamental reason -- the very reason your column was about, namely failure to use the proper high-level abstraction.

The example you gave used the functionality of a collection to map one type of value (word strings) to another (integers) in a one-to-one fashion, which completely overlooks the idea of simply creating a class that contains a string and an integer for the count -- after all, the entire point of classes is to associate related data together. (The count is really an attribute of the word.)

In the Java example, you would define a WordCount class to contain a String (key) and integer (count) and map the comparison methods that the collections use (such as hashCode()) to the String (so a WordCount object can be compared to a String). The code would then look more like this:

{
  WordCount wc = myMap.get(key);
  if (null == wc) {
    wc = new WordCount(key);
    myMap.put(wc, wc);
  }
  wc.count++;
}
If the class used for myMap were extended to automatically allocate and store a new WordCount if its key wasn't found (like the [ ] operator obviously was in the C++ example), you could eliminate the explicit allocation:

{
  WordCount wc = myMap.get(key);
  wc.count++;
}
Or even:

myMap.get(key).count++
which finally looks a lot more like the C++ code.

It should probably be done that way for the C++ version too. It doesn't change much:

myMap[key].count++;
Once you have a WordCount class, the objects can be treated more consistently and flexibly -- for example, you could add a field to keep track of how often the word was found at the beginning of a sentence without using a separate collection.

I've always thought that one of the biggest problems with operator overloading is the distraction it causes. Being able to apply it as a powerful solution to one problem can make you overlook a better and more powerful solution -- in this case, the idea of object-oriented programming (which is kind of the point of using C++ in the first place).

Of course, whether you use C++ or Java, the Lisp programmers are still sniggering...

John Bayko

Thanks for this enlightening response. I was trying my best, however, to compare apples to apples. In C++ there is no need to create any classes or extra scaffolding whatsoever. While C++ does support objects, the goal of C++ is to write effective code for the problem at hand, whether it is procedural, object-oriented, generic, or a mixture thereof. Part of my discomfort with Java is that it forces a particular paradigm on me, even when it isn't needed. The total program will be more concise in C++ (especially the I/O!). I appreciate that one doesn't do things the same way in different languages, but if one just wants to count a sequence of globs of non-white characters, one should be able to do it without creating unnecessary abstractions. As a Lisper myself, I'm not sniggering. -- cda


Dear CUJ,

This is only the second time I have bought CUJ, and your September issue features on .NET and Managed C++ were exactly what I was looking for -- my compliments for being so timely and informative. As I spend a good part of the year in Europe, I can't become a subscriber, but I sure won't miss a single issue henceforth!

Best regards,
Andrew Bertallot

We're glad you liked our .NET supplement. FYI: CUJ subscriptions are available in Europe if you're interested. Visit www.cuj.com for more on international subscriptions. --jc


Hello,

I read Cristian Vlasceanu's article ("Debugging Memory Errors with Custom Allocators") from April 2002 and was excited enough by the prospect of protecting memory from corruption to give it a try under a Win32 environment.

There is one rather large catch.

In a normal situation, a custom allocator would sub-allocate its pages, and therefore over/under running an array (etc.) would actually run into adjacent memory -- corrupting an adjacent object.

Giving each allocation sole possession of a page, as is shown in the article, removes this occurrence -- thus invalidating the purpose of the exercise.

Alternately, if you retain your custom allocator's sub-allocation scheme, changing protection of even one byte using VirtualProtect, the entire page gets that protection -- very likely including other unrelated objects that just happen to be on the same page (including your custom allocator's data structures). This would cause exceptions when other objects try to modify their own data in a valid way.

If the other objects on the same page are using the same remove-protection/modify-storage/restore-protection scheme, they will remove the protection for the entire page and therefore would not cause an exception when they are corrupting memory, unless that memory is on another page.

Happily there is a way around this. Unhappily it is not trivial and does have a rather large impact on access speed to "protected" pages [1].

The solution is as follows [2]: set up a "Structured Exception Handler" [3] around some suitable function -- try main or WinMain. In your exception handler (after ensuring that this is the correct exception and the memory is being correctly accessed), remove the protection from the page, set the "caller" to single-step mode (on x86: context->EFlags |= 0x100), and return EXCEPTION_CONTINUE_SEARCH [4]. You will then get a single-step exception, whereby you restore the protection and again return EXCEPTION_CONTINUE_SEARCH [5].

With this scheme, you can allow to fail (by returning EXCEPTION_EXECUTE_HANDLER) invalid accesses and other exceptions.

Bruce Mellows

[1] The overhead for the exception was about 1/60th of a second; the overhead for calling VirtualProtect twice was about 1/100th of a second, resulting in a total speed hit of well over 2,000 times compared to unprotected access (Pentium III 650 Mhz on Windows 2000).

[2] Credit for understanding "Structured Exception Handling" belongs to Jeremy Gordon's articles at <www.GoDevTool.com>.

[3] I personally do not use the __try/__except syntax in the Microsoft documentation -- the compiler I use does not support it (and I prefer to stick to standard syntax in my C/C++), so I use a small (15 instructions) assembler function.

[4] I do not understand why I have to return this rather than EXCEPTION_CONTINUE_EXECUTION.

[5] You do not have to reset single stepping -- this is already done by the OS.

Bruce,

Thanks for your diligent comments and insights regarding the behavior of VirtualProtect.

On Unix (the platform I use on a daily basis), the manual page for mprotect tells you loud and clear that the memory is expected to be page aligned. In my implementation, using pvalloc takes care of this requirement.

I ported my code to Windows mostly as an exercise for writing a STL allocator targeting as many compilers/STL implementations as I had available at the time.

I must say I was surprised that VirtualProtect did not impose any requirement on the memory to be aligned at a page boundary. I did not pay much attention, ouch.

Now when I read your comments, it all makes sense. So, thanks again.

As for your strong dismissal of my exercise, I can answer by telling you that I actually caught a live bug by using the technique described in the paper.

Secondly, I quote from my article: "you must be aware that the custom allocator may change the behavior of the bug that you are trying to unveil. On Unix platforms, for example, the mprotect call works only if the memory is page-aligned. To allocate memory aligned to a page boundary, you can use (as shown in the companion code) either the mmap or pvalloc system calls. [And here's the punch line:] A side effect may be that the memory-corrupting bug magically disappears or manifests itself in a different way."

So there you have it. I'm very nicely covered by the small print.

Regards,
Cristian Vlasceanu


Dear Mr. Allison,

Thanks for the editorial in the November 2002 issue. I purchased CUJ yesterday at a computer bookstore where the shelf space devoted to C# books has now exceeded that devoted to C++ books. Combine that with the paucity of C++ jobs offered in my area and one might think the language was dying. Your editorial was a welcome reminder that this isn't so, and the rest of the issue proves C++ is vibrantly alive and still evolving. Keep up the good work!

Sincerely yours,
Yves Meynard

Thanks for your letter. My research shows that the C++ community is still very strong. For example, it remains the most sought-after programming language skill in Silicon Valley. A friend of mine who sells both C++ and Java products has noticed that C++ is holding steady while the demand for Java is diminishing. The standards community is currently considering a number of proposals that will strengthen the library and further stabilize the language. Add to this the fact that C and C++ are the most versatile languages for multi-language development; the outlook is favorable indeed. -- cda