Back to TOC 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 P.J. Plauger,

I know you are busy but I wanted an opinion of someone whose views on C++ I respect. Two functions that behave similarly:

void foo(int *myVal);
void foo2(int & myVal);

The user writes:

foo(&anInt);
foo2(anInt);

To my way of thinking, foo1 is clearer to the reader of the code and by inference, better. The fact that you are passing by reference is unambiguous. It seems to me the pass by reference syntax of foo2 is a mistake in the C++ design. What are your feelings?

Have you discussed this in a previous issue of C/C++ Users Journal? If so point me in the right direction. I have decided that I must have the CD of previous issues. This magazine has become indispensable! I am looking forward to the rest of the Locales series. Keep up the good work.

Andre R. Oughton
PictureTel Corporation
aoughton@pictel.com

I too prefer the overtness of passing an explicit pointer rather than a reference. Nevertheless, there are times when references are really necessary. For example, you cannot declare a sensible assigning operator, such as operator+=, unless you can make the left operand a reference to the object to be altered. Const references also let the implementor of a function optimize the passing of large objects. — pjp


Dear CUJ,

Pointers and references. Enjoy.

The difference between a pointer and a reference is like duck and glass.

A duck quacks like a duck and flies like a duck. Therefore, it is a duck.

Glass quacks like a duck and does not fly. Therefore, it is not a duck.

Think of a thick glass pen fully surrounding zero or more ducks. Fly all they want but cannot quack the glass.

Now, a duck outside the glass may not get in, but can fly from pen to pen to pen.

Please ship all ducks in pens.

Denton E. Karle

It's a little scary to think someone has to go this far to get a grip on pointers and references. But for the sake of those whose brains really work this way, I'm inclined to correct a few misconceptions buried in all your prose:

I hope this clears things up a little. I feel better, I think. — mb


Dear Editor:

C/C++ Users Journal is an excellent magazine. However, many nowaday developers use tools — either Visual C++, or Borland C++ Builder, or some other visual IDEs. So if you can include the Visual C/C++ Development column in your magazine (say, talking about C++ Builder components), it would be much better. Best wishes to your magazine. Thanks.

Regards,

Richard Zhu
OHS System, HR&OGC
rzhu@ford.com

Your suggestion has some merit, but it conflicts with CUJ's goal of being as vendor and platform neutral as possible. We aim to provide content balanced between universal, timeless information (e.g., the importance of information hiding) and specific, timely information. In the latter category, for example, we've run articles on ISAPI, a Microsoft-specific API. And we won't avoid vendor- or platform-specific articles in the future if we judge them too important to pass up. But a vendor-specific column would just be going too far.

We know that no single article can be useful to all readers. But we hope that if an article isn't immediately useful to all, it will at least be interesting and instructive to most. A column on Visual C++ would be unquestionably useful to a select portion of our readership, and boring to all the rest. The gnarly details of navigating an IDE, or trying to get a Wizard to do what you want, have little to do with programming in general, and they quickly go out of date.

On the other hand, I think there are a lot of interesting topics having to do with developing software in a visual environment, which remain largely untapped. How to leverage visual components in performing regression tests is one that immediately springs to mind. Very brief articles — one-pagers or less — with tips on how to work around quirks of specific visual environments would also be most welcome. So if anyone reading this has some ideas for articles, please visit our website (www.cuj.com) and get a copy of our Author Guidelines.

Thanks for writing.

— mb


Dear CUJ,

In response to the letter by John Gammel, published in CUJ, December 1997, on DOS extenders:

The DOS/4GW extender provided with Watcom is not the professional version. It is a subset of the commercial product provided by Tenberry and thus has some limitations, including a 32 MB memory limitation. This limitation should not be present in DOS/4GW Professional.

As for Virtual Memory Management on Windows 95, NT, or any other multitasking OS, one of the aspects of memory management is avoiding resource starvation. Running a memory-intensive process such as the one you describe will cause numerous page faults (accesses to virtual memory) simply because the memory manager places an upper bound on the amount of memory available to a single process. To ensure that your process receives the maximum resource utilization, consider relaxing any ceiling constraints on the memory manager (if such options are available), or running on a single-user OS.

Best of luck!

Eli Ofenstein
ejo@vsurf.net


Dear CUJ,

You published a letter by John Gammel in December 1997 about only accessing physical memory (128 Mb of it). I didn't look at the program, but things to try are:

1) disable swapping (this is an option)

2) look at the algorithm

There are algorithms that are friendlier to virtual memory. For example, when processing arrays such as:

double array[BIG][BIG];

there is a big difference if you start doing:

for(i = 0; i < BIG; i++)
    for(j = 0; j < BIG; j++)
        process array[i][j]

against the same loop with the body:

process array[j][i]

The first example will (generally) process a page of VM at a time, the second will process one entry on a page. (i.e. each entry may cause a swap).

I agree with PJ, that if you're doing serious computing on an Intel platform, you may be advised to do it on Linux or FreeBSD.

Also, how much other stuff are you doing during this computation? If the system is idle other than this program, almost all the physical RAM should be dedicated to the program. (I wouldn't be surprised if it's not on MS operating systems.)

Marty Leisner
leisner@sdsp.mc.xerox.com

The essence of virtual-memory support is making a relatively small physical memory look like a much larger one. The manager can achieve significant improvements in performance if it guesses right about the pattern of page accesses it must satisfy. The program itself can achieve significant improvements if it avoids patterns known to be difficult to deal with. In either case, you the programmer must rely on documentation, lore, and experimentation to help both the manager and the program. I appreciate our readers sharing some of that lore. — pjp


Dear PJP,

In the January 1998 We Have Mail, Saul Rosenberg made a few comments concerning my article, "Segmenting Large Database Transactions in C" (October, 1997). I was incapacitated when the letter was received, but am now able to respond to a few of his comments. To quote Mr. Rosenberg:

"In the October 1897 edition, the article by Stan Milam on 'Segmenting Large Database Transactions in C' was devoted to discussing Checkpoint / Restart, and listed a sample implementation. The article contained some excellent points and procedures but also reminded me somewhat of distributing plans for a V8 dragster without including brakes :-) A caution is necessary:

"As written, the Restart function does not check if it is attempting to restart a transaction that may have caused a previous abort/restart. Without this necessary check, once the program encounters a transaction/situation that is a hard, repeatable failure, the program will endlessly yo-yo, aborting and restarting at the same point. This yo-yo wastes system resources, blocking other programs from running. It can also mislead system observers who see regular CPU usage into thinking that meaningful work is being performed while in fact it is not.

"The recommendation is to modify the Restart routine to write info to disk each time a restart occurs. (This restart-point file parallels the checkpoint file written by the Checkpoint routine.) The restart file contains a pushdown array (or a circular array) of size N. Each time a Restart occurs, push down the current record number (or transaction key or other unique value describing the current position). If the last N restarts are at the same record number, the Restart routine returns a code indicating a repeatable failure on that record."

Yes, checkpoint/restart can be a double-edged sword. If one is not careful designing how a transaction is processed it can be a yo-yo, but I have never encountered such a situation. The reason is that every environment in which I have worked has used a sophisticated logging mechanism. When programs abnormally terminate they are designed to log detailed messages, including all relevant data and error messages returned from the database server. Before restarting a program the cause is thoroughly investigated and resolved, thus avoiding the yo-yo syndrome. To put it another way, when your favorite C/C++ compiler gives error messages, do you simply start the compiler again and hope for better results, or do you resolve the problems with the code, then invoke the compiler?

For the reasons already stated, I do not believe any other modifications are necessary. However, if Mr. Rosenberg has some special situation where he feels this is necessary, then why change the checkpoint package code? He could just save the pushdown array in the save area when he checkpoints, then do the checking when a program restart is detected. No changes to the checkpoint function is required. If he really wants to change the package, he can download the code and do what he wants with it (and have fun too) :-).

Mr. Rosenberg also suggests the possibility of reducing block size to one when a restart occurs, until the system is safely past the problem area. This would allow the offending transaction to be isolated. I would like to reiterate, however: if a detailed message is logged you already know exactly where the problem occurred. Once investigated and resolved the program can be restarted and sent on its merry way. However, if deemed necessary to do so, logic can be implemented in the program to lower the checkpoint threshold when a program restart is detected.

I would like to thank Mr. Rosenberg for his comments. I have been using checkpoint/restart mechanisms for over 10 years now and learned a lot about using them. Checkpoint/restart is just one of the tools I have accumulated in my toolbox, and detailed message logging is another. Used together they are quite effective dealing with large transactions with updates numbering in the millions. I had 3,000 words in which to describe checkpoint/restart, and I tried to mention the most prominent caveats as best I could in that confining space. My other C/SQL tools with message logging would easily fill another article. I guess it is time to get started on it!

Stan Milam
milam@metronet.com


Dear CUJ and PJ:

I experienced a strange thing. It was not detected when using Borland C++ 5.0 and CodeGuard, but it was detected by Visual C++ 5.0 (perhaps due to app differences, since the Visual C++ app reads a lot more data). The problem was that the VC apps using my persistent streaming scheme (ported from Borland and modified to use only the STL) would exit the debugger with a single warning:

Detected memory leaks!
Dumping objects ->
{25} normal block at 0x00FA0A30,
  33 bytes long.
Data: < .         >
  00 2E 00 CD CD CD CD CD CD ...

After tracking down all allocations of 33 bytes to the one at the given address, I found the lines of code that were causing the problem. The call stack went something like this:

// line 265:
operator new(unsigned int 33))
// line 25:
std::_Allocate(int 33, char * 0x0)
std::allocator<char>::allocate( ... )
std::basic_string<char, ... >::
  _Copy(unsigned int 1...)
std::basic_string<char, ... >::
  _Grow(unsigned int 1...)
::assign( ... )
::assign( ... )
std::basic_string<char, ... >( ... )
SpecInterface::SpecInterface( ... )
  [my code]

There were a large number of _malloc_dbg allocations of size 33. The one complained about was the last one before breaking back into my code, at the marked lines below.

It seems to me that the reference counting scheme in the library might be bad, and, in some circumstances, the reference-count might end up off by one. Why it should occur here in the creation of a temporary object, I do not know. My fix was to note that nowhere do I use the path parameter. So I commented it out.

Next topic. Which is more readable, Listing 1 or Listing 2?

I must confess that Listing 1 is only a little easier to read. I'm sure you're familiar with this code, PJ. It's about the worst example of readability I've ever seen. I must read it character-by-character to understand it. If it were pretty-printed, I could grok it by gestalt. Why is almost all library source code so incredibly hard to read? Why are there no comments in library source code? Do they strip them out before they ship the code?

One other readability problem with Microsoft code is those dang warts, the worst of which is the m_ (member variable) wart. If you can't tell the nature of a variable from context, your code will cause you (and your clients) problems.

Borland code and Microsoft code are both messy-looking. If I were generating code, I'd take the time once to make sure the code looks spotless, professional, and highly readable. I'd use #pragmas to demarcate wizard/expert code, instead of linty constructs such as:

//{{AFX_WIZARD_BEGIN

Better still, I'd reduce the reliance on such breakable things, and make the wizard better able to collect unmarked information from the code. I've broken wizard code many times by accident; now I use it only in an experimental project, and copy the generated code to the project I really want to work on. I use my editor's features to pretty-print it as I type.

I had a Borland AppWizard app that Windows 95 would flag as a corrupt executable. As a result, I no longer support both compilers, except in library code. I find that running code through both compilers finds more stuff to clean up. But I'm not too happy with either compiler.

Sincerely,

Chris Ahlstrom
major league software
ahlstroc@compuserve.com

I'll take the blame for the storage leak. It is a known bug in the VC++ library that assigning a small string to a large one sometimes causes a storage overwrite. We have been regrettably slow in getting the word out on this one, and a patch to go with it.

I'll take a little blame for the code you find hard to read. It is religiously indented to show control structure, but pretty sparing in its use of white space. The funny names are largely mandated by the C++ Standard, to avoid collisions with macros you may define in the code you write. And yes, some informative comments are removed, partly for contractual reasons. Having said all that, however, I confess that Microsoft has already requested that I make the library headers more readable in future. I hope to do so soon. — pjp