Departments


We Have Mail


Dear Dr. Plauger,

I must first congratulate the CUJ for being a source of ideas, questions, and answers that have been especially useful in the years leading up to standardization of the C language. I have found your recent book "The Standard C Library" very useful in exposing some of the traps in practical library construction with C.

I have a question that deals with a part of the language of which I am a somewhat unsure. The following paragraphs contain the question and some justification for not treating the question as C esoterica.

While leafing through a text on Standard C I was surprised to find in a section on incomplete types that there seemed to be an obligation to complete an incomplete type in the same scope as that type's introduction. Is this the case for struct and union types whose sole use is in the definition of a pointer struct tag*?

The reason behind this question is that a pointer to an incomplete struct type being a complete type implements an opaque private type very nicely. All compilers I have used are quite happy with pointers to incomplete struct types which are never completed and give useful diagnostics when trying to dereference such pointers or assign between pointers of different incomplete struct types.

In various articles void* pointers have been used to implement opaque types — unfortunately, while void* pointers hide the structure of the storage they reference, all void* pointers are assignment compatible with all other non function pointers, giving no type safety at all (e.g., to a compiler stdin would be an acceptable LIST object).

I have found the practical rewards of not exporting implementation details have included: one header file serving a number of tailored implementations stored in separate libraries that can be bound at link time and the ability to alter the representation of an implementation in response to changes in storage requirements, etc. There are costs in efficiency but these can usually be recouped in the tuning phase when the implementation can be exported and a selection of access functions converted to macros inlined.

Yours sincerely

J Sainsbury
1 Meranti St
Crestmead Q4132
Australia
+61 7 200 9007

You don't necessarily have to complete an incomplete type. If you do, however, you must do so in the same scope. pjp

Dear Mr. Williams,

A few days ago I read your article "A Console Stream Class for Borland C++" in The C Users Journal.

The problem you commented on in it is interesting to me because a couple of months ago I had to solve the same problem — using both stream I/O and Borland's console I/O. After reading through your article I found out that I had used the same approach but slightly different implementation.

My implementation of a console-oriented stream-output class is shown in Listing 1. [Listings omitted due to lack of room. You can get them with the monthly code disk for this issue — pjp] I have not defined the do_sputn method but defined the constructor to make the ConioBuffer class unbuffered. In this case, the overflow method called for each character. The defined method overflow prints the character using console-oriented function putch.

Listing 2 [also omitted — pjp] shows the approach I have used to interface the stream output to a window class.

I would be grateful to have your comments about this as well as about other problems concerning programming in C++. In general, it is difficult for me to have more information and to discuss such problems in this country.

Yours sincerely,

Stefan Ganev (Mr)
PO Box 560,
Bourgas 8001,
Bulgaria

Al Williams responds:

Thanks for your interest in the console stream class. You are correctas usual there is more than one way to solve this problem. Your method is perfectly correct. There is at least one more, equally valid approach. You don't have to define an overflow method. The default method simply calls do_sputn for each character.

I elected to write both functions so I could illustrate their use. In practice, many streams do need bufferingI wanted to show the most illustrative case.

You might be interested to know, more about the console stream class will appear in a future C Users Journal (probably in the November issue). This time, I'll add manipulators to the class so you can write things like: cout< <go_xy(1,1)< "Hello";.

Again, thanks for your interest, and good luck in your programming endeavors.

Regards,

Al Williams
310 Ivy Glen Court
League City, TX 77573

Dear Mr. Plauger,

I'm confused by the description of va_list in your book The Standard C Library.

Under arg.h (pg. 210) there is a warning about using the same va_list ap in two called functions, but under .h (pg. 259), the example for vfprintf() does exactly that.

Is the example in error? Could you provide an example of when special care for the va_list ap is required?

Please feel free to use my question for the C Users' Journal, if you feel it appropriate.

Thank you for your help,

Brett Wuth
3510 44St SW
Calgary
Alberta
T3E 3R9
CANADA
Tel: +1 403 242-0848

In this particular case, the value of ap is reinitialized by invoking the macro va_start between calls. That's why it works properly. Otherwise, you're correct to be concerned. pjp

Dear Mr. Plauger,

I hope this is your prefered e-mail address for this type of question. In the test program TSTDIO2.c from your book, The Standard C Library, I think I have found a bug.

Is it not necessary to add the line as I have, to make sure that the temporary file is closed, and thus renamable?

Thanks for your help,

Brett Wuth
3510 44St SW
Calgary
Alberta
T3E 3R9
CANADA
Tel: +1 403 242-0848

You're right in the general case. It turns out that UNIX and DOS let you get away with this, which explains my blind spot. Thanks for the fix (included on the monthly code disk for this issue). pjp

Dear Mr. Plauger,

I enjoyed reading the article by William Smith in the May 1992 issue entitled "Number Crunching in C." I would like to make three comments on the article.

First, the main technique that Mr. Smith uses to speed up his matrix inversion (storing pointers to rows of the matrix to allow swapping of pointers instead of swapping of actual rows) can be implemented in many languages. In FORTRAN, the rows of matrix coefficients could be stored in a singly-dimensioned vector, with an integer vector storing offsets (pointers) to the coefficient data.

Secondly, the timing ratios from the examples presented would have been more informative if the runtimes for matrices of dimension nxn requiring 0 pivots and n pivots had been compared. This would give a better indication of the resources used by swapping rows. I also would have like to have seen timing ratios for matrices up 100X100.

Thirdly, it would be interesting to compare run times for a FORTRAN program using the C-pointer technique to the C program.

Sincerely,

Ron Bondy
Simulation Sciences Inc.
3033 South Parker Road
4th Floor
Aurora, CO 80014

William Smith responds:

Thank you for your comments. The difference in speed between the FORTRAN-style C and the optimized C is mainly due to the replacement of many individual element exchanges with a single pointer exchange. This reduces the amount of physical memory that must be copied. As the matrix size increases this difference is significant. I have found that I cannot count on a compiler to do this optimization. Elimination of indirection is a minor optimization compared to the elimination of element exchange operations. I try to depend more on the optimizer between my ears than the optimization capabilities of the particular compiler I happen to be using. The article was meant to inspire and encourage the use of C-specific language features when translating to or improving numerical analysis code in C.

Sincerely,

W. Smith
Engineering Manager
Montana Software
P.O. Box 663
Bozeman, MT 59771-0663
(406) 586-2984

Dear Mr. Plauger,

I am writing in response to letters in the March and July CUJ, regarding single-character keyboard entry on a VAX. I too experienced this problem. After trying getch and several functions in the curses library, I finally discovered the SMG$ library functions. The code fragments below may help someone avoid this pain in the future.

To initialize the keyboard,

#include <smgdef.h>

int kb_id;

SMG$CREATE_VIRTUAL_KEYBOARD (&kb_id);
And to read one character,

int getraw(void)
   {
   short termcode;

   SMG$READ_KEYSTROKE(&kb_id, &termcode)
   return (termcode);
   }
This function returns special keys (such as the cursor keys, PgUp, Delete, etc.) as values 256 and above. This is a terminal-independent way of reading special keys on a VAX.

In my personal opinion, DEC doesn't believe in C. Their C libraries are woefully inadequate. One indicator of this is the fact that it should be possible to read a single character using curses functions, but it is not possible (as far as I have determined). Many other important operating-system functions cannot be called directly from C programs; the programmer is forced to wave wands, sacrifice small animals, and convert arguments and return values into arcane structures, in order to placate the gods of VMS. Luckily for us, DEC documentation devotes most of a full page to explaining the necessary conversions.

I have enclosed the source code for a program I wrote for VMS, called BROW. (Available on the code disk only.) It is a file-browser modeled after Vernon D. Buerg's very popular LIST utility. BROW uses the SMG$ library to read the keyboard and paint the screen. I am releasing it to CUJ for publication or any other use.

Sincerely,

Robert D. Bybee
5011 Brougham Court
Stone Mountain, GA 30087
(404) 498-3556

I don't know whether DEC believes in C, but I know they believe in a variety of programming languages. I too have found myself doing extra work to convince VMS to do things from a C point of view. pjp

Dear PJP:

On page 59 inn the June issue of The C Users Journal you commented that the X3J11 considered defining a set of screen-control functions. I happen to think that such a thing is an excellent idea. If one does not try to support everybody with such a set of functions I think it can be carried off successfully.

I would propose to support those who have a need to maintain the same program on different machines with roughly the same interface for all. Since terminals differ a lot in how they handle user interaction I would propose to use forms as the common way of handling non-stream terminal I/O. I would start out with a simplified version of the GET/READ model found in xBase languages such as FoxPro 2, dBase IV and Clipper plus some cursor movements. Portability would only be assured for the simplest use of the forms. The cursor movement would only be portable when done in "batch mode". I think it would be possible to fit in windowing without stretching things too far.

One would need to define a set of core facilities that will always be present and then to allow the program to inquire about the optional facilities. Currently I favour the set of functions to consist of one function represented by the following prototype:

conStat conio(consoleDescriptor conD, conCmd cmd, conPar *params)
There should be no problem in using the API to support hardcopy devices. People who want complete control should not try to use this facility. (You can ask for a scroll bar, but you are not likely to be able to specify that it should be a Motif style scroll bar.)

In order to avoid having to support all sorts of devices in one library it would be convenient to use the X Window model and create a protocol for communication between application and the program that controls the screen. (I call that program the termio manager.) The protocol would be a variable resolution sort of thing. The termio manager will only tell the application things it needs to know. (You tell the termio manager what you want to know.) This reduces communication overhead. If the protocol were done right one can assume that other languages/tools/applications would use it. By the way, the termio manager is not supposed to be a window manager, it will run under and use the window manager.

On an IBM PC or compatible it would mean that if the termio server is implemented as an MS-DOS driver it would allow an MS-DOS program to look like a native MS Windows/ Presentation manager application when using it from an GUI (if a GUI version of the termio manager was available).

It would also be possible for many programs to work together as a whole. For example, when replacing a built-in editor in a windowing product one could expect the editor to fit in. There are a lot of things that need to be put down in writing until this becomes the preferred way of doing things.

Much of this probably intrudes well into what the Object Management Group considers their domain. Especially the bit where the termio manager request a computer to start up a specific program with itself controlling the display.

I enjoy reading your columns. It is quite strange to read about an American who happens to be aware of other character sets than seven-bit ASCII.

Greetings,

Tarjei T. Jensen
tarjeij@ulrik.uio.no

Nice to hear from Norway. Your proposal for a screen output standard shows how many details need to be worked out. Still, it's basically a good idea. By the time the C Standard is ready for revision, we may all have a better idea how to add screen support. Sorry I had to omit from your letter your proposed extensions to C. We didn't have the space. pjp