Dear Editor:I am somewhat disappointed in your answer to J.P. Singh in the April 1995 issue concerning publishing articles on object-oriented programming (OOP). You say that you will probably never do an article on object-oriented programming per se. Since your magazine addresses C and C++, I think that OOP is a natural. After all, OOP is what C++ is all about. Additionally, I see an overabundance of articles that address the Wintel Windows environment and sort of use C or C++ just to get it into your magazine. I believe that articles on OOP are much more appropriate than Windows articles. By the way, I use a Wintel machine at work and a PowerMac at home. (I greatly prefer the Mac.) For me, articles that are not tied to a specific machine are much more useful.
Frank J. Mlinar
frankm @ xetron.com
Let me clarify with a slightly longer answer. When I say that we don't intend to do articles on OOP, per se, I refer to the preachy articles that tell you why this wondrous new discipline is good for you, and why what you've been doing all these years has been wrong and short sighted. We try to run articles of interest to practicing programmers that show them how to improve their grasp of the art, in particular by the use of the C and C++ programming languages. As an important side effect of doing that job right, we can't help but discuss OOP issues, particularly as they relate to class design in C++ and good coding discipline in both C and C++. Indeed, we welcome such philosophical discussions, so long as they're in the context of solving concrete programming problems.
I don't know how many of our submittors intentionally recast articles in C/C++ terms to get into this magazine, but I doubt that it's very many. Even the most Windows-specific articles we run should have some message a programming technique or a design solution that is of wider interest than just to Windows programmers. I welcome Unix- and Mac-oriented articles, for example, as well as ones on freestanding programs, to give our readers a variety of perspectives. These diverse environments have far more in common than they have differences, in my opinion. But then, I've been a freak about writing portable code since Jack Kennedy was president.
As a final note, I agree only partially with your observation that "OOP is what C++ is all about." As both Chuck Allison and Dan Saks have often emphasized in these pages, C++ is also intended as a better C, and as a language for supporting abstract data types (which is not necessarily OOP). We intend to continue to explore those aspects of C++. And we intend to continue exploring the uses of good ole C, for that vast community of programmers who still find it more than adequate as a programming language. pjp
Editor,
One of my Coworkers suggested implemeting a state machine using:
while(NULL != (state=(*state)() ) ) sleep(1);The only problem is that there seems to be no way to define a function returning a pointer to a function returning a pointer to a function, ad infinitum. Of course I can use a void pointer, but I was wondering if there is any proper way to declare this.Thomas E Zerucha
zerucha@ shell.portal.comI get this problem about once every two years. You are right, of course, that you cannot write the infinite declaration required to support the notation shown above. There are still several ways you can get the executable code you seek. The easiest is to break the recursion with type casts, but not quite the way you suggest. A void pointer can represent an arbitrary object pointer, but not an arbitrary function pointer. (Your example should be rewritten to test the return value against a bald zero.)
Fortunately, any function pointer can represent any other function pointer without loss of information. You must simply be sure to cast the pointer back to its proper type before you use it to call a function. Thus, you can write:
typedef void Any_fun(void); typedef Any_fun *State_fun (void); Any_fun *state1(void); Any_fun *state0(void) { ..... return ((Any_fun *)&state1; } void do_it() { State_fun *state = &state0; while ((state = (State_fun *)(*state)()) != 0) sleep(1); ..... }Here, state0 is a sample initial state function. I've avoided the temptation to write state() in place of (*state)(), because the terser notation further obscures an already confusing situation. This is the form I recommend, because it is the simplest to understand and all compilers that I know translate it properly. Sensible compilers add no code for the type casts, so the execution is exactly as you describe in your (impossible to declare) example.To write a declaration with infinite regress, you must involve a self-referential structure. The tidy way to do so is to write a function pointer member declaration with an incomplete return type, as in:
struct Wrap1 { struct Wrap1 (*fp)(void); } struct Wrap1 state1(void); struct Wrap1 state0(void) { struct Wrap1 ans; ..... ans.fp = &state1; return (ans); } void do_it() { struct Wrap1 state = {&state0}; while ((state = (*state-fp)())->fp != 0) sleep(1); ..... }This is valid Standard C, but I wouldn't be surprised if some compilers balk at the declaration of fp in struct Wrap1. Others may well handle the structure return with less grace than a function pointer not that you're likely to measure the difference in performance. Still, you're on safer ground if you get your infinite regress with self-referential pointers instead:
struct Wrap2 { struct Wrap2 * (*fp)(struct Wrap2 *); } struct Wrap2 * state1(struct Wrap2 *); struct Wrap2 * state0(struct Wrap2 *pans) { ..... pans->fp = &state1; return (pans); } void do_it() { struct Wrap2 state = {&state0}; while ((*state->fp)(&state)->fp != 0) sleep(1); ..... }Sadly, this technique requires that the state functions receive a pointer to a structure, so they know where to store the successor state function pointer. I've resisted the temptation to use one or more static structures to hold return values, since such code is not safe in the presence of multiple threads of execution. Instead, I've added a structure pointer parameter to each state function. The state functions return the pointer argument purely for notational convenience in the state-transition loop.Please note that I have not compiled any of this code. It may well contain infelicities. pjp
Editor,
I am trying to locate the author of RCSIT, a neat utility for use with the RCS (Revision Control System). Last address: Michael Cooper, mcooper@usc-oberon.arpa. The source code for RCSIT is dated 1986 (and it does work), but email to the above address was returned HOST UNKNOWN. I will try to track Mr. Cooper down through NIC, but hope one of your readers can help.
PS: You use RCSIT to prepare your source code for the RCS system it figures out whether your source is C, header, FORTRAN, shell script, whatever, then automatically prepends a comment header with the proper RCS keywords (eg , $Header$, and $Log). RCSIT works fine, transparently, and can be customized. I found it on the UNIX POWER TOOLS CDROM.
Thanks in advance for any help.
Scott Daniels
daniels@minga.pn.com
CIS: 73201,670
Anyone knowing the whereabouts of Michael Cooper (including Michael Cooper) please give us a holler. pjp Mr. Plauger,
I am developing an OS/2 application using IBM's C Set++. One of the most important reasons for using this compiler is because my third party library provider only supplies an IBM compiled library for their product. I would like to be able to use Borland C++ for 0S/2 to compile and link my applications.
The problem of course is that I encounter "Unresolved external" errors with the linker. I understand why, I would just like to know if anyone knows a way around this (i.e. using the IBM compiled library with the Borland compiled application).
I have tried to convince the library developer to supply a Borland library, but their response was, "You can write it yourself."
In closing, I would like to let you know just how much I enjoy The C/C++ User's Journal. I know many readers would have liked for it to have remained The C User's Journal, but I think your coverage of both of these evolving languages is top rate. I gladly renew my subscription every year. Living in a technology starved area like Chattanooga, Tennessee (did ya hear they're coming out with a 486?), CUJ supplies my monthly dosage of "the real world." Thanks a million for the great work!
Thanks,
Andrew M. Pierce
As a native of West Virginia, I always thought of the Appalachian Mountains as more real than the world of computer programming. But I admit that it's rather more laid back than high-tech land. pjp
Dear P. J. Plauger,
I find that CUJ is the best C, C++ magazine ever. For a beginning C programmer I find the articles easily understood and driving me to use C as a preferred language. This letter is a request for help in finding some missing math functions not within my Microsoft C/C++ 7.0 compiler. There seems no ability to calculate inverse hyperbolic sine, cosine, or tangent functions. I have seen asin, acos, sinh, cosh and some others. Where may I get a full complement of math functions for my programming environment? The scientific calculator that comes Windows 3.1 Accessories program group has these capabilities. It would be nice to have that source code in C. Any help would be greatly appreciated. Thank you very much.
Steve Price
sprice@asmusa.orgThe inverse hyperbolic functions are not part of the C Standard. I have seen them in some UNIX systems, even helped write a set for a C compiler once. You might try the CUG library or Project Gnu. pjp
Editor,
Subject: rewind manipulator in letters of March 1995 issue is too complicated. There is no need of a special constant rewind_type object. Here is the simpler version:
#include <iostream.h> #include <fstream.h> istream& rewind(istream& is) { is.seekg(0); is.clear(); return is; } main() { int a,b; double x,y; ifstream is ("filename"); is >> a >> b; is >> rewind >> x >> y; return 0; }Charlie Havener
cdh@epsilon.comLooks like it ought to work. Thanks. pjp
Editor,
I would like to make a comment regarding The C/C++ Users Journal. I have been teaching myself C and C++ for the last year. I subscribed to your magazine to help me learn. Most of your articles are over my head. My I suggest a feature called something like "Beginner's Corner," where you explain more basic concepts and publish small utility programs, like a simple menu program.
At this point your magazine is not much use to me and I do not intend to renew.
Paul Kadow
C/C++ Users Journal:
Thank you for the opportunity to review The C/C++ Users Journal. The Journal is quite interesting and I found some of the listings enlightening. The "Call for Papers" section is also a great idea, allowing reader input.
However, in many respects, the Journal did not meet my expectations. Being an object oriented systems designer/developer I was disappointed at the limited and trivial C++ articles. C++ is moving fast and there is a need to focus on the new and exciting enhancements. I would like to see in-depth coverage of the proposed ANSI standard set forth in the summer of 1994, reference material and listings of the STL, uses of RTTI, Cfront 3.0, and design issues. Therefore, at this time, I will elect to cancel the subscription.
I will continue to browse the magazine racks and purchase The C/C++ Users Journal when topics of personal interest are showcased. Thanks again.
Regards,
A.K. Zambeck
PS: The issue received to review is May 1995. I believe there is a potential memory leak in the copy constructor in Listing 22 on page 85. From experience, it would also be wise to make your destructor virtual.
These two letters illustrate well the problem of any magazine, particularly a technical one. Aim too high and you lose the beginners. Aim too low and you lose the experts. Don't aim and you lose everybody. We appreciate feedback like this even when we lose subscribers to help us continually refine our focus. pjp
Dear Dr. Plauger,
I have always used the style of declaring a local variable within the innermost block containing all uses of that variable. However, a colleague of mine is very much against this practice. He feels that more memory management is done when a variable is declared within its innermost block. If a variable is declared at the beginning of a function it will only be allocated (and freed) once when executing that function. However, if it is declared within a block contained in a loop then memory management will occur each time control passes through the block. Therefore, he claims that it is more efficient to declare all the variables at the top of each routine. How valid is his argument? Thanks for the help.
Steve Coffman
Readability and maintainability argue strongly for your preferred style keep those declarations as local as possible. Only when you fear an unacceptable loss of performance should you compromise such important style rules. In this case, such losses are rare, but they can occur. Here's a brief summary of the issues.
In C, automatic variables are generally allocated all at once on function entry, then deallocated on function exit. If any implementations actually allocate and free storage dynamically within a function body, I don't know about them, but they can do so and conform to the C Standard. Normally, you can dismiss performance as an issue in this particular area.
In C++, storage for automatic variables typically is handled much the same as in C, but that's only part of the story. Objects in C++ must be constructed when they come into scope and be destroyed when they go out of scope. That can involve allocation and freeing of heap storage, which cannot be optimized away. So moving a nontrivial object inside a loop can indeed have surprising performance implications. Moving an array of such objects inside a loop is even more surprising, since every element is constructed and destroyed each time around the loop.
In summary, it helps to know how expensive a declaration is at run-time. When in doubt, however, favor maintainability over any putative loss of performance. pjp
Lee Wittenberg,
I read your good article in the CUJ about Literate Programming. [See "Literate Programming in C and C++ Using CWEB," CUJ, May 1995.] I'm still undecided about it, but this is what I found: I don't want code to look like it needs to be passed through a markup language. How does someone "edit" a literate program? While it makes good presentations, I'm not sure it makes a good working product.
I use c2man to document library functions. What I found (we also have a C-to-man tool in-house) is that I don't want C comments to look like troff text. I've seen lots of examples of comments as input to a markup processor, and that's not what I want.
The beauty of c2man is it requires you to structure your comments a certain way, then it does all the rest. For example:
/* * This is some arbitrary function. * * Warning: use at your own risk */ void somefunc(const int a, /* this is a number */ const char *name /* I hope this is your name */) { }The result looks like this:
somefunc(3) somefunc(3) NAME somefunc - This is some arbitrary function. SYNOPSIS void somefunc ( const int a, const char *name ); PARAMETERS const int a This is a number. const char *name I hope this is your name. DESCRIPTION This is some arbitrary function. WARNING Use at your own risk.Just my experience...marty
leisner @sdsp.mc.xerox.comI share your dislike for any impediments to proper documentation. Equally, I have my reservations about any tool that purports to extract adequate documentation directly from code. Still, automatically generated man pages have to be better than none at all. pjp