inst182@tuvie (Inst.f.Techn.Informatik) (10/16/89)
It's high time the problem of formatted output be addressed. The current method of providing formatted output ( the form() function with format strings a` la `C') is not a particularly logical and/or efficient method. Here are some reasons AGAINST using form(): + no optimization can be done; if you want to output even a trivial thing like form("d=%d\n",d) and you have to parse the output string over and over again. + since form() is declared as somthing like char *form(char *format, ...); no typechecking can be done (neither at compile- nor at runtime). Things like { int *a; *a = 1; printf("%d\n",a); } have happend to almost anyone of us, we dont want this to happen in `C++' as well. + Extending form to handle user-defined types is impossible. (Ok, maybe *YOU* fancy writing a new form() function for each and every of your programs. Most of us don't.) So if form() becomes a standard, it will mess up one of the main ideas that seem to be behind the development of C++, namely that you should be able to handle your own classes no different from the standard classes. One possible solution is to introduce a new operator that handles formatting. A nice thing would be to write: cout <<4:2<<5.2:6:4; The precedence would have to be between the likes of + and <<. But obviously the colon is not suitable because of its use in ?:, invalidating all C++ code thus far written. Moreover, `:' is difficult to spot. Two unused ASCII characters remain: `@' and `#'. So what about cout <<4#2<<5.2#6#4; or cout <<4@2<<5.2@6@4; The definition of # could be something like: operator#: int x int -> int_form operator#: int_form x int -> int_form_form operator<<: ostream& x int_form_form -> ostream& and a constructor int_form_form: int_form -> int_form_form It is also possible to use the new operator to also specify a format string: cout <<4#'x'#2<<5.2#'f'#6#4; ( The format character would have to go in the first place, because the 2nd and 3rd call to operator# are optional.) Other possibilities involve using both `#' and `@': cout <<4#2@'x'; or cout <<4@'x'#2; This also handles format characters. The advantages of this method are: + Typechecking is possible at compile-time. + This concept can be used with user-defined types as well. + The use of a format operator logically extends the idea of using input/output operators. + Optimization is possible: If the operator# functions are inlined, constant propagation can be used to select the most efficient formatting algorithm. (NOt the whole operator# needs to be inline code, but only some kind of switch() statement which selects the right call to the formatting function.) + It doesn't involve writing a lot of extra chars. (That's important!!! :-) [ You also could use a format(int val, int fchar, int x1 = defvalue1, int x2 = defvalue2). But this involves a lot more characters. ] Some arguments against this idea and possible answers: + Who needs new operators, this is supposed to be an OO-C, not a new language. ++ The new() and delete() operators didn't exist in C either. + We don't want to mess up the language with output, format, i/o operations. That's library stuff. ++ Nor do I. Let's put the implementation of the operator in a library (see also: optimization when inlining). If someone wants to write her/his own output functions, he needs not include the header file. The operator per se is value-free. Only the implementation makes it a formatted output operator. If you want it do have any other meaning, you can implement it, to do whatever you like. Just like <<. +----------------------------------------------------------------------------+ | ____ ____ | | | / / / / / | Michael K. Gschwind | | / / / / / | | | ---/ |----------------------------------------------------| | / | | | ___/ | ...!uunet!mcvax!tuvie!eimoni!gschwind | | | | +----------------------------------------------------------------------------+
carroll@paul.rutgers.edu (V. I. Lenin) (10/16/89)
> It's high time the problem of formatted output be addressed. Not only has it been addressed, but it has been solved in 2.0's iostream library. > The current method of providing formatted output ( the form() > function form() is not the current method. 2.0 doesn't even have it, for precisely the reasons you state. As I've discussed before on this group, the perfectly elegant solution in 2.0 is to use ostrstreams. Ostrstreams have all the properties you desire: > + Typechecking is possible at compile-time. True for ostrstreams. > + This concept can be used with user-defined types as well. True for ostrstreams. Just define a new ostream insertion operator, and you can use that operator to insert into any kind of stream you like: cout, core buffers, whatever. > + The use of a format operator logically extends the idea of > using input/output operators. In 2.0 iostreams, format "operators" are called manipulators. They get inserted (more correctly, the illusion is that they get inserted) into streams just like real data, e.g.: cout << hex << 12 << dec << 12 << endl; outputs "c12". > + Optimization is possible:... Insertion operators and manipulators can be inlined like anything else. > + It doesn't involve writing a lot of extra chars. (That's > important!!! :-) You don't gotta write *anything* extra with 2.0 iostreams. Just define the new insertion operator, and everybody who needs it inherits it automatically. -- martin
henry@utzoo.uucp (Henry Spencer) (10/17/89)
In article <738@tuvie> inst182@tuvie (Inst.f.Techn.Informatik) writes: > + no optimization can be done; if you want to output > even a trivial thing like form("d=%d\n",d) and you > have to parse the output string over and over again. Why? All it takes is a compiler that knows about library functions to optimize this. (No, this is not "contrary to the spirit of C"; there are already C compilers that do it, and ANSI C has blessed it.) > + since form() is declared as somthing like > char *form(char *format, ...); > no typechecking can be done... Again, dunno about C++, but there are C compilers that type-check calls to printf(), so form() should be checkable. > + Extending form to handle user-defined types is impossible. This is *probably* true. -- A bit of tolerance is worth a | Henry Spencer at U of Toronto Zoology megabyte of flaming. | uunet!attcan!utzoo!henry henry@zoo.toronto.edu
thoth@springs.cis.ufl.edu (Robert Forsman) (10/21/89)
I agree, form looks evil and brings up the specter of printf. > From: inst182@tuvie (Inst.f.Techn.Informatik) > One possible solution is to introduce a new operator that handles > formatting. . . . Two unused ASCII characters remain: > `@' and `#'. So what about > cout <<4#2<<5.2#6#4; > or > cout <<4@2<<5.2@6@4; > From: carroll@paul.rutgers.edu (V. I. Lenin) > In 2.0 iostreams, format "operators" are called manipulators. They > get inserted (more correctly, the illusion is that they get inserted) > into streams just like real data, e.g.: > > cout << hex << 12 << dec << 12 << endl; I assume you guys have already discussed the idea of an explicit conversion and trashed it. String decimalString(int value,int width); String hexString(int value,int width); String decimalString(float value,int width,int decimals=-1); (and for those manic math lovers like me :^) String anybaseString(float value,unsigned int base, int width,int decimals=-1); cout << decimalString(4,0); cout << "Time to impact :" << decimalString(boom,1,2) << '\n'; Needless to say you could provide a simple subset for users with much shorter names and let them write their own anybaseString(). ( side note. I had trouble with that '\n' being converted to an int before reaching the ostream. I decided to typecast it and that solved the problem. It was probably just a compiler "bug") -- (U. of F., the only place where the CIS department has its own beach :)
bright@Data-IO.COM (Walter Bright) (10/24/89)
< One possible solution is to introduce a new operator that handles < formatting. . . . Two unused ASCII characters remain: < `@' and `#'. So what about < cout <<4#2<<5.2#6#4; < or < cout <<4@2<<5.2@6@4; This is supposed to be more readable than printf? Bzzzt! Next!
jss@jra.ardent.com (Jerry Schwarz (Compiler)) (10/24/89)
In article <THOTH.89Oct20212523@springs.cis.ufl.edu> thoth@springs.cis.ufl.edu (Robert Forsman) writes: > > I assume you guys have already discussed the idea of an explicit >conversion and trashed it. > > String decimalString(int value,int width); > String hexString(int value,int width); > String decimalString(float value,int width,int decimals=-1); >(and for those manic math lovers like me :^) > String anybaseString(float value,unsigned int base, > int width,int decimals=-1); > > cout << decimalString(4,0); > cout << "Time to impact :" << decimalString(boom,1,2) << '\n'; > Indeed, more than discussed. This is essentially the method used by the AT&T 1.2 stream package. There are several problems with it. Where does the space come from for the string? How about all the twiddles on formatting available in stdio? (e.g. the case of the alphabetic "digits" in a hex number) But you don't have to choose. Its fairly easy to implement the functionality of the above without intermediate strings. One (among several choices) is class decimalString() { public: decimalString(int v, int w) : value(v), width(w) { } int value ; int width ; } ; ostream& operator<< (ostream& o,decimalString& s) { int f = o.flags(); o << dec << setw(s.w) << s.value ; o.setf(p,ios::basefield); return o ; } There is a philosopical point here. In C the builtin types are special. Its perfectly reasonable to have a C I/O library that has a lot of formatting stuff for them. In C++ user defined classes are just as important as the builtin types. What is important is not that there be a lot of formatting stuff for the builtin types, but that there be a mechanism for extending the I/O. In C++ it is usually much better to determine styles of printing, widths and the like based on the role (type) type of the data rather than specifying it at each individual I/O statement. In hindsight I think I put too much special stuff in the iostream library for the builtin types. Historically, what happened was that the builtin type stuff was done first, and only much later did I develop the extensibility features (such as xalloc). > ( side note. I had trouble with that '\n' being converted to an int >before reaching the ostream. I decided to typecast it and that solved >the problem. It was probably just a compiler "bug") This is a well known "feature:-)" in AT&T 1.2. Its been fixed in AT&T 2.0. Jerry Schwarz Disclaimer: I'd usually run code like the above before putting it in a netnews item. In this case I didn't.