PRODUCT : Borland C++ NUMBER : 1384 VERSION : ALL OS : ALL DATE : October 25, 1993 PAGE : 1/7 TITLE : Streams: Part 2 -- Built-in Types and Manipulators Streams : Part 2 : Built-in Types and Manipulators The built-in types are int, long, float and double, and all variations of these types i.e. unsigned int. For all these types you simply need to use the stream desired. Here is an example: #include void Ex1() { int anInteger = 5; cout << anInteger; } The display shows 5 and then the program returns to the next instruction. What actually happened is a little different. The following account of the process that is performed is only ment to be an explanation of the complexity of what happens when you use streams. You will not need to memorize or understand the process in order to use streams. The object "cout" is an instantiation of the ostream_withassign class with a pointer to a filebuf, if we look we see that the class doesn't have an insertion operator. If we look deeper, by looking at an ostream class, which ostream_withassign is derived from, we discover the following: // Formatted insertion operations // insert the character ostream _FAR & _Cdecl operator<< ( signed char); ostream _FAR & _Cdecl operator<< (unsigned char); // for the following, insert character representation of // numeric value ostream _FAR & _Cdecl operator<< (short); ostream _FAR & _Cdecl operator<< (unsigned short); ostream _FAR & _Cdecl operator<< (int); PRODUCT : Borland C++ NUMBER : 1384 VERSION : ALL OS : ALL DATE : October 25, 1993 PAGE : 2/7 TITLE : Streams: Part 2 -- Built-in Types and Manipulators ostream _FAR & _Cdecl operator<< (unsigned int); ostream _FAR & _Cdecl operator<< (long); ostream _FAR & _Cdecl operator<< (unsigned long); ostream _FAR & _Cdecl operator<< (float); ostream _FAR & _Cdecl operator<< (double); ostream _FAR & _Cdecl operator<< (long double); // insert the null-terminated string ostream _FAR & _Cdecl operator<< (const signed char _FAR *); ostream _FAR & _Cdecl operator<< (const unsigned char _FAR *); The insertion operator has been overloaded for each of the built-in types. So in the above example the function declared as: ostream _FAR & _Cdecl operator<< (int); was called. This function is an inline function defined as: inline ostream _FAR & _Cdecl ostream::operator<< (int _i) { return *this << (long) _i; } and since integers can be safely promoted to longs, the integer is casted to a long and the insertion operator is invoked again. This time calling the long version, declared as: ostream _FAR & ostream::operator<< (long l) this function performs several things; determines base conversion, checks for negative sign, and calculates the prefix, next it converts the number to a character buffer and lastly calls another function, outstr, to insert the characters into the output buffer. The function outstr checks for a prefix, field padding and a suffix. Displaying the buffer to the screen is done by calling osfx inside the outstr module, the osfx function calls do_osfx which calls flush. The flush function then calls sync. The sync function is a member function of the filebuf class, and calls write. The write function finally puts the output on the screen. The checking mentioned previously, check the state of the formatting flags (see part 1, ios class). Depending on the PRODUCT : Borland C++ NUMBER : 1384 VERSION : ALL OS : ALL DATE : October 25, 1993 PAGE : 3/7 TITLE : Streams: Part 2 -- Built-in Types and Manipulators values of these flags the output or input will change. The way in which we change the flags, and as a result change the formatting, is to use manipulators. One thing to note before delving into manipulators is the return type of the operator. The fact that it is a reference to an ostream object allows concatenation of commands, for example: #include void Ex2() { int anInteger = 5; cout << "This is an integer " << anInteger << "."; } The order is in which the stream requests are processed is from left to right. First the call to the member function in "cout", matching a const character string insert operator, is processed. That call returns a reference to the cout object, therefore, the member function in "cout", matching an integer insert operator, is called and again that call returns a reference to the cout object. Lastly the member function in "cout" matching a const character once again is called. The output of Ex2 is shown below: This is an integer 5. Modifying the behavior of a stream is done in one of four methods. First, using the functions provided in the "ios" class object. Example 3 demonstrates this: void Ex3() { cout.setf(ios::hex); cout << 20; } This is highly irregular, the common method used is manipulators. The second and third methods use manipulators. Manipulators are the commonly used convention. Using manipulators doesn't improve performance and doesn't reduce code size. The reason PRODUCT : Borland C++ NUMBER : 1384 VERSION : ALL OS : ALL DATE : October 25, 1993 PAGE : 4/7 TITLE : Streams: Part 2 -- Built-in Types and Manipulators manipulators are used is convience. Manipulators are broken into two categories, manipulators and parameterized manipulators. Here are the prototypes for manipulators: // manipulators ostream _FAR & _Cdecl operator<<(ostream _FAR & (_Cdecl *_f)(ostream _FAR &)); ostream _FAR & _Cdecl operator<<(ios _FAR & (_Cdecl *_f)(ios _FAR &)); As you can see, the prototypes are similar to the built-in types prototypes except that they take function pointers as arguments. We can then draw the conclusion that manipulators are in fact functions. Let's look at the following example using a manipulator to output a hexadecimal value from a decimal value. Also note using manipulators requires another include file iomanip.h: #include #include void Ex4() { cout << hex << 10; } The output of Ex4 is shown below: a The hexadecimal equivalent of decimal 10, a, is shown. Tracing through the source code we discover what happens. The hex used in the stream statement is in fact the address of a function. This function which is a member of the ios class invokes the overloaded insertion operator which is defined as taking a function pointer as its argument. This function then cleverly uses the passed function pointer to invoke the function itself. The code for this procedure follows: ostream _FAR & ostream::operator<< (ios _FAR & (*f)(ios _FAR &)) { (*f)(*((ios*)this)); return *this; PRODUCT : Borland C++ NUMBER : 1384 VERSION : ALL OS : ALL DATE : October 25, 1993 PAGE : 5/7 TITLE : Streams: Part 2 -- Built-in Types and Manipulators } In our example the function which gets invoked from this function is: ios _FAR & hex(ios _FAR & s) { s.setf(ios::hex, ios::basefield); return s; } This function simply sets the bits in the appropriate variable, so that when the insertion operator for a long is called (see above) the variable tested to determine the base of the value will properly indicate that we need to convert the output to hexadecimal. The following formatting flags are used to determine how the stream will be formatted. The current state of the streams formatting is kept in the variable x_flags, which is of type long and is a member of the ios class. Here is a list of values and corresponding flags: // formatting flags enum { skipws = 0x0001, // skip whitespace on input left = 0x0002, // left-adjust output right = 0x0004, // right-adjust output internal = 0x0008, // padding after sign or base indicator dec = 0x0010, // decimal conversion oct = 0x0020, // octal conversion hex = 0x0040, // hexadecimal conversion showbase = 0x0080, // use base indicator on output showpoint = 0x0100, // force decimal point (floating output) uppercase = 0x0200, // upper-case hex output showpos = 0x0400, // add '+' to positive integers scientific= 0x0800, // use 1.2345E2 floating notation fixed = 0x1000, // use 123.45 floating notation unitbuf = 0x2000, // flush all streams after insertion stdio = 0x4000 // flush stdout, stderr after insertion }; The final method of modifying the format flags, and sometimes the only way to do what you need, is to call other provided PRODUCT : Borland C++ NUMBER : 1384 VERSION : ALL OS : ALL DATE : October 25, 1993 PAGE : 6/7 TITLE : Streams: Part 2 -- Built-in Types and Manipulators manipulator functions directly. The following are additional manipulator functions: // set the conversion base to 0, 8, 10, or 16 smanip_int _Cdecl _FARFUNC setbase(int _b); // clear the flags bitvector according to the bits set in b smanip_long _Cdecl _FARFUNC resetiosflags(long _b); // set the flags bitvector according to the bits set in b smanip_long _Cdecl _FARFUNC setiosflags(long _b); // set fill character for padding a field smanip_int _Cdecl _FARFUNC setfill(int _f); // set the floating-point precision to n digits smanip_int _Cdecl _FARFUNC setprecision(int _n); // set the field width to n smanip_int _Cdecl _FARFUNC setw(int _n); Note: The field width is reset to 0 after each stream insertion. Other flags maintain their value until explicitly reset. These functions, in addition to modifying the x_flags, also modify these other flags which effect formatting: int x_precision; // floating-point precision on output int x_width; // field width on output int x_fill; // padding character on output Here is an example using a parameterized call to a function: #include #include void Ex4() { cout << setiosflags(ios::showbase) << hex << 0x16 << endl; cout << 0x17 << endl; cout << resetiosflags(ios::showbase|ios::hex) << 0x17 << endl; } PRODUCT : Borland C++ NUMBER : 1384 VERSION : ALL OS : ALL DATE : October 25, 1993 PAGE : 7/7 TITLE : Streams: Part 2 -- Built-in Types and Manipulators The output displayed is: 0x16 0x17 22 This is the result of using the hex manipulator with the showbase flag. Since the manipulators change the flags permanently the second statement is also displayed with the hexadecimal and showbase formatting. The last statement resets the flags we had set earlier, this causes the output to default back to displaying a decimal value. Part 2 covered the concepts, terminology when dealing with built in types and manipulators. For more information on streams please see the following list: Part 1 : Introduction Part 2 : Built-in Types and Manipulators Part 3 : Error Checking Part 4 : User Classes Part 5 : File Input and Output Part 6 : In-Memory Formatting Part 7 : Printing DISCLAIMER: You have the right to use this technical information subject to the terms of the No-Nonsense License Statement that you received with the Borland product to which this information pertains.