Departments


We Have Mail

Letters to the editor may be sent via email to cujed@rdpub.com, or via the postal service to Letters to the Editor, C/C++ Users Journal, 1601 W. 23rd St., Ste 200, Lawrence, KS 66046-2700


Dear Mr. Plauger,

You probably thought you heard the last on your review of Anthony Porters book, The Best C/C++ Tips Ever. While I originally might have agreed with your review, I now know you were too harsh and perhaps too short-sighted. Let me explain.

Two week ago, I was given the unexpected opportunity to dust off my job-seeking skills. I have been in data processing over 20 years and a C programmer all of that time, self taught in C++. Most recently involved with O-O A&D, I have been working with APIs and various class libraries, and my C++ skills languished. But I still felt comfortable. I had no doubt my proven track record would wow any future employer. Wrong!

During my first technical phone interview, before we discussed any of my O-O accomplishments, the young Turk wanted to check my C++ skills. He asked me a few exotic questions about assignment operators and constant member functions. We never got any further. I didnt appear to the interviewer to know anything about C++, therefore I was judged not to know anything about MFC, OLE, COM, Client/Server, etc. It was a bit of an embarrassment. Hell, it was a bloody disaster.

In desperation I went to the nearest book store and grabbed the only three books they had on C++. Dear kind Mr. Porters book was one of them. At home I glance at the books I had bought. I judged Best Tips a little too light and proceeded to jump into the ARM. The very next morning I got a phone call, another technical interview. Oh No! Im not ready, only having read about a chapter the night before.

First question, What is the meaning behind declaring the base class of a derived class virtual? Panic!

I remembered you could give scope specifiers, but virtual? At that moment I couldnt remember my name. Just like the proverbial drowning man I spotted Porters book. I scanned the index while muttering something about derived classes. Aha! There it was Virtual classes, declaring, 232. Turning to Tip 232, I spotted the key phrase multiple inheritance. I promptly muttered what I could about multiple inheritance while flipping pages forward and backward. There! Tip 231, Using Virtual Classes. Confident now and memory jogged, I proceeded to paraphrase the entry.

Hey, he is buying this! God is Good!

Next question, What is a static member function?

A quick scan of the index while I babbled about statics sent me to Tip 131, a turn of the page and there it was, Tip 133, Static member functions may access only static data.

A few more questions, a few more frantic accesses to the index, and we are off into a discussion of Rumbaugh, Booch, and Client/Server methodologies. Apparently I am knowledgeable in those areas now, because I know what a damn static member function is.

Since then, The Best C/C++ Tips Ever and my telephone are constant companions. I am aceing technical interviews and refreshing my C++ at the same time. Would another book be a better reference? Maybe. But Best Tips seems to be the handiest. In fact I have become suspicious that interviewers are using the book as a resource for questions. So many questions seem to have an identical Tip entry. Which presents a great visual. There we are, they with their copy on one end of the phone and me and my copy on the other. There is a Dilbert cartoon in there somewhere.

Anyway I thought you should know that no book is ever useless. You know, one mans trash is another mans parachute.

If you publish this letter, please violate policy and tradition, and dont print my name or address. I may still be looking for employment or be under a watchful eye during a warranty period. Lets just keep this our little secret. Smile.

[name withheld by request]

I quite agree that no book is ever useless, and I appreciate your pointing that out so clearly. Im always reluctant to write scathing reviews for just that reason. Hope you find the job you want. — pjp


Dear Editor (And Mark R. Parker)

I was really disappointed by the article Encapsulating Math Coprocessor BCD Arithmetic, not by the implementation of BCD arithmetic itself, but by the design of C++ code. I understand that it was more illustrative material than commercial software, but even so its unaceptable to write this not-so-C++ code.

  1. Mr. Parker wrote: Since no details about the data are available, class users should not be tempted to modify an objects data through means other than those provided by the class. And later: The file BCD.CPP contains the member function definitions. Users need never see this file. But this has nothing to do with C++, nor to OO! In order to hide the internal class data, its enough to declare it private. This style of hiding is neither necessary nor effective. (More about speed later.)
  2. Why are the operators << and >> declared as friends? They use only public methods of the class BCD. Declaring them as friends is unnecessary (and probably copied from the famous Stroustroups book). And why, please, does the author use printf to print to standard output?
  3. Where is the copy constructor? Because the author didnt declare it, the compiler would add its own. In this case, that is very dangerous! According to the BCD declaration, the following code will compile and produce memory leaks, if not GPFs:
BCD    a("123");
BCD    c (a);

4. There are many discussions about overhead of C++ over C. But in most cases, the only cause of the overhead is improper use of C++. In this case, why not declare data of the class as an array of bytes:

class BCD
{
.....
private:
enum { NUM_BYTES = 10};
BYTE DigPairs [NUM_BYTES];
};

That makes it possible to make all functions inline and thus to get fast, usable code while still being strictly object-oriented.

Poul Costinsky

Mark Parker replies:

Thanks for your message, Poul. You have raised some good points about the article. I will respond to each of them.

  1. We have a disagreement about this particular style of information hiding. When I designed the basic class BCD, I was not operating under any particular constraints of object-oriented programming. I could have made the data for BCD instances an array of bytes, but I chose not to do so. It seemed to me that to hide the actual implementation as I did would prevent users of the class from even thinking about modifying the data in ways other than those provided.
  2. As I mentioned in my article, the functions BCDGet and BCDPut were originally written to handle input and output of BCD objects. I was more concerned with getting the job done than with object-oriented programming. As the project developed, I decided to put wrappersaround these functions to allow for a more C++-like style of input/output. My reasoning in declaring the operators << and >> as friends to the BCD class was that they mediated between the BCD class and the ostream/istream classes. For each, the left operand would need to be a stream object (e.g. cin or cout), which means that neither operator could be a member of the BCD class. On the other hand, I liked the idea of placing their declarations in the class body rather than making them non-member functions. This is why these operators were made friends of the class. Incidentally, I did not copy them from Stroustrups book.
  3. This is the most serious problem. During development of the program I did have a copy constructor for the class, which I was having problems with. I found that taking it out seemed to solve the problem I was having. I reasoned (incorrectly!) that it would be okay to have the compiler generate its own copy constructor. The difficulty surfaces, as you point out, when two BCD objects point to the same dynamically allocated memory, and one of the objects is deleted. I have added a copy constructor which I believe solves the problem. Mea culpa!
BCD::BCD(BCD& BCDVal)
{
DigPairs = new BYTE[NUM_BYTES];
for (int i = 0; i < NUM_BYTES; ++i)
    DigPairs[i] = BCDVal.DigPairs[i];
}

4. My response to point 1 covers the issue of array vs. pointer to dynamically-allocated memory, so I wont restate that argument. As far as making the functions inline goes, I wasnt concerned with speed as much as with providing a high-level, user-friendly abstraction for a hardware device that can be controlled only by assembly language instructions. This was the main purpose of my article and the code I provided for it. I do not consider the code I wrote to be the last word on either design or implementation, and welcome any improvements that readers can provide.

Sincerely,

Mark Parker


Dear Editor,

I have several related questions which are concerned in part with software engineering practice, and in part with the design of the C language (specifically the meaning of incomplete types.)

I am writing a library. Some of the functions in the library require state information to be saved from one call to the next. I dont want to store this information in the library. For various reasons it would be preferable to assign that responsibility to the calling code. On the other hand, I dont want the users of the library to be able to make any assumptions about the implementation of the library.

Clearly, one way to do this is to allocate a structure internal to the library and pass back its address to the calling function as a void *. The drawback to this approach is that the compiler has no way of determining that the caller is passing in a pointer of the correct type.

If I create a new type of pointer with the statement

typedef void * opaque;

then although opaque is a new type, it seems to inherit the typeless characteristics of void *, which is probably desirable behavior but it doesnt offer any help for my problem.

It has been brought to my attention that if I incorporate a declaration of the form

struct undefined_tag * opaque;

in a C module the compiler will perform type checking on opaque and it will allow me to use it as a pointer variable as long as I dont dereference it. Also, since the structure tag is not known to the compiler at the time it is used, it need not correspond to the name used internally in the library. So if the library contains a structure called by the unlikely name of

struct date_time_and_ref_count

I could export this type in a public header file under a less revealing name, such as

struct mylib_magic_cookie * opaque;

As long as the external declarations of the functions in the library use this name in describing parameters and return values, users of the library can use the externally defined type and get the benefit of type checking from the compiler.

I have three questions.

1) Is this method considered good programming practice?

2) Is the use of different names internally and externally considered okay?

3) Is this behavior a fluke of the implementation of several compilers, or was it a design decision of the standardization committee?

Up until now I have used the void * approach. Since this alternative provides a means of developing more robust code I would like to know if it is something on which I should rely.

Sincerely,

Robert Carey

Ive omitted the example you supply for space reasons, and because my reply doesnt depend on such intricate details. You have indeed run across an unfortunate limitation of type checking that we felt moved to spell out in the C Standard. Interestingly enough, it was a topic of considerable discussion at the most recent C standards meeting.

Put simply, C++ might give you the type checking you desire for incomplete pointers, provided you get all the tag names to match up properly. The same will probably be true of C9X (the next version of Standard C, optimistically named for a year in this millenium). But right now, those incomplete pointers offer nothing over void pointers in the way of type safety. — pjp


Mr. Plauger,

I have seen numerous methods (including one in an old CUJ) for dynamically allocating two-dimensional arrays. Each method basically involves allocating an array of pointers (one pointer for each row of the array), and then filling this array with pointers to several other arrays (one array for each row). For example, the following function returns an integer array of size R x C:

int ** Allocate2DArray(int R, int C)
{
int ** Row;
int n;
Row =
    (int **) malloc(R * sizeof(int *));
for (n = 0; n < R; n++)
    Row[n] =
      (int *) malloc(C * sizeof(int));
return Row;
}

This method seemed a bit complex to me, considering the number of malloc operations performed. From a programming standpoint, it is easy to get confused with the amount of indirection, and the function that destroys the array must be written very carefully to ensure that all of the memory will be freed properly.

When I first encountered this problem a few years ago (and not knowing about the above method) I came up with the following solution. To create the array I used the following code:

int COLS;
int * Allocate2DArray(int R, int C)
{
int * Row;
COLS = C;
Row =(int *) malloc(R * C *sizeof(int));
return Row;
}

Somewhere in the code there was the following:

int * My_array;
My_array = Allocate2DArray(x, y);

I would then use the following macro to access the array:

#define MyArray(r, c)\
   My_array[r * COLS+ c]

This method seems much simpler and safer, considering that there is only one malloc statement. Each member of the array can be accessed just as easily as in the other method, and it is much more unlikely that someone will incorrectly destroy this array. Another benefit of this method is that it actually allocates less space than the first method (it does not need to allocate the array of pointers).

The only tradeoff that I can see using this method is that a global variable COLS must be created. (Keep in mind that the programmer will most likely keep track of this variable anyway.)

My question to you is: Is my method bad C or just plain bad programming style? In my own mind it seems to be the best method, and yet I have never come across anyone using it. I must assume that there is a good reason for this, and would appreciate it if you could lend your insight into the matter.

Thanks,

John Manfreda
jmanfred@fh.us.bosch.com

Ive seen 2-D arrays handled both ways, but I personally always do it your way. Feel any better? — pjp


To the Editor:

Ive been enjoying this magazine (and C/C++) for some time but I havent seen a discussion of several issues important to programming productivity when using C and C++. These issues include (the lack of) automatic generation of header files, and (the lack of) the ability to place initialization values immediately following class members.

Books on C tell you to write the declaration of extern functions twice (the second time in a header file for other files to include). This is very inefficient, especially when there are large numbers of small functions. It is also very difficult to ensure that comments describing the functions get properly updated in the header file.

For C, I wrote my own function prototype extractor. Functions to be extracted I precede by a special identifying comment so that both the comment and the function declaration can be emitted to an automatically generated header file. It easily became a habit to put useful commentary within the special comment knowing that this would be put into the header file for other people to use for reference. (I could write a separate tirade about make programs and what I did about that.)

Then I migrated to C++. Books on C++ encourage you to define the class functions in a different file than the header file they were declared in. The result of splitting things up into two files is to make it very difficult to maintain the class. I keep having to switch back and forth between two files and there is the old problem of having two copies of the comments for each function.

Lately Ive been pushing my compiler further and further by leaving more and more code inline, and so far my compiler hasnt broken. I would prefer to write all my code inline and then use a class abstractor to produce a header file. Ive held off because I have no way to be sure that the abstracted class declaration would match up with the original. I would prefer that the C++ language definition would give the necessary guidance to make this possible.

In some C files I put one class (in C++ terms) per file, in the form of static variables and external functions using those static variables, defining (usually) just the one and only object of that class. All of the static variables are initialized at the definition or otherwise are initialized by the run-time system to zero. I realize that this technique does not extend to dynamically allocated memory, but I would have expected that C++ would fix this. Unfortunately, with a C++ class I have to move all the initializations into the constructor. It is difficult to ensure that all the members get initialized. An odd exception to the above is if a member is of class type; the ugly workaround (including macro for one constructor argument) is:

#define CMEMBER(type,membername, \
                initvalue) \
class type ## ___ ## membername :\
public type { \
public: type ## ___ ## membername() : \
type(initvalue){;}\
} membername
CMEMBER(foo,bar,value);

Although C/C++ addresses many other issues nicely, I have to wonder if the developers of C and C++ have given truly serious thought to development/documentation productivity issues. If anyone has any insights, I can be reached at merrill@park.com.

Ted Merrill
332 Riverside Ave. Ben Lomond CA 95005

I think its fair to say that the developers of both C and C++ have indeed given serious thought to such issues, but there are always tradeoffs involved. Having an automatic header generator such as you describe can ameliorate some of the problems you identify. — pjp