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 Mr. Plauger:In the October 1997 issue, Kevin Van Horn describes how to use templates to get compile-time assertions ("Compile-Time Assertions in C++''). I have been using a similar technique for a while, and would like to share with your readers my technique. It is more flexible and intuitive than the template approach. The idea is similar to Van Horn's, only I use a typedef:
#define CTassert(x) \ typedef int _CTassert_dummy[1 / !(x)]Now you can use CTassert just like a regular assert:
CTassert(sizeof(int) == 4);If the argument to CTassert is zero, a division by zero will result in the evaluation of the array dimension of _CTassert_dummy, which should cause a compiler diagnostic. Any other value will generate:
typedef int _CTassert_dummy[1];There are several advantages to this approach versus the template one: First, it does not actually declare a variable, which means you don't have to come up with unique variable names. This makes it perfect for use in include files. Second, it can be used in environments where templates cannot for example, in C (not C++) source, on compilers with not-so-complete (or non-existent) template support, or by your colleagues who are unfamiliar with templates. Third, because it uses a macro define, it is more likely that the compiler error will point to the line of the CTassert, not to some other file where the template is defined.
Although I have used this for a while now and never had any problems, I do have doubts as to its complete portability. For example, do all compilers allow the redefinition of typedefs (if they are identical)? I have never seen one that didn't, but I suspect there are compilers out there that wouldn't like it. (Any comments on exactly what the C and C++ standards have to say on this?) Using an extern instead of a typedef can achieve similar results, but this may be fraught with its own problems.
Feedback is appreciated!
E. Fletcher Dunn
Terminal Reality
fletch@terminalreality.comSo-called "benign'' redefinition of typedefs is specifically required by the C Standard, and by the draft C++ Standard. Your approach has the advantages you cite. Indeed, I have for years used a simplified version of it: no need to contrive a divide by zero, since an array of zero size is equally invalid. There are other tricks you can perform with templates that are well beyond boolean testing, however pjp
Dear Editors,
In your September 1997 issue, P.J. Plauger's article 'Standard C/C++: The Header <limits>'' displayed the following syntax in the code excerpt:
static _Ty epsilon() throw()and other similar function declarations. Although I've been a C++ programmer for about three years, I've never seen this form of declaration. What does the throw() mean? Is it a macro? I would appreciate it if you'd clarify this form of declaration.
Thanks in advance,
Amir Hermelin
The throw() in the function declaration is what is known as an exception specification. Our Consulting Editor Chuck Allison explains C++ exception specifications in last month's issue. See his article, "Error Handling with C++ Exceptions, Part 2,'' on page 57. Here's a brief excerpt from that article:
The definition:
void f() throw() { // Whatever }disallows any exceptions while f is executing; that is, it is equivalent to:
void f() { try { // Whatever } catch(...) { unexpected(); } }Hope this helps. mb
Dear Mr. Plauger,
In "Creating a Guestbook with ISAPI'' (October 1997), Don Gaspar discusses communications between input forms and ISAPI extensions. I also have some experience with this and would like to share some hints with readers who are interested.
First, quotation marks should never be used in input forms for passing parameters. Each variable should look something like this:
< INPUT TYPE = text NAME = first_name ...>because PARSE map will not resolve a member function if its variable name does not match a name inside the input form. So, if you pass 30 to 40 parameters (common for real business applications) and accidentally print in the input form:
< INPUT TYPE = text NAME ="first_name " ...>with one extra blank inside quotations marks, then the variable name will not match the input parameters. You can see where that would go...
Second, for calling an ISAPI extension, actually we can use a statement of the form:
<FORM ACTION = "/scripts/Guestbook.dll" ... >assuming you have moved your extension into the scripts folder. While you are debugging your application there is an easier way to call extensions. Go to Internet Service Manager and by using "Property of WWW service'' declare an alias:
C:\Guestbook\Debug /GuestbookIn that case you can call:
<FORM ACTION = "/Guestbook/Guestbook.dll" ... >which means you have linked the input form with the up-to-date version of your application located internally in an original C++ folder and do not need to copy Guestbook.dll from C:\Guestbook\Debug to C:\InetPub\WWWroot\scripts after every recompile.
Third, you need to stop and start WWW service every time you want to replace your ISAPI extension with the new version. It is pretty boring especially during debugging time. To avoid this, go to the registry editor and change the value for the registry key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet \Services\W3SVC\Parameters\CacheExtensionsfrom 1 to 0. This stops caching extensions in memory and allows them to reload every time they are called.
Lev Baran
lbaran@bondnetwork.comThanks for the tip. I'm happy to see that ISAPI programming isn't nearly as complicated as "real'' programming in C or C++. pjp
Dear Mr. Plauger,
I have a program written in Pascal that needs to call a function written in C. I am having difficulty setting up the external reference in the Pascal code so I thought that I might convert the Pascal to C. The schedule for this project does not allow me to rewrite the Pascal program from scratch so I would like to find a Pascal to C translator. Do you know of a good Pascal to C translator which would produce a readable C output and runs on a PC?
Thanks,
Steve Goldstein
Litton Applied TechnologyI wrote one years ago, for my company Whitesmiths, Ltd., but I don't think it's commercially available any more. I believe Project Gnu has a Pascal to C translator, and I've seen ads for a commercial one. Good luck. pjp
A version of Gnu P2C is available at ftp://csvax.cs.caltech.edu/ mb
Dear CUJ:
In the sidebar "Multithreading Problems in Windows NT'' in the article "Porting Server Applications from UNIX to Windows NT (CUJ, October 1997), Thomas Becker indicates that you cannot use MFC to write a Windows NT service, since there is no global CWinApp object. I've written several mutithreaded services that use MFC, and encountered this problem very quickly. After digging through the MFC source code, I discovered that there is very little that the a CWinApp object really needs in order to be considered "initialized'' for the purposes of using AfxBeginThread, or other MFC service classes. See Listing 1.
This solution worked very well under VC++ v4.2 on NT v3.51; I expect that it would work under VC++ v5.0 and NT v4.x as well.
Sam Robb
WiseWire Corporation
srobb@wisewire.comYour letter just goes to show that we should take it with a grain of salt when someone says something "cannot be done.'' Thanks for enlightening us. mb
Dear PJP,
In the October 97 CUJ, 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.
What happens next depends on the calling program and the type of data. If each record is independent, and the processing sequence doesn't matter, perhaps it is okay to shuffle the problem record to a suspense holding file, and continue on the next record. Or perhaps the program should die gracefully, and permit others to proceed.
A further refinement if blocks of, say, 200 records per DB commit are usually processed (for efficiency), when a restart occurs, the block size might be lowered to 1 temporarily until the system is safely past the problem area. This way, a particular offending transaction can be located and isolated.
The necessity for a "restart-point'' file (so far as I know, I invented this name) became painfully apparent when I was developing a program that took several days per run, and involved much new code with a bug deep inside a CPU-bound loop (but that's another story).
As a reference, I cite the classic quote "Those who do not learn from history are doomed to repeat it.''
Saul J Rosenberg
rosenberg@objdev.org
Object Developers Group, Inc (ODG)
Dear Sir,
I read with interest the article by J. David Wendel on "Triangular Tiling with Real-Time Searches'' (CUJ, November 1997) as, in the past, I've had to solve a similar problem. In my case, I wanted the surface generated by the triangulation. David Wendel's algorithm, while optimal in the sense that the triangles found are as near equilateral as possible, is not optimal in terms of CPU time and storage space. I thought that David Wendel and other readers might find an alternative algorithm of interest.
The algorithm that I used was based on a paper that I found in the Computer Journal, by P.J. Green and R. Sibson (Vol. 21, No. 2, 1978, pp. 168-173, "Computing Dirichlet tessellations in the plane''). The algorithm is roughly linear in terms of memory usage and N log(N) in terms of CPU time, as compared with N squared. It also results in an optimal triangulation, where the triangles are as near equilateral as possible. I downloaded and compiled David Wendel's code and compared the results with my implementation of Green and Sibson's algorithm running on my MS-DOS based PC.
Wendel Green & Sibson 60 points 25 0.17 1000 points Not possible 3I believe that the results of the triangulation from Green & Sibson's algorithm could also improve the time to search for a triangle.Yours faithfully,
Andrew L. Jackson
Dear Editor:
I would like to thank Kenneth Ngai for his August 1997 article. "A Template for Reference Counting.'' Thanks to CUJ too for publishing this useful article. I have seen many articles on reference-counted objects, but none was implemented as neatly as Kenneth showed in his article. With his template-based implementation one can derive from the template class and forget about reference counting altogether. It very neatly encapsulates the reference-counting machinery in the base class.
However, the article has some flaws, or omissions rather. A relatively minor omission is the fact that classes need not derive publicly from CHReferenceCount. Public inheritance needlessly exposes the Reset method to client objects. A private inheritance appears to be more appropriate here. The second, and to my mind a major omission, is the fact that all derived classes must declare and implement a copy constructor and an assignment operator (both delegating appropriate parts of their work to the base class) for the reference-counting mechanism to work properly.
Ahsan J. Sharafuddin
Cleveland, Ohio
Ahsan.Sharafuddin@Penske.com[Opinions expressed are my own, not my employer's.]
Our greatest asset as a magazine is the writers we attract. Our second greatest asset is the readers we stimulate to share their own experiences. Thanks to all for the suggestions and constructive criticism. pjp