Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

We Have Mail


January 1994/We Have Mail


Code Disk Update

The C Users Journal provides all code listings from articles on a monthly code disk, which may be purchased separately from the magazine or on a subscription basis. In addition, the code disk typically contains listings that are too long to be printed in the magazine. Unfortunately, several code listings referenced in the October 93 CUJ didn't make it to the code disk. These files are described as follows:

splash.zip — this file contains the entire splash class library described by Jim Morris in his article, "The SPLASH Class Library." Due to size constraints, this library was not listed in its entirety in the article, but was to be provided on the code disk.

winroth.zip — this file contains the exception handling macros described by Harald Winroth and Matti Rendahl in their article, "Exception Handling in C."

1110072C — this file contains Listing 6 for the article "Random Event Simulation for C Programmers" by Martin Scolnick.

We provide the missing files on the December 1993 code disk. However, if you received an October 1993 code disk missing these files, and would like a replacement, please call, write, or e-mail R&D Publications, Customer Relations, 1601 W. 23rd Suite #200, Lawrence, KS. 66046-2700. (913)-841-1631. e-mail: [email protected].

Also, CUJ code listings are available online, from a variety of sources. For a description of these sources, refer to the section entitled "CUJ Online Source Code," in the Table of Contents, page 6.

Dear Bill:

Lint for C++? A Great Idea!

Actually, as the producers of PC-lint, we've been asked this question hundreds of times over the past few years. Our answer has always been, "We're working on it. It's coming."

Well, it's almost here. We are nearing the end of our beta test cycle and on November 1, 1993, we expect to release PC-lint 6.00 for C/C++. FlexeLint for C/C++ will follow shortly thereafter.

We appreciate Ken Pugh's recommendation of our PC-lint for C but challenge his assumption that the design of C++ all but makes lint-like checking obsolete. Over the years, a number of authors have offered do's and don't's for good C++ programming. See, for example, Effective C++ by Scott Meyers, C++ Programming Style by Tom Cargill, C++ Programming Guidelines by Tom Plum and Dan Saks, and "Check List for Class Authors" (The C++ Journal, Nov. 92) by Andrew Koenig. At the very least, wouldn't it be great to be able to do this kind of checking automatically?

Perhaps a design feature of C++ was to make lint-like checking obsolete, but, as they say, the best laid plans of mice and men oft go astray. I am reminded of an "unsinkable" ship called The Titanic.

Sincerely,

Anneliese Gimpel
Marketing Director
Gimpel Software
3207 Hogarth Lane
Collegeville, PA 19426
(215) 584-4261
FAX (215) 584-4266

I am pleased to see the Gimpels moving in this direction. I agree that their excellent C tools also have a place in the C++ world.

Dear Sir,

Given all the interest in calendar matters, I would like to add some more on the subject. First and foremost, I would like to point out the wealth of algoritmic information in:

Doggett, L.E.: "Calendars"

in Seidelmann, P.K. (ed.):

Explanatory Supplement to the Astronomical Almanac
Mill Valley, University Science Books, 1992,
ISBN 0-935702-68-7
chapter 12 (pps. 575-608).

One can find there many date and calendar conversion algorithms, from and to Julian, Gregorian, Islamic, Indian, and the basic JDN (Julian Day Number), with the interesting aspect that not a single algorithm requires the floating-point format. A wealth of references is also to be found (51, one of them dated 1583! In Latin, of course.) It is to regret that the reference list doesn't seem to comply with ISO 690 and therefore some of the books will be hard to retrieve.

It also is interesting to note that astronomers are moving their Julian calendar origin from January 1 —4712 into the future, to (the noon of) January 1, 2000 (o,c., section 1.253 p. 8) and calling that new system J2000. (All this is very much oversimplified here, of course.) That has several reasons, but the one that really interests me is avoiding, well, astronomical integers. Unfortunately it is connected with a rather arcane thing called Barycentric Dynamical Time I had better not know about. Maybe more practical will be the modified Julian date (MJD) given by:

MJD = (Julian day number) - 2400000
which works with UTC (l.c.) so that January 1, 2000 (noon), which is JD 2451545, becomes a quite manageable MJD 51545 (or J2000 day 0).

There also was a letter from Mr. Viscogliosi to PC Magazine (May 11, 1993, p. 401) including a code listing (DOWGJ.C) for Gregorian and Julian day of the week (on PC Mag Net too, I suppose). Sorrily, that program should be named DOWGJ.CPP — people are getting confused — but it's worth being looked at.

The LeapYear function used by many, and namely presented by Mr. David Burki in "Date Conversions," CUJ (1993) 11-2:29-34, works nice as a macro such as:

#define LeapYear(Y) (! (Y % 4) && (Y % 100) || !(Y % 400))
(I'm using TC 2.0.) There's a most interesting Easter Day algorithm presented in:

Carmony, L.A., Holliday, R.L.:

A First Course in Computer Science with Turbo Pascal
New York. Freeman & Co. 1991. p. 204
ISBN 0-7167-8216-2

which is said to have a domain of Year: [1900; 2099] although the reason is unstated. After streamlining and translating it boils down to the following function:

int Easter(int Year)
{
int Y, A, B, C, D, Month, Day;

/* Return false on domain error */
if ( (Year<1900) || (Year>2099) ) return 0;

/* Compute Easter day */
Y = Year - 1900;
A = (7 * (Y % 19) +1) / 19;
B = (11 * (Y % 19) +4 -A) % 29;
C = ( Y + Y / 4 +31 -B) % 7;
D = 25 -B -C;

if (D>0) { Month = 4; Day = D; }
else     { Month = 3; Day = D +31; }

/* Wrap and return result */
return Month + Day*10;
}
the result being easily unwrapped by the calling function without need of a struct: Month = Result %10, Day = Result /10.

From all that I've read in recent months, it appears that a group of sturdy civil calendar functions is something people need badly and keep on rewriting, a bit like the many attempts to augmenting mantissa size in C numerical formats. I have come across many algorithms of many kinds for calendar calculations and now possess a modest but confusing collection of those, but still try to improve on some of them. At this point, I took for myself some obviously questionable directions, such as not to consider algorithms requiring floating point, and sacrifice function domain for more basic data types such as ints by choosing a suitable calendar origin.

From then on, some basic functions seem to be: day of the week, days between dates, new date given a date and an offset in days, and full date string in the country's language. (My compiler for sure has no strftime nor Locale.h. There are a number of these algorithms, but which is fastest, smallest, most accurate, simpler, most portable, easier to modify, etc.? We can't keep on re-inventing the wheel, even if it's our own wheel. Could it be that someone already did this? I don't want to spend a lifetime doing it, and bet most people don't, but I offer my cooperation — sorry it's no big thing.

Finally, just some tiny matters. I subscribed just very recently to CUJ, but am finding myself asking things such as "Where was that function?" or "Where's that paper on..." and browsing through my very lean collection of CUJ issues (which I guard with my life, more or less.) Given that I also subscribe to the disk listings, couldn't it be interesting to have in each one a index for the issue, possibly with keywords, accessible to search with a grep or DOS FIND? Authors usually know how to do that. I know it's a bore, but it doesn't take too long either.

The final aspect is related with CUG Library. The most recent volumes have no documentation readily accessible. For instance, without having ordered CUJ back issues I would not have known that NEWMAT is a matrix/statistical package (something I need very badly) but is unfortunately coded in C++ (quite an icecold shower). I know you have such niceties as e-mail, but I don't (X.25 is unnafordable) and it's not my fault, nor yours, obviously. It just happens that I'm just an impoverished scientist.

Finally, allow me to congratulate you and the CUJ team on such an accomplished journal. I am totally hooked on it and think I will subscribe to CUJ as long as I code in C, which I expect will be for quite a while.

Best regards,

Joao C. de Magalhaes
R. Almeida Garrett 16 5E
2795 CARNAXIDE
PORTUGAL

And I thought I knew everything about calendar computations. I'm just returning from a visit to the CUJ intergalactic headquarters in Lawrence, Kansas. Interestingly enough, we identified two supplemental services that we felt should be beefed up. One is to make more available a machine-readable index to past articles in CUJ. The other is to better educate our readers about that little gold mine called the CUG Library. Your letter provides useful reinforcement at a critical time. pjp

Dear CUJ,

I am addressing this question to you because I know of no one else who can help me with this question. I have a DOS version of the editor vi. It is called VIPC although it comes up as PC/VI when it loads up. — This software I believe was developed by:

Custom Software Systems
P.O. Box 678
Natick, MA 01760
617-653-2555
508-653-2555

I have tried contacting them but have been told by the Better Business Bureau of Massachusetts that CSS is no longer in business or may be listed under a new name. The reason I am trying to contact them is because I am trying to get an updated version, as the version I have is about six years old. If they are out of business, I was wondering how I could get hold of the source code to this software. I realize that at one time the AZTEC C Compiler came with a version of vi but I prefer the CSS version, as it is more like the Unix version.

This is the specifics of the program:

name      size   date      time
VIPC.EXE  95595  06-04-87  8:57p
The following is the text that is displayed as it loads up:

PC/VI Version 2.01 (IBM-PC) — 6/04/1987 —Copyright (C) 1985-1987
 Custom Software Systems
So if you know how I can get a new version of VIPC or the actual source code I would greatly appreciate it.

Thanks,

Manuel Lopez
6820 LBJ Frwy
Dallas, TX 75240

Anybody? — pjp

Dear C Users Journal,

Just a note to Hutchinson Persons, Engineer, who so eloquently presented his anthrocentric agenda in his letter published in your July 1993 issue. He objected to Christopher Skelly's "errors of distinction" in his personification of some computer terms and characteristics.

Mr. Persons, it warms me truly to see a man so intent on controlling his environment. Damn the mosses! Let the human reign! With people like you at the helm, the planet shall be truly lacquered. Your sense of irony seduces me. What better way to point out the "imprecise thinking" in Mr. Skelly's animative statements than to present your own? ("Can your p[n] make this claim?")

Thank you, also, for pointing out "the importance of a human centered philosophy." After all, when you state that "the word 'sense' applies to a human ability", we human-centered philosophers, at least, are aware that dogs do not really smell, bats do not really hear, and a whale feels nothing when a half-ton fetus slips from its loins. Senses? Of course not, those are merely the automatonous snappings of substandard synapses.

Finally, thank you for denying the evolution and creative use of language. After all, if p[n] cannot "live" below p, then neither, as you sarcastically point out, does fly-tying require "surgical ability." You back this up with your other sarcastic statement that you "compute for a living." We "precise thinkers," of course, know that you really mean "I write programs which enable computers to compute, for a living." I salute you!

I hope that when we all move into sealed and sanitized geodesic domes, as a result of our using "computers, electronics, chemistry, machinery, and any other method (we) can...to enjoy...control...of (our) environment," that you will be my neighbor, so that we, as humans can stand together and never be "relegated to the status of the animal."

Sincerely,

Ed Hawco
Writer, Technical
4854 rue Dagenais
Montreal, Quebec H4C 1L7

And I thought that I was hard on the guy.— pjp

Dear Mr. Plauger,

It was interesting reading Anthony Naggs letter in the March 93 issue of The C Users Journal. In this he was talking about trying to make the bubble sort more useful. I have often wondered why anyone would bother with bubble sort when, with the addition of a couple more lines of code, you can have a Shell sort which is considerably faster. I have found the following version of Shell sort to be very efficient and, as you can see, very easy to implement:

void shell_sort(int list[], int listSize)
{
    int gap=listSize/2, goforward,
           goback, temp;

    while (gap > 0) {
        for (goforward=gap;
           goforward<listSize;
           goforward++) {
           goback = goforward;
           while (list[goback--gap]
                 > list[goback]) {
               temp = list[goback];
               list[goback]
                  = list[goback-gap];
               list[goback-gap]
                  = temp;
               if ((goback -= gap)
                  < gap) break;
           }
        }
        gap = gap * 3 / 5;
    }
}
This beats the sock off bubble sort. For arrays of one million integers I've found it to take about twice as long a quick sort. On the other hand, it does have the advantage of tight memory control.

Regards,

Gordon Lingard
P.O. Box 1550
Armidale NSW 2350
Australia
[email protected]

Your point is well taken. And for a rather small number of items, a bubble sort can be smaller and faster than either Shell sort or quick sort. — pjp

Dear Dr. Plauger,

I am a relatively recent subscriber to The C Users Journal. So far I am finding it quite informative and an inexpensive way to improve my C and C++ skills. I have also purchased and read your book, The Standard C Library which I also found interesting and useful.

In the process of attempting to design a string class for an application a friend and I are working on, I studied several examples found in various books I had purchased on C++. A problem that existed in all of them was an elegant and simple way to handle exceptions to allocating memory. One implementation never verified the return value of new at all — which ran against my training and experience. My initial solution was to use set_new_handler, but on investigating this avenue further, it didn't seem to be ideal. One text referred to set_new_handler as an interim solution and I didn't want to consciously code something that is obsolescent.

Anyway, over the course of about a week of experimentation and study I finally hit upon the following solution: overload the global new operator so that it takes a function pointer argument:

void *operator new(size_t size, void (*newException)());
This function pointer would be used to point to the desired exception function. The code for the overloaded operator new is:

void *operator new(size_t size, void (*newException)())
{
    Boolean quit = FALSE;

    // allocate memory
    void *p = malloc(size);

    // if error allocating memory
    if(!p)
    {
        // if newException points
        // to a routine
        if(newException)
        {
            // call the exception
           // handler
            newException();

            // attempt to allocate
            // memory once more if
            //     newException returns
            if(NULL == (p =
             malloc(size)))
                quit = TRUE;
        }

        // exit if no handler defined
        else
            quit = TRUE;
    }

    // if memory allocation failed
    if(quit)
    {
        cerr << "\nInsufficient memory.
            Exited program...";
        exit(EXIT_FAILURE);
    }

    // memory allocation succeeded,
    // retum pointer
    return p;
}
My intent was to have a general purpose memory allocation error function that is called automatically. If the error routine returned, there would be one more attempt to allocate memory before exiting the program.

I think I have accomplished that. In addition, some experiments with my own code has convinced me that this approach eliminates a large amount of code, which is to say, the object files are a lot smaller for modules that make extensive use of dynamic allocation. While I haven't run the overloaded new operator through a profiler, it seems reasonable to me that the reduction in speed is not that significant. The standard global new operator is not shadowed and is readily available should it be desired.

Later, it occurred to me that the same approach could be utilized with the Standard C function malloc. It would be relatively simple to define a function such as the following:

void *mymalloc(size_t size, void (*exception)());
with code similar to that found above. This would greatly simplify writing dynamic allocation routines since most applications will want to handle exceptions in only a few standard ways. Using this method seems both simple and elegant. Code would be easier to read, and executable files would be significantly smaller (at only a slight cost in run time).

What do you think of this approach? Is it a good method or is there a complication I'm not considering?

I can't help feeling a lot of people smarter than I have worked many years in these languages. This has to have been considered at one time or another and yet I have never run across it before.

Finally, what is the future of set_new_handler? Is it really intended to be an interim solution or do the C++ committees intend to retain it?

Sincerely,

Randel Dale Astle

The joint committee definitely plans to retain the function set_new_handler, with just a few refinements in its semantics. As you have observed, programmers do not always check whether a new expression succeeds. Thus, the joint committee has introduced an exception that is thrown by default when the expression fails. Reconciling this behavior with past practice involves a few subtleties that I'd rather not explore here.

I like your approach of passing function pointers for exception handlers. Yes, I've seen it before, in one form or the other, but it's not widely used. My guess is that most programmers don't want to have to specify the handler function on each call. — pjp

Dear Sir:

I recently subscribed to the CUJ, and enjoy it very much. I especially enjoyed the articles on curve fitting, the alpha/beta filter, and recovering distorted wave forms. I would very much like to see some good, fully researched articles on the following topics:

  • Fast Fourier Analysis
  • Maximum Entropy Spectral Analysis
Maybe one of the excellent "engineer" authors would do this for CUJ.

I am not an engineer, but I need to use these methods in a project that I am working on. I have not been able to find anything concerning these topics in any of the programming magazines. Any help or comments will be appreciated. Thank you.

Sincerely,

Delbert Bourling
648 Maple Grove Rd.
London, KY 40741

P.S. Any published information on "Maximum Entropy Spectral Analysis" seems to be very, very scarce. I think some good algorithms and C/C++ code would be very useful to a wide range of CUJ readers.

Your interest is noted. Potential submittors might note the same.— pjp

Howdy, howdy!

Not only am I going to try out your journal but I am going to present you with a challenge to see how useful you can be to me.

In mode 18 (native VGA) I draw X-Y axes on a screen, label them, make tick marks on the axes, and again label them. Then I draw a graph from data stored in an array. But now I want to send a duplicate of this to a printer and I don't really want to wait more than a minute for this to happen. Presently, I can redraw the image on a hidden video page and do a kind of screen dump by reading the entire screen pixel by pixel, row by row, and loading it into an array. Then I take a third-party printing utility (PGL Toolkit) to send the array to the printer. It is slow and the output is poor. If I had their source code, I would see if I could redesign it for my particular application and recompile it as a subroutine in my program. Also their printer drivers could stand some improvement. How do Microsoft and WordPerfect get their graphics images to a printer? I would like to know how and do it myself. If you have some alternatives or know something I don't, please send me some info.

Hoping this finds you willing and able I am

Jim Baugh
412 South Wakefield Drive
Lafayette, LA 70503-4632

Anyone want to rise to the challenge? — pjp

P.J. Plauger,

Recently I was made aware of the existence of The C Users Journal. What do I have to do to subscribe? I would also be interested in contributing. Your monthly column is something I always enjoy.

I have been a practicing programmer for about 30 years and your comment about "computer science" in the June 1993 Embedded Systems Programming struck a chord. I have never seen any justification for either "computer science" or "software engineering." My view has always been that these are just power-grabbing ploys by the two established disciplines of Science and Engineering, both of whom saw it as a threat to their cozy worlds and a chance to grab some of the kudos and dollars that would go along with annexing this new and useful but essentially unrelated activity.

Programming has always seems to me to be a skilled craft. It has also been my observation that some people can do it and some can't. The ones who can't just never seem to get it, and no amount of training, etc. will help. I learnt to program in the early 1960s without the benefit of formal teaching by anybody who knew anything at all, simply because there weren't many such people available. Over the years I have managed to correct most (but not all) of the bad habits acquired and the process has left me with a keen awareness that I should always be looking at how I do things and be ready to change when there is obviously a better way.

Which brings me to other thing which you have mentioned and causes me some perplexity at the moment, C++. As a practitioner, I have embraced most of the other significant developments in programming that have occurred over that last three decades. In just about every case they solved an obvious problem or pointed out a better way to do things. I can't get excited too much about C++. Maybe I am just getting too old for this and should go find something else to do, but comparing C++ to the elegant simplicity of Pascal or Modula leaves me cold. The fact that the authors/creators in many instances have simply replaced one set of jargon with another doesn't help in sorting out what is substantive in what is being offered.

I guess I will have to go with the flow and at least try one project with C++.

Frank Campbell
uunet!mti.com!campbell

I think you're being a bit hard on computer science and software engineering. There are definitely both scientific and engineering principles that are highly relevant to the design and use of computers. I agree that programming is a craft, having learned it much as you have, but many a craft has been improved by the application of discipline and technology.

As for C++, it is certainly not elegant in the same sense as Pascal, Modula, or even good old C. I find that C++ comes into its own is with larger programs. If you have little occasion to work on large projects (where "large" is admittedly a relative term), you may find few compelling reasons to use C++. — pjp

Dear Mr. Plauger:

I have a number of questions regarding file handling functions that I hope you can answer, or else direct me to a publication which discusses this area.

The Microsoft C60.0 library includes three groups of file handling function; fopen, open, and dos open. Other than the buffering offered by the fopen family, I do not know what their other advantages and disadvantages are. For example, is there any difference between using _dos_read to read file data to a far buffer, and using read compiled with the large memory model?

A second question relates to DOS system buffers. When a C function writes data to the hard disk, does it initially write to a DOS buffer, and then to disk when the operating system decides? I presume the system buffers are the ones defined in the CONFIG.SYS file. I also presume the buffering provided by the fopen family uses different buffers. Is there any way to ensure that data is immediately written to the disk?

Finally, how does SHARE work? I use it with the fopen and dos_open functions because my programs are often used on networks and I need to implement record and file locking. I am confused about SH_DENYNO and SH_COMPAT. For example, Microsoft recommends using SH_COMPAT for DOS-based networks, but I have only been able to implement proper file sharing on Lantastic and Novelle networks using SH_DENYNO SH_COMPAT gives a "sharing violation" whenever a second program attempts to open a file already open but not locked.

I would be very thankful for any light you can shed on these problems. Thank you.

Yours truly,

R.W.J. Ford, M.D.,
Department of Anaesthesia
Shaughnessy Hospital
4500 Oak Street, Room A437A
Vancouver, B.C. V6H 3N1

I can only give you partial answers, since I tend to stick to fairly portable C. Function open and its brethren model the original UNIX style of I/O. When C started getting moved among machines, fopen and company were added. These guarantee a reasonable amount of buffering, and insulation from peculiarities of the underlying operating system, at the cost of still more machinery and fewer supporting operations. The DOS versions give you access to more DOS-specific features. In particular, you can choose whether or not to lose those carriage returns that DOS has and UNIX lacks when you read from a file.

You can pretty well count on system buffers to be involved in practically all reads and writes. The buffering logic within DOS has to deal with blocking and unblocking sectors of various sizes, and with various limitations on how well DMA channels can address different parts of storage.

All I know about SHARE is what you apparently know — that it helps minimize collisions among processes reading and writing the same files in parallel. I know it is supposed to support more sophisticated kinds of sharing such as you describe, but I've never had occasion to program with SHARE in mind. — pjp

Dear Mr. Plauger,

My apologies for writing to you at a rival publication, but this was the most obvious address to get you at. The purpose of the letter is to tell you how much I have enjoyed "Programming On Purpose" over the years, and how much I will miss it years to come.

I found your column to be one of the most thought provoking I have ever read (and not just from a programming point of view) — and often used to bring the themes up in conversation at my place of work to get a debate going around the issues you raised. I cannot say I always agreed with you. I never, however, disagreed strongly enough to write a letter to the author!

I sincerely hope the book Programming On Purpose: Essays on Software Design, as well as its companions, is released in South Africa. Just in case, would you mind sending me the ISBN numbers of the books at the above address. This will help me to get hold of them easily. I believe the publisher is Prentice-Hall.

Once again, thank you for some provocative stuff.

Yours Sincerely,

John Bannister
P.O. Box 32092
Braamfontein, South Africa 2107

Volume 1 (design) is ISBN 0-13-721374-3, volume 2 (people) is ISBN 0-13-328105-1, and volume 3 (technology) is ISBN 0-13-328113-2. All are indeed published by Prentice Hall and all originally appeared in Computer Language. — pjp

Dear Dr. Plauger,

Thank you for printing the letter in which I asked Mr. Pugh why my int10h handler successfully controlled printf scrolling on monochrome, while EGA/VGA systems appeared to scroll without calling int10h at all (CUJ March '93, p.124).

If you'll permit it, I'd like express publicly my thanks to Brian Knoblauch (Toledo, OH), Ir. H. Hahn (Veldhoven, The Netherlands), Steve Ferrell (Duluth, MN), and Carl Smotricz (Hattersheim, Germany). They took the time to share their experience and knowledge about EGA/VGA scrolling with me. Mr. Knoblauch suggested that I investigate int42h. Mr, Ferrell recommended PC Interrupts (Ralf Brown and Jim Kyle, Addison-Wesley, 1991), and said int42h was used as a replacement for int10h on some EGA/VGA systems. Mr. Hahn gave details about intercepting inth10h AH=13h (Display String) to control scrolling from string display functions. Mr. Smotricz had discovered that some systems call int10h AH=6 (Scroll Up) from int10h AH=9 (Write Char & Attr) and int10h (Write Char); he suggested that I intercept those functions in addition to intercepting Scroll Up.

Disassembling int42h on an AST Premium 386 showed that it was indeed an int10h workalike, as Mr. Ferrell and Mr. Knoblauch indicated. In particular, the Scroll Up function (AH=6) looked like that of int10h. But when I modified my program to intercept int42h AH=6 in addition to int10h AH=6, nothing changed: Scrolling was controlled as expected on the XT-clone monochrome system, but not on the AST VGA or a 386SX VGA with Award BIOS. Nevertheless, the suggestions provided by Mr. Ferrell and Mr. Knoblauch provided valuable experience which I will use in future projects.

My experiments showed that intercepting String Display (AH=13h) or Write Char (AH= 9 and 10h) of int10h would make screen updates too slow for my current application. Still, the detailed information on these points given by Mr. Smotricz and Mr. Hahn will be useful in other contexts. I'm glad they didn't let either national boundaries or the Atlantic Ocean stop them from giving me the benefit of their hardwon experience.

Since there doesn't seem to be a good way to make my int10h method control printf scrolling on EGA and VGA, I had to look at alternatives. The solution I finally implemented was a modification of a method I had devised prior to the int10h method. It involves changing DOS Offset of Video Buffer (OVB) value stored at absolute address 0:44E to point to the first colunm of top row to be scrolled, and clearing 25 rows of video-buffer memory at that offset. This "OVB method" assumes that video page 0 is current, and that the last byte of page 0 is immediately followed by at least 3,840 bytes of unused memory (which is usually the first 24 rows of video page 1). The original version of my OVB method worked on a Hercules-clone monochrome system, and on a Paradise VGA Professional, but failed to control scrolling on an IGC 20 TIGA system, and on one other machine with an unknown video system. That's why I abandoned it in favor of the int10h method outlined in my earlier letter (CUJ 93 March, p.124).

The failing systems behaved as if they reset the OVB word at 0:44E to 0 repeatedly. (Indeed, the BIOS listings in the IBM Hardware Technical Reference (84apr) reveal that a call to int10h AH=0 (Set Mode) or 5 (Set Active Page) will cause the word at 0:44E to be reset to 0.) In the original version of my OVB method, I set the scroll-limiting OVB value only once, during program initialization. To make it work in spite of repeated resetting, I wrote a function to use instead of printf whenever limited scrolling was in effect. The replacement function (Listing 7) re-assigns the scroll-limiting value to the OVB word at 0:44E prior to each screen write.

[The listings mentioned in the preceding and subsequent paragraphs are omitted because of their length. We include them on the monthly code disk. — pjp]

This solution controlled scrolling on all systems tested (Hercules, Paradise VGA Pro, and TIGA), but a problem remained: Lines written in the scrolling rows did not appear at all on the TIGA screen, yet displayed as expected on the other systems.

To investigate the TIGA problem, I wrote a small test program so I could experiment with the effect of modifying three additional video parameters in various combinations. (The OVB word at 0:44E was changed for every test). I found that modifying the Start Address Register didn't work on any of the three test machines. Modifying the Screen Length word at 0:44C was not effective on any of them, either.

In the test program, changing the Number of Displayed Rows at 0:484 to correspond to the new value at 0:44E worked on all the test machines. For example, if row 0 through row lockrw were to remain locked on the screen while rows lockrw+1 through 24 were allowed to scroll up, putting the value 160*(lockrw+1) at 0:44E and 23-lockrw at 0:484 would work on all test machines.

Unfortunately, when I tried this method in a production program, the TIGA machine showed display anamolies. At that point, I decided that it would not be economically feasible to continue trying to include TIGA systems in this version of the production program. I removed the code for changing the Number of Displayed Rows value at 0:484 from the production program; and it worked as expected on all test machines except the TIGA.

To use the OVB method, you'll need code similar to that shown in LOCKVROW (Listing 2) to lock a specified video row on the screen while allowing those below it to scroll. You'll also need something like UNLOCKVR (Listing 3), so you can restore normal scrolling before returning control to the operating system, or before each section of code that needs a visible cursor.

The cursor should be hidden (HIDECURS, Listing 5) during locked-row operation, because its position is relative to the changed offset at 0:44E, while the visible characters on the screen are always relative to offset 0. For example, if the cursor is visible on row 24 when 0:44E is 0, changing 0:44E to 2720 (to lock row 17 on the screen) will put the cursor on row 24 of the "new" screen that begins at offset 2720 of the video buffer. But that row isn't visible on the displayed screen that begins at offset 0 (it would be row 41, counting from there).

Furthermore, experience shows that printf (and scanf) ignore the 0:44E offset. Their data is relative to offset 0 of the video buffer, regardless of 0:44E. In one experiment on a Hercules clone, I set 0:44E to 17*160=2720, then used a BIOS call to put the cursor on row 0. The cursor immediately appeared on row 17, but both printf and scanf operated as if the cursor were on row 0: characters appeared on row 0 as the cursor moved in step along the columns of row 17.

For sections of code that need a visible cursor, as when requesting and obtaining keyboard input, UNLOCKVR (Listing 3) can be used to restore the OVB word at 0:44E, and NORMCURS (Listing 6) will restore the cursor. To return to locked-row operation, use HIDECURS (Listing 5), then use RELOCKVR (Listing 4).

I hope others can benefit from our combined experience with controlling printf scrolling. Thanks again to you and to the other four people who so unselfishly helped me. It's encouraging to see free exchange of ideas continuing in spite of software patenters who, ignoring that all ideas are derived from others — which are based on the ideas of still others, ad infinitum — conceitedly claim exclusive ownership of any they might use in software. Please continue your valuable contributions to the international community of programmers, students, and teachers.

Sincerely,

Sid Sanders
5 Seneca Avenue
Geneseo, NY 14454-9508

I add my thanks to all our readers who have repeatedly demonstrated a willingness to share their knowledge with others. — pjp

To the editor:

Mr. Ralph Franke has called to my attention an error in the RPFT code described in "Curve Fitting With Extrapolation, C Users Journal, June 1993. The statement for reading the command-line parameter -DIG in line 47 of function commd reads only the second digit. Lines 46 and 47 are:

if (!strncmp(&argv[i][0],"-DIG=",5))
{ k=sscanf(&argv[i][6],"%d",dig);
but should be:

if (!strncmp(&argv[i][0],"-DIG=",5))
{ k=sscanf(&argv[i][5],"%d",dig);
Mr. Franke also pointed out that, for data which are an exact fit to a polynomial and which include a Y value of zero which is not the first or last datum, the interpolation routine may give incorrect results with no warning message. This is not a problem for inexact empirical data, but the revised routine shown in Listing 8 should be used for safety.

This version can still fail for some abscissae with exact polynomials such as:

y = 10 x -9 x^2 + 8 x^3 -7 x^4 + 6x^5
For x = -3, -2, -1, . . . , 12
That function is fit properly by RPFT, but in a separate test using the interpolation routine by itself it failed at some points such as X = -1.5 with the message that a pole (zero denominator in the interpolating function) may exist at that point. X = -1.500001 runs OK.

Lowell Smith
73377.501@compuserve

We provide the corrected version of RPFT on the monthly code disk. — pjp

Dear CUJ,

First, let me say that it would be nice if prospective letter writers were directed to an e-mail address. I didn't really want to dump on you, Mr. Plauger. :-) [Use [email protected] — mb]

I enjoyed Chuck Allison's July article on C++ Streams. However I wish somebody would provide an exhaustive and accurate list of the effects of all format flags for both input and output. Also, there is a lot of confusion out here regarding the duration of these flags and items set by manipulators such as width, precision, and fill.

I have been through talks or books by Stevens, Stroustrup, Coplien, Saks, Semaphore's Shewchuk, Rowe, Swan, and Allison, and I still am not sure on a couple of points. Specific areas of confusion include:

Q. Is the default for ios::basefield all bits off? Is this the same as ios::dec? For Borland, at least, the default for an istream is all bits off, and this is not the same as having ios::dec set. For instance, given the example in Listing 9, try typing "0x12" three times:

0x12 0x12 0x12
18 cin.good()=1
18 cin.good()=1
0 cin.good()=1
0 cin.good()=0
0 cin.good()=0
The first time uses the default input setting, and converts to hex. The second is explicitly set to 0 (the default) and also converts to hex. The third input explicitly sets the decimal flag and stops reading at the "x" giving you a value of zero. The other inputs choke on the "x."

If you think this is esoteric, have a user enter "010" in a field where you have not set the base explicitly to ios::dec! You guessed it, you get a conversion to octal for a value of 8.

Q. How long are width, precision, and fill in effect for istreams? ostreams?

Best Regards,

Dave Rogers

Frank Russell Company

[email protected]

The Library Working Group of the joint C++ standards committee is indeed working to clarify the issues you raise. Currently, they suffer from an excess of variety, as you point out. Much of the basic work in this area has already been done for the LWG by Jerry Schwarz, the original author of iostreams. I will be answering questions such as this in more detail in my column, "Standard C" (admittedly a slight misnomer here), and later in the book I am currently writing on the Standard C++ library.

For now, I will simply say that ios::basefield should be initialized to ios::dec. Precision and fill stay in effect until you explicitly change them. Width gets set to zero by conversions that use the width. — pjp

Dear C User's Journal:

I enjoyed the article on "Automated Unit Testing" by Roger Meadows in the August 1993 CUJ (pp. 53-58). I did notice a bug that the test routine did not catch in the strws function. If the input string ends in whitespace, processing continues to run through memory until it finds a \0 terminator that is not preceeded by whitespace.

I realize that the point of this article was to point out how to include a main routine for testing purposes but it should also be noted that this is just the type of error that is very difficult to track down when you get "bus error — core dumped" much later due to other data being walked on. Especially when it is dependent on a specific data pattern that will likely be very intermittent and hard to reproduce.

Anyone writing testing routines needs to look very carefully at potential errors in the routine being tested. In my opinion, any syntax like "*to++ =... ", should raise a flag that says "you'd better make sure you don't accidentally walk out of the bounds you intend." This means adding at least a suffix to the data. In this case, the suffix must contain multiple blanks or tabs to detect the problem.

An example is shown in Listing 10.

Consider the string "test\t", after copying the "test" characters, to and from both point to the tab. A blank is copied instead of the tab and both pointers are advanced to point to the \0 terminator. The inner while is not executed since we are pointing to the end of the string. We now come out and copy the next character since it is not a blank or tab. However, it happens to be the \0 terminator. The pointers are advanced and no longer point to the terminator. The outer while will continue to process until it finds a terminator not preceded by a blank or tab.

The fix is to add a condition, as shown in Listing 11.

And to include in the test code as shown in Listing 12.

DISCLAIMER: I have not actually tested this code, these are just my thoughts while reading the article. You should check them before publishing any comments including this code.

Ed Sarlls, III
Western Geophysical Exploration Products
Houston, TX, USA
[email protected]

Opinions expressed are not Western Geo's and may not even be mine.

Roger Meadows replies:

Thank you for your comments on the article and for finding the problem with the sample program. I followed the process presented in the article to fix the problem. First, I modified the test portion of the sample program so that it finds the bug you described. I used the test code modifications you suggested. The changes did cause some of the test cases to fail. However, it seemed that all of the test cases should have failed. I had to increase the length of the test suffix to get all of the test cases to fail. Then, I modified the application code, also using the modifications you suggested, to fix the problem. Rerunning the test code demonstrated that the fix worked and that it did not break anything.

I think your suggested modification to the test code makes a good addition to the rules for writing automated test code.

8. Make sure application code does not write beyond the end of buffers.

STRWS. C, a revised listing of the sample program with "/*new*/" at the end of new lines, is available on the monthly code disk.

Dear Mr. Plauger,

I have been reading CUJ now for several years. My work has benefited much from the feature articles and editorials in your magazine. Now I would like to query that immense knowledge base for a specific need. I am involved in the development of a large data acquisition system running the iRMX operating system. The system is distributed around an FDDI network that is implemented primarily with virtual circuit connections linking software modules. We need to synchronize the time on all of the machines as closely as possible. I was wondering (hoping) that either you or one of the readers knows of some iterative-feedback type algorithm for performing such a synchronization; sort of a software version of the four-wire power supply.

Thank you,

Frank Metayer
Electric Boat
Groton, CT 06340

I don't, but I hope one of our readers does. — pjp

Dear Mr. Plauger,

I was just sitting here reading the letters to the editor in Vol. 11 No. 7 of CUJ, waiting on my compiler to decide if I got it right. In reading the letters, I'm amazed at how critical they are! I hope you have a similar number of positive letters. Please don't be discouraged. I believe you provide an excellent service and a valuable resource. Keep up the good work!

Tedd Gimber

Letters tend to be more negative than positive, I think because anger is a more powerful motivator to action than mere joy. Mostly, I've learned to mentally compensate for that bias. But I still enjoy letters like yours whenever they come in. Thanks. — pjp

Chuck Allison:

In your article in the August 1993 of The C User Journal, you talk about the void * pointer. Well, is it true that void * == (int * or char * or float *)? Why or why not? Please explain because I would like to know why the following statement is invalid:

struct PIZZA {
    int key;


    /* Other fields. */
};

void main(void)
{
    PIZZA *myPizza = calloc(10, sizeof(PIZZA));
    /* ERROR message generated. */

    ...
}
Thanks in advance,

-Con(rad)

ps: I received an error message for the above statement for every compiler that I tried to run my code on.

Chuck Allison replies:

If you are using C, the problems with your program are:

1) You didn't include <stdlib.h> for calloc

2) You didn't qualify PIZZA with struct (or typedef it).

The program shown in Listing 13 works fine.

If you are using C++, you really shouldn't be using calloc. Listing 14 shows a C++ version that works.

As far as your question about void * it is an animal unto itself. It is not an int * or any other *. Its purpose is to allow assignment to and from pointers to any type. It cannot be dereferenced, hence it is impossible to think of it as an int *. This has little to do with your program excerpt. Since calloc returns a void *, it can be assigned (in C) to a PIZZA * or any other pointer type. Correct problems 1) and 2) above, and you're home free.

Hi,

Just read your article in The C Users Journal. I'd like to propose something that would be of great benefit to control progammers like I am. Embedded systems, and dedicated controllers often need the equivalent of floating-point fractional numbers, without the overhead of a floating-point package. I'd like to see a modifier as follows:

fixed — This modifier implies that the variable associated with is is inherently split in the middle with a binary point. All math associated with it takes this into consideration. for example:

fixed int scale_factor; — would have (on a 16-bit machine) an 8-bit integer portion and an 8-bit fractional portion, and thus could represent 0 to 256.9960. By the same token, fixed unsigned char would have a 4-bit integer portion (0-15) and a 4-bit fraction (0.9375). A fixed unsigned long would of course have a 16-bit integer and a 16-bit fraction.

Conversion rules:

Fixed of one size to fixed of another: smaller to larger i.e. 4.4 -> 8.8 would simply have the first 4 bits placed into bits 0-3 of the first byte, and the second 4 bits placed into bits 7-4 of the second byte. For larger to smaller, such as 8.8 -> 4.4, bits 0 to 3 of the first byte (in the 8.8) would be placed into bits 7-3 of the 4.4 and bits 7-3 of the second byte of the 8.8 would go into bits 3-0 of the 4.4

Conversion to integers would simply drop the fractional portion. Conversion to floats would give the floating point equivalent. I have had to write these sorts of things many times, and it is always an aggravation.

The second thing that I'd like to propose is a type called quad. quad is a to a long as double is to a float. If a long is 32 bits, then a quad is 64 bits. quads could also be prefaced with the fixed modifier.

Cheers,

Woody Baker
Postscript consultant/Flint knapper
Austin, TX
[email protected]

A superset of the fixed-point arithmetic you describe is in PL/I and Ada. I don't know how widely it actually gets used. I believe that the Numerical C Extensions Group (X3J11.1) has also explored extended-precision integers. pjp

Dear Bill,

Thank you for the opportunity to present my article on "Extending C for Object Oriented Programming" (though I fear it will mark me for life as the "Macro King"). I have since received several kind letters by email asking for source and reporting bugs. It's letters like these make all the hassle worthwhile. I have also copied you on my mailing of the latest source in case you want to update the code disk for the article.

Yours sincerely,

Greg Colvin
[email protected]

P.J. Plauger:

Salutations from the other side [of the world]. I've just finished reading your April editorial in CUJ, and wish to agree wholeheartedly. In the last 12 months, it seems my professional world has been turned on both its ears with release after release of software, each claimed to be an improvement over the last. In general, this is true, but I can't help lying awake some nights feeling completely inadequate in my abilities to keep up with the pace of change. It makes me feel much better to see that the giants of this programmer's world (for such you are, even if you don't see yourself that way) also suffer some of the same feelings. While I have your ear, or eyes, is it possible to order backissues of CUJ by email? It is a long way from Melbourne, Australia, to basically anywhere, and it would be considerably more convenient for me to order them by email rather than snailmail. Having only recently subscribed to CUJ, I am still catching up (since we get magazines about three months late down here), and I appear to have missed the March 93 issue. If you have not already done so, do give Visual Basic a look. While not complete in itself, it can be a lot of fun to use, and when combined with C, it becomes a truly powerful environment. Enough of this. I thank you for your time in reading this.

Stay sane.

-Craig

We are working on smoothing the process of ordering back issues electronically and otherwise. Meanwhile, I've forwarded your letter to R&D's customer service folk. pjp

Dear PJP:

I read your article "Developing the Standard C++ Library" in the October, 1993 issue of The C Users Journal. I am very anxiously looking forward to your book on the Standard C++ library.

As a member of the C++ user community I would like to add my voice to the outrage over the delay in getting some kind of documentation of the iostreams interface to the public. To my way of thinking, even greater fervor should be applied to releasing some minimal documentation. When the ANSI standard for C was developed, K&R had already been published for several years and users were comfortable with the stdio library interface. If any changes were made to stdio as a result of the ANSI standard, it was relatively easy to make code adjustments.

This is not the case with C++, since as you point out there is very little documentation available on iostreams. Steve Teale's book is a step in the right direction and for that he deserves praise. The title of the book is somewhat misleading since the book does not pretend to be the definitive iostreams reference. As a further criticism, the book references ANSI working documents by Jerry Scharz and Mike Vilot which are not available to the general public.

To the members of the ANSI committee who are working hard to get iostreams standardized, I apologize. Those members who are prolonging the development of the standard with excessive concern for minutiae should be set straight. There is a desparate need now in the user community for iostreams documentation. The ANSI committee should set an immediate goal of informing the public as to what iostreams features and interfaces are likely to remain stable in the standard.

I look forward to your upcoming columns on the C++ standard library as well as your book. I hope you, Steve Teale, or someone else will soon satisfy the need for an iostreams reference.

Larry Johnson
NCR, Lisle
cuuxb!laj
708-810-6524
(VP 473-6524)

I can only share your concern. And believe me, I want to get my C++ library book out soon, too. — pjp


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.