Bobby concludes his stint as C/C++urve author by emptying his mailbag. Look for him in a new guise next month.
Copyright © 1998 Robert H. Schmidt[Since Bobby will be taking over "Questions and Answers" next month, he'll be needing a few questions to answer. Send your C/C++ questions to cujqa@mfi.com. mb]
Well, this is it: my final Learning C/C++urve. Next month I'll start my new Q&A column, the name of which I still haven't picked. A minor detail, I know, but I've been distracted with moving. (And as it turns out, I'm not moving near Dan in Ohio, but moving instead down the road to Redmond.)
No Scott Meyers This Month
Scott Meyers has apparently lost his email privileges, since I've received no spam from him in weeks. As a result, his regular column feature does not appear this month.
Field Width
No, this is not about how big your yard is. "Field width" describes situations like
/* yields "< x>" */ printf("<%5c>", 'x');In this example, the field width is 5. A variation is
printf("<%*c>", 5, 'x');or the more useful
printf("<%*c>", n, 'x');where n must have type int.
In a recent project I had cause to make n quite large (in the thousands). Unfortunately, my Metrowerks library appeared to have a bug: if I made n too big, the printf would instead write
<%*c>That is, it would interpret the format string literally as text. After some trial and error, I found the cutoff point to be n = 509.
A different example makes this more clear:
int i; i = printf("%509c", 'x'); /* i = 509 */ i = printf("%510c", 'x'); /* i = 5 */printf returns the number of characters written. As these examples show, the second call interprets the format as literal text.
Mystery Solved
Metrowerks provides me with Mac OS compilers gratis, so I reckon the least I can do is submit suggestions and bug reports. Accordingly, I filled out the email form and sent in my bug, figuring they'd file it away as a low-priority nuisance.
I was wrong. Instead I received a lovely e-missive from Diligent Reader Vicki Scott, self-proclaimed "Director of Engineering, Libraries and Frameworks" for Metrowerks. In her letter, Vicki confessed:
I love your column and read it (almost) religiously so I feel honored to have a bug report from you! [1]
She went on to hypothesize about the origins of this 509-character limit, agreed it should be documented, and had one of her engineers (Michael Marcotty) investigate. At the end she gave me other contacts within the company, should I ever again have any concerns about the runtime libraries.
Michael called me a couple of days later to this talk over. After some research, he found this within the ANSI C89 Standard discussing fprintf (section 7.9.6.1):
The minimum value for the maximum number of characters produced by any single conversion shall be 509.
Mystery solved! I had never picked up on this limit, and for some reason didn't find it in my research before sending in the report. As Michael also found, the C9X Standard ups the ante (section 7.13.6.1):
The minimum value for the maximum number of characters produced by any single conversion shall be 4095.
(I'll note for completeness that if the C++ Standard has a similar limit, I sure can't find it.)
Above and Beyond
We in the computing press often presume the industry owes us special attention; yet in all honesty, I sent this report to Metrowerks purely as a user of their products, nothing more. I expected, at best, a "thanks for the bug report" response from their technical support. I did not expect an Engineering Director to take such interest, let alone for one of her developers to call me and then do independent research.
Metrowerks isn't perfect otherwise, they'd have no need of bug reports at all but I'm gratified to see at least one group in at least one company cares enough to follow through on this customer's issue, without any provocation.
private = protected?
Stan Kelly-Bootle forwarded me email suggesting that Microsoft's Visual C++ "lets derived classes access base class private members." How this access comes about he didn't say; I tried a small example on MSVC, but couldn't get private to work this way.
If you know the real scoop here, please let me know too. As much as I carp on Microsoft, I don't want to believe their compilers actually allow this.
C++ Multithreading
Be careful what you wish for... In my August column I asked readers for their take on threads in C++. Of the replies I received, I want to briefly note two.
Andrei Alexandrescu of Micro Modeling Associates sent me his proposed Java-ish synchronization primitives for C++. As Andrei wrote in his email:
It's true that C++ wasn't designed for multithreading. But it's equally true that C++ is a broad language and has lots of features that library writers can use in order to accomplish certain behaviors. And I think that having the right library in hand, you can multithread even better than in Java or Ada.
...Let me attach to this message some simple synchronization pseudo-keywords I've put in place. They enable C++ programmers to use Java-like syntax. The library is just a first shot. It's not protected to the issue brought by Mr. Simeonov (static variables in MT environments), but certainly this is not a big issue as simple workarounds can be done (such as including a static plain bool variable and calling a routine that takes care not to initialize things twice).
If you are interested in following up with Andrei, you can contact him at <mailto:AlexandrescuA@MICROMODELING.COM>.
Mike MacFaden of Cabletron Systems, Inc., pointed me to a site [2] describing "Design Patterns for Concurrent, Parallel, and Distributed Systems." In his mail Mike notes
[W]hile in principle [I] agree with your Reflection on C++/MT, I think some good work has been done to make C++/MT it "a lot easier" to work with than C... [Y]ou might want to review the double-checked pattern for initializing file scope variables and show this to your readers in addition to Simeon's template.
I haven't made time to investigate the solutions proposed by either reader, but hope to once I'm settled in from moving.
Implicit Casts
(I first saw the germ of this idea months ago, either on Usenet or some Usenet-recommended website I can no longer recall.)
Last month I briefly mentioned the implicit conversion between string literal arrays and char * in C++.
char *p = "char *p"; // OKThis is one of the many implicit conversions or casts that C++ makes on your behalf, whether you want them or not. Because these conversions are silent, you might not always be aware they are happening. For built-in types, or for classes you don't write, there's nothing you can really do to prevent the conversions but you can make them more apparent.
The most obvious solution is a traditional C-cast:
char *p = (char *) "char *p";This makes clear the conversion between "array of const char" and char *. Unfortunately, it also implies that, were the cast not present, the initialization would not work. Typically we put casts in because our code would not compile without them. To put in a cast that's actually redundant is confusing and probably misleading.
What we really need is a standard way to say "FYI, here's a conversion that happens automatically." An unaesthetic method that works in C or C++ is
#define IMPLICIT_CAST(To, From)\ From char *p = IMPLICIT_CAST(char *, "char *p");The macro's To argument (char * here) is an editorial for the user, and does not factor into the generated code. By the time the macro expansion dust settles, this code is identical to the original
char *p = "char *p";A slightly more elegant implementation is
#define IMPLICIT_CAST(To) char *p = IMPLICIT_CAST(char *) "char *p";Now the macro nets out to nothing at all, as if it were just a comment.
Cast Templates
For all its utility, IMPLICIT_CAST is still a macro, and still looks like the kludge it is. In C, you can't do much better, but C++ offers hope. For inspiration, consider the form of the C++ new-style casts:
const_cast<T>(x);Does this form look at all familiar? Why yes, it looks just like... a call to a template function! It's as if const_cast were really a template declared as something like
template <class To, class From> To const_cast(From const &);We can follow this same pattern to make a new pseudo-cast called implicit_cast, replacing our macro IMPLICIT_CAST with [3]:
template <class To, class From> inline To implicit_cast(From const &x) { return x; }For C++, I find this an elegant and efficient solution. The generated code should be the same with or without the implicit_cast call. Further, this code looks consistent with other new-style casts, and is much more self-documenting than the cast-free alternative.
You can extend this idea to other sorts of casts and conversions:
template <class T> inline T sign_cast(unsigned); int i = sign_cast<int>(123U);While this may seem a useless extension to existing cast technology, consider the specialization
inline int sign_cast(unsigned x) { if (x > INT_MAX) throw std::domain_error; return x; }If the unsigned argument can't fit into a collateral signed object, the pseudo-cast throws an exception (rather than engage in some silent conversion).
I've heard that implicit_cast was considered for inclusion in Standard C++, but it was proposed too late. This is no great hardship, since implicit_cast can be easily implemented in a library, as I've shown here.
qsort Redux
Last month I discussed some bizarre error messages I got from both the Metrowerks and Microsoft compilers. The trouble came when I called qsort with a class member as the comparison function. I left you with
class T { public: void f(); private: int compare(void const *, void const *); }; void T::f() { qsort(NULL, 0, 0, compare); // error }and MSVC complaining
cannot convert parameter 4 from 'int (const void *, const void *)'to
'int (*)(const void *, const void *)'I also asked you to consider
qsort(NULL, 0, 0, &compare);which would appear to fix the MSVC error message, by making sure the fourth parameter to qsort is really a function pointer.
Fortunately, the original error message goes away; unfortunately, a new message takes its place:
'&' : illegal operation on bound member function expressionTime to drop the other shoe. All the problems this month and last stem from a subtle distinction: while qsort is expecting a pointer to a function of a certain signature, and T::compare is a function that appears to have the correct signature, the two can never blend together. Why? Because T::compare doesn't convert into a real function pointer:
typedef int (*fp)(void const *, void const void *); fp p = T::compare; // errorInstead, T::compare converts to a different kind of pointer, what the C++ Standard calls a pointer-to-member:
typedef int (T::*fp)(void const *, void const *); fp p = T::compare; // OKPointers-to-members are hybrids between real pointers and class/struct offsets, with their own variations on normal pointer syntax (witness the T::* instead of the normal * above). I actually have a real reader question that involves pointers-to-members, which I'll publish within the first few installments of my Q&A column.
For the problem of qsort and T::compare, the best answer is to make T::compare a static member. Pointers-to-members refer to instanced members (those with this pointers); static members lack a this pointer, and can be referenced by real pointers:
class T { public: void f(); private: static int compare(void const *, void const *); }; void T::f() { qsort(NULL, 0, 0, compare); // OK }And Speaking of this Pointers...
Another insight courtesy of Stan Kelly-Bootle, who forwarded me an illuminating email thread. The part that interests me here involves section 5.1 paragraph 3 of the C++ Standard:
The keyword this names a pointer to the object for which a nonstatic member function is invoked. The keyword this shall be used only inside a nonstatic class member function body or in a constructor mem-initializer. The type of the expression is a pointer to the function's class, possibly with cv-qualifiers on the class type. The expression is an rvalue.
Read that last sentence again: the expression this is an rvalue. This represents a change from Bjarne Stroustrup's books; in fact, in the mail thread Bjarne expressed surprise at the change. He originally had this as a constant lvalue of type T * const (for a non-const T member) or T const * const (for a const T member).
What practical difference does this make? For starters, if you try something like
T const * const *p = &this;in a member function, you're okay if this is an lvalue, but in trouble if this is an rvalue. (In my testing, Metrowerks treats this as an lvalue, while GNU, MSVC, and EDG consider this an rvalue.)
Beyond code changes, this forces a change in my conceptual model. When Dan Saks and I teach C programmers introductory C++, we show them the benefits of making pseudo-members in C with an explicit this-style pointer parameter. We later map that to C++ with its implicit this pointer pseudo-parameter.
Such mapping from C to C++ works if this is a real pointer lvalue, just like an explicit pointer parameter would be. But now this is a symbol for an rvalue, just like the symbol 123. However, where 123 has the same type and value in all contexts, this has different types and different values in different contexts.
I'm not really sure why the committee made this change. To me, it adds an unnecessary complication to the conceptual model, and may unnecessarily break existing code, without providing tangible benefit to C++ programmers. But as I'm not on the committee, I have to grant the benefit of the doubt here, and believe there is a compelling reason for the switch.
Foreshadowing
Instead of emailing questions directly to my personal account, send them instead to cujqa@mfi.com. That way, if I miss an issue, someone else can silently field your queries; also, the distinct "To:" line lets me easily filter questions into a special email folder.
My preference is to handle a larger number of questions with shorter answers. To steal a cliche, I'd rather teach more of you how to fish than give a couple of you a bushel basket of fish. I also think I'll have more fun writing my column this way.
You may wonder about the title of this month's column. In astronomy, "second contact" is the instant when the moon fully covers the sun during a total solar eclipse. I find this a fitting metaphor, as I now completely eclipse "The Learning C/C++urve." In that sense, next month's column will represent third contact: the instant when the metaphorical moon starts uncovering my new column's sun.
I'll see you on the Dark Side of the Moon.
Notes
[1] That line alone grants Vicki lifetime Diligent Reader status although I have to wonder about someone "honored" to receive a report that says "your product is broken." It's like a Spice Girls fan saying to Ginger, oops, Scary Spice, "I'm so honoured it was your car that bashed mine in the car park!"
[2] <http://www.cs.wustl.edu/~schmidt/patterns-ace.html>. Look for the section titled "Initialization Patterns."
[3] I'm cheating here, since this definition of implicit_cast won't work with our char *p = "char *p" example. I leave it as an exercise for the student to create an implicit_cast that works correctly in all contexts.
Bobby Schmidt is a freelance writer, teacher, consultant, and programmer. He is also a member of the ANSI/ISO C standards committee, an alumnus of Microsoft, and an original "associate" of (Dan) Saks & Associates. In other career incarnations, Bobby has been a pool hall operator, radio DJ, private investigator, and astronomer. You may summon him at 14518 104th Ave NE Bothell WA 98011; by phone at +1-425-488-7696, or via Internet e-mail as rschmidt@netcom.com.