Proposed Small Corrections to Library Draft X3J16/93-0109 WG21/N0316 29 July 1993 P.J. Plauger Here are a number of small changes I feel we should make to the library draft (X3J19/93-0108 WG21/N0315). In the draft, I tried to restrict myself to changing things only when there was an out-and-out contradiction between different parts. The changes I discuss here are more a matter of improved hygiene or more uniform style. >> Add the header I believe we need a central repository for several types, macros, and constants used widely throughout the library. Some obvious candidates are the types size_t, wint_t, and what I called fvoid_t (typedef void fvoid_t(), used to declare various handlers in library support headers). To this we may want to add NPOS and Size_T (if we keep it). Such a header will also serve as a convenient repository for other widely used additions as we run across them. >> Break into smaller pieces. This header is huge, by any standards. I propose we break it up into five pieces: -- for class ios -- for class istream -- for class ostream -- for streamoff, streampos, and streambuf -- for cin, cout, cerr, and clog If we keep the current rules in the library draft about headers including each other, existing programs need not change. Any that includes will perforce quietly include all the others. But newer programs can selectively include pieces, possibly reaping the benefit of improved translation times. Who might benefit? I grant that the typical application has need of cout, so nothing changes for such programs. But embedded applications often make little or no use of the standard I/O machinery. We can make C++ more palatable to that important constituency by lowering the potential overheads for implementations aimed at supporting such programs. I also confess that my personal approach to wide-character I/O (X3J19/93-0112 WG21/N0319) benefits from such a splitting. The wide versions of the various I/O classes want to pick and choose among the existing classes. It would be a pity to require future programmers to drag in all the narrow I/O declarations if all they intend to do is perform wide-character I/O. >> Standardize naming conventions We inherited Bad_cast and Type_info from the extensions WG. I've already observed that the latter should not be put in a header with a name like . There are still enough systems out there to make that underscore problematic. I took the liberty of changing the header to in the library draft. At the very least, I'd like to see the upper case letters go away in the class names. It looks like underscores are widely favored -- they keep popping up in the proposals we accept -- in both member function names and class names. I have a separate proposal concerning exceptions (X3J16/93-0110 WG21/N0317), where I propose some new names for those critters. But one naming issue overlaps class ios -- if we don't change the exception class xalloc, then it matches the static function defined in ios. True, the language allows such reuse, but I find the double meaning unpalatable. I propose we change one or the other, but without any specific candidates (yet). >> Set eofbit uniformly in istream The rules are highly erratic for when an istream member function sets eofbit. As a result, people tend not to put much trust in testing that bit. I think we should state a blanket rule for istream that ensures consistent setting (and clearing of eofbit. >> Set badbit uniformly in ostream Same argument as above for eofbit, only more so. Hitting end of file is problematic in input, what with look ahead, putback, and all. Having a write fail on output is more clear cut. We should spell out blanket rules for setting badbit when writes fail in ostream member functions. >> Reinstate bitmask classes The iostreams proposal describes several classes with common properties. fmtflags, iostate, openmode, and one or two other candidates are all what I chose to call ``bitmask'' types. Through rev. 6, the proposal said that these could be implemented as integers (for backward compatibility), enums, or classes. The idea was that the class implementation could check closely enough to rule out expressions that mix flags of different bitmask types. Rev. 7 eliminated the class option, however, on the basis that enums were now strong enough to provide ``most'' of the checking that is desired. That added strength also REQUIRES that an implementation overload various operators for enums. Otherwise, users have to write type casts all over the place, thereby destroying ALL type safety when mixing flags. Here is what I came up with as a representative bitmask type based on enums. (NB: code is unchecked.): enum bitmask { C0 = 1 <<<< 0, C1 = 1 <<<< 1, C2 = 1 <<<< 2, C3 = 1 <<<< 3, .....}; bitmask& operator&=(bitmask& X, bitmask Y) {X = (bitmask)(X & Y); return (X); } bitmask& operator|=(bitmask& X, bitmask Y) {X = (bitmask)(X | Y); return (X); } bitmask& operator^=(bitmask& X, bitmask Y) {X = (bitmask)(X ^ Y); return (X); } bitmask operator&(bitmask X, bitmask Y) {return ((bitmask)(X & Y)); } bitmask operator|(bitmask X, bitmask Y) {return ((bitmask)(X | Y)); } bitmask operator^(bitmask X, bitmask Y) {return ((bitmask)(X ^ Y)); } bitmask operator~(bitmask X) {return ((bitmask)~X); } int operator==(bitmask X, bitmask Y) {return (X == Y); } int operator!=(bitmask X, bitmask Y) {return (X != Y); } And here is what I came up with for a bitmask class (also unchecked): class bitmask { unsigned int val; enum hidden { H0 = 1 <<<< 0, H1 = 1 <<<< 1, H2 = 1 <<<< 2, H3 = 1 <<<< 3, .....}; bitmask(hidden X) {val = X; } public: bitmask(int X = 0) {val = X == 0 ? 0 : ~0); } bitmask& operator&=(bitmask X) {val &= X.val; return (*this); } bitmask& operator|=(bitmask X) {val |= X.val; return (*this); } bitmask& operator^=(bitmask X) {val ^= X.val); return (*this); } bitmask operator&(bitmask X) {return (bitmask(hidden(val & X.val))); } bitmask operator|(bitmask X) {return (bitmask(hidden(val | X.val))); } bitmask operator^(bitmask X) {return (bitmask(hidden(val ^ X.val))); } bitmask operator~() {return (bitmask(hidden(~val))); } int operator==(bitmask X) {return (val == X.val); } int operator!=(bitmask X) {return (val != X.val); } int operator!() {return (val == 0); } operator int() {return (val != 0); } }; static const bitmask C0(H0); static const bitmask C1(H1); static const bitmask C2(H2); static const bitmask C3(H3); ..... It seems silly to me not to allow this more complete solution, when the amount of code involved is hardly different than for enums. I'd like the class option to be reinstated. >> Drop duplicate declarations in The Standard C library declares five of the functions in in a way that is fine for C but lousy for C++. (The string in question is passed as a const char * argument and received as a char * return value.) At some point in the past, the committee decided to replace things like: char *strchr(const char *s, int c); with: const char *strchr(const char *s, int c); char *strchr( char *s, int c); That's okay, but I question the need for the second variant. True, it permits more C programs to become C++ code without change, but the lack of change propagates a lie. These functions do NOT alter their string arguments and do NOT return pointers to alterable strings, at least in their universe. I have already convinced WG14 not to replicate this mistake in the Normative Addendum. The five analogous functions now have pointers to const for both argument and return value. I suggest we eliminate the second alternative and bring this portion of the Standard C library (under C++) in line with the Normative Addendum. >> ios::fill/setfill should deal in type char It is probably a historical artifact that ios::fill/setfill return type int and takes an int argument. That also simplified life (perhaps) before iomanip became a template. Now, it is simply glaringly untrue that this type is int instead of char. I suggest we fix these functions.