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.


pjp,

I have one doubt regarding the sizeof operator. When this sizeof is applied to the empty class it returns the value as one byte. I request you to clarify why the sizeof empty class is one byte.

When we declare some virtual functions in that class then the sizeof will return four bytes. I request to to clarify why it returns in that way.

Waiting for your valuable answer,

Raviteja Nandibhatla

In most contexts, a completely empty object is not permissible. (The notable exception is a base class that declares no members — it need contribute no size to an object of the derived class.) Thus, many compilers add a single byte to an empty class to give it a nonzero size. An object of a class with virtual functions stores a pointer to a "vtable," or table of pointers to the virtual functions. Typically, such a pointer is four bytes. It satisfies the requirement that an object have nonzero size, so the single-byte padding is omitted. Hope this helps. — pjp


Editor:

Patrick Bailey, in "A Signal Command and Control Class for Unix" (CUJ, March 1999) fails to mention a major caveat of using signals. Basically what he suggests is unsafe because it is not safe to do more than a few very limited things in a signal handler. For example, printing a message in a signal handler is not safe. (The app could crash, hang, or have other problems, immediately or later during the run.) The C Standard, Posix, and the C++ Standard all have things to say about this, and they impose strict limits on what is safe.

I know the editors and many of your readers know this well, but lacking any caveats, less experienced readers might try to carry the use of signals too far.

Regards,

David Anderson
davea@sgi.com


Dear Editor,

I would like to comment on Patrick Bailey's article, "A Signal Command and Control Class for Unix" in the March 1999 issue. I thought the article was interesting, and had some good general purpose ideas in it. However, when I finished it, I was left wondering what the problem was that it was trying to solve.

If the problem was how to send input to a running daemon, it could have been solved much more simply without signals, and without the problems inherent in their use.

And if one is going to catch signals for any purpose, surely signal is no longer the right way to do it. I was surprised that the Posix sigaction call was not used, as well as the fact that the problems with signal-based handlers (and those with the handler in the sample code) were not discussed.

For example, in the sample handler there is a window between entry into the handler and the call to ignore the signal, in which a signal can occur again. This isn't the case with sigaction, which causes the signal to be blocked for the duration of the handler. Not to mention that ignoring the signal within the handler, rather than blocking it, is a good way to miss a second occurance of the signal. Again, sigaction would not let this happen.

The thing that most bothers me is that all the command handlers are executed in the context of a signal. This didn't cause problems in the demo program because there was nothing else going on, but that would not be the case in a production program. Confining the command handlers to executing in signal context is quite a severe limitation, which makes the approach given of limited value in my view.

Given that the test program (and most other programs I've seen) is driven by an event loop, it's much easier to deal with signals by having the handler simply set a global flag for the event loop to see, or writing the signal number into a pipe that the event loop is watching. This has the great advantage of not forcing command code to run in the context of a signal.

Indeed, given that the only information that is really of interest comes through the message queue, all that's actually required is that the event loop watch that. Signals, or the infrastructure given in the article, are not needed at all for what the article describes. In fact, using a FIFO rather than a message queue, and having the event loop watch the FIFO directly, would allow one to do something like

$echo DEMO1 > /tmp/program.fifo

instead of messing around with signals.

I'll grant that these complaints are Unix issues, not C++ issues. However, the article was billed as showing how to use signals for communication. It's not, in my opinion, the right way to do that.

Joe Halpin
jhalpin@ix.netcom.com

Patrick Bailey replies:

To David Anderson: thank you for your observation, and I have no argument that you should not try to implement any single extensive routine through a signal. The SIGUSR1 or SIGUSR2 signals are more appropriate for small, well-defined tasks, and the approach within the article provides support for selecting one of many small tasks during run time. Just as in using a traditional one-for-one relationship, you obviously don't want any of the tasks that can be selected to involve a potentially long running loop that would make the application hang.

You should note that the concept of signaling an application to read data from another resource (data queue, updated file) is common, as you probably well know. The approach suggested only leverages this to allow flexibilty with limited overhead. Further, I agree newcomers always risk the potential of inappropriately using the rich IPC mechanisms of the Unix operating system without guidance from more experienced developers. In the end and as always, it is a judgement call based on a combination of standards, experience, and the needs of the application.

To Joe Halpin: thank you for highlighting the use of sigaction in lieu of signal. You're quite correct in that it is the better approach in setting up the signal handler.

I do disagree with your summation on the utility of the concept. I will not argue with your solution of the implied context. However, continous loops do not always necessarily imply event-driven activities involving a regular series of switch/case statements. Watching a FIFO as part of the regular processing within a loop implies polling. This can add overhead to some daemon processes, which is not often desired, especially if an application's primary loop involves a time critical process. In those instances, it is preferable for an application to alter its processing only when there is an actual need to do so, and it certainly is possible the application's required number of exception-handling options may exceed the defined set of signals available to developers for general use. Ergo the use of signals and messages.

I will meet you halfway on this and suggest that the message-passing action could be handled through a FIFO. As I mentioned in the article, placing this in a separate class hides those details from the CmdControl class.

Your comments were most welcome.

Very sincerely,

Patrick M. Bailey

And Joe Halpin replies:

Thanks for your response. However, I think you're still missing something. There is no polling implied in the solution I suggested. I assumed that it would be understood that select would be used as the monitoring mechanism for event loops. This can keep track of FIFOs, pipes, sockets, timers, etc., without polling, and will block the process until something happens, or until a caller-supplied timeout parameter expires. This is a very common way of constructing event loops, and it works very well.

If something that cannot be accomodated by select is really required (such as a SYSV message queue) then something else would be needed. If such a situation does arise, then I agree that a signal might be the only way to deal with it. However, I think it should still be synchronously dispatched from within the main loop (which is pretty easy to do), not handled by the signal handler directly. Executing code in signal context is too limiting for most uses I've encountered.

No doubt there is generally more than one way to do things, but I guess we'll have to agree to disagree on this one. If you want to use C++ classes, I recommend to you an approach described by Dr. Doug Schmidt, aviailable at the following web site:

http://www.cs.wustl.edu/~schmidt/signal-patterns.html

Regards,

Joe


Dear Editor,

In the March 1999 issue of CUJ, I read about Todd Osborne and Steven Woolgar's frustration with finding the January issue full of Java articles. While I sympathize with and understand their points, I must say I am not as worried as they are. I program only in C++, but I think Java is too big a phenomenon to be ignored by C++ programmers, lest we become obsolescent without noticing. So I welcome, every now and then, some articles that let me follow the Java phenomenon a bit, without going to the extent of subscribing to a Java magazine. I think Java is in the C/C++ family of languages, maybe not a brother, but a near cousin, so it is good to devote some space to it, while I would scream with horror if I found an issue devoted to Visual Basic. It's difficult to make everybody happy.

Giovanni Bavestrelli

You got that right. I appreciate your comments. For the record, though, I would not characterize the January issue as being "full of Java articles." There were in fact three, if you include Chuck Allison's Java column. As for that putative issue devoted to Visual Basic: when pigs fly. Thanks for writing. — mb


Editor,

A methodology question:

We are developing a software application for an embedded system. The system only supports C but most of us have been doing C++ for quite a while now. We want to maintain an object-oriented approach, but two camps have emerged on what exactly is meant by object-oriented in relation to straight C, and especially when you have to maintain the tight environment of an embedded system.

One side wants to use total encapsulation of all structures so that no data members are available without calling an access function specific to that structure — in other words, making each structure a class with only private member variables — while the other side wants to use some data in structures directly. They feel that the object approach should be left to defining modules in which common data and procedures are grouped together.

One example used was a function to retrieve player information from a player manager module. The player information consisted of an account number (int), a name (char[]), and an authorization flag (BOOL). The first group would have the information returned in a structure that was only prototyped so that the internals of the structure were not known. Then the access functions would be called to return the member variables individually. The other side wanted a single access function in which the return would be a const pointer to a structure. Then the data elements would be dereferenced as needed.

Both sides agree that the second approach is more efficient but the first group thinks that their way is much more readable and maintainable which makes some inefficiency acceptable.

I am not sure whether this comes down to a debate on public member variables or on the use of structures in object-oriented design. I would like to hear what advice you would give.

Thomas Lemmons
Director
Applications Development Livewire Inc.
toml@lw.net

It is always a good principle to return scalars rather than structures, and to call access functions rather than access member objects directly. But these are principles, not laws. I have no trouble using a structure whenever I'm likely to use most or all of the fields on each access, because they are so closely interrelated. I have no trouble with direct access to members whenever I really believe that a) I'll never want a chance to intervene on accesses in the future, and b) arbitrary changes by external agents will not compromise the integrity of the object. And in any event, I'll compromise design purity if I really believe that adequate performance is at stake. Hope this helps. — pjp


Hello, Mr. Plauger,

I hope you can help me as I'm trying to use the hashmap container you published in November 1998 CUJ. I'm getting a compile error on this line from hmaptrait.h.

typedef _Ax::template<>
   rebind<value_type>::other

The message is:

hmaptrait.h(8) : error C2902: '<' :
unexpected token following
'template', identifier expected

This is VC++ v5.0.

Are there any examples available of the use of these templates that you're aware of?

Thanks,

Bob Youmans

I have to tailor my production code for older compilers, which presents a problem when presenting code in an article. In this case, VC++ (along with many other commercial compilers available today) can't handle the rebind notation. My company Dinkumware, Ltd. now has a version of the hash tables available to the public as an add-on to VC++. Visit our web site:

http://www.dinkumware.com

But I can't describe simply how to change the code I presented to compile with VC++. — pjp


Editor,

Re: The Journeyman's Shop, March 1999, Initialization and Cleanup, Part 1.

I always specify the return type in declarations, and always include void for functions that take no arguments, in C and C++, because not including those goofs up external processing of the code when used by me in other ways.

Non-whitespace characters must occur between the parentheses for my grep to distinguish between prototypes-of and calls-to functions that take no arguments.

Please don't look at my code and think I'm a moron!

Bart Crane
bcrane@graham.com

I assume you mean that you always specify argument types. But I have to applaud any style that is rigorous and unambiguous, particularly if it helps grep in helping you to search your code. — pjp


Dear pjp,

There seems to be some confusion in your Standard C/C++ column in the March 1999 issue, "Simple Iostreams." In the explanation of the code, uflow and underflow are the wrong way around. uflow is the function that should consume the character it returns, and underflow should return a character from the input buffer without consuming it. Sure enough, that's just what your sample code does.

The description of uflow in the article reads, "to peek at the next input character without pointing past it in the input stream," and then goes on to describe how this functionality is implemented in the sample code in the article. However, it is the underflow function that implements this functionality and not uflow. In order to be certain of this, I checked the C++ Standard, Draft 2, that I have at hand. It states, for underflow, "returns first character of the pending sequence, without moving the input sequence position past it."

Apart from this, I found your article an excellent resume of the most important points behind the standard streams. I agree with you — there are few written guidelines for using the iostreams classes. I still find The Draft Standard C++ Library a very valuable resource. Most of the content is still valid, and I particularly like the synchronization between the Standard Draft text of that time, the explanations, and the supplied library code. BTW, the iostreams functions above are not confused in the book.

This may not be the place to point it out, but I also found some other text in the book rather perplexing. Page 183 says of istream::operator>>(ios& (*) (ios&)): "Calls (*(ios*)pf)(*this), then return this." I can hardly consider this a typo, since page 227 describes ostream::operator<<(ostream& (*) (ostream&)) as "Calls (*(ios*)pf)(*this), then return this". The referred text is part of the Standard C++ draft included in the book, and the supplied code and explanations do get the function right. As defined in Draft 2, the function should just call pf(*this). As you know, this is what allows us to use manipulators as in cout << x << flush, as explained, for instance, in Stroustrup 3rd edition, 21.4.6.

One last point. I couldn't agree more with the "Better Template Error Messages" article in the same CUJ issue. Poor diagnostic messages when using templates is making our life harder than necessary. I agree as well with some of the answers. For instance, I would still like to have access to the long cryptic messages, currently reported, when necessary. Poor support for run-time debugging when stepping through templatized code doesn't help either.

Sergio Boixo
Sergio_boixo@hotmail.com

Thanks for pointing out both errors. I continually confuse uflow/underflow, and I stumbled about quite a bit before finding a cleaner way to describe the extractor that makes manipulators work. I too look forward to the day when most or all compilers will say something sane when a template is misused. Pete Becker and I just spent half a day chasing ghosts because of bad template instantiation diagnostics. — pjp o