C H A P T E R  4

Using Iostreams and Library Headers

This chapter explains the library and header file changes that were implemented in the C++ 5.0 compiler. You must consider these changes when migrating code that was intended for C++ 4 compilers for use with the C++ 5 compilers.


4.1 Iostreams

The C++ 4.2 compiler implemented classic iostreams, which never had a formal definition. The implementation is compatible with the version released with Cfront (1990), with some bug fixes.

Standard C++ defines a new and expanded iostreams (standard iostreams). It is better defined, feature-rich, and supports writing internationalized code.

In compatibility mode, you get classic iostreams, the same version supplied with the C++ 4.2 compiler. Any existing iostream code that works with the 4.2 compiler should work exactly the same way when compiling in compatibility mode (-compat[=4]).



Note - Two versions of the classic iostream runtime library are supplied with the compiler. One version is compiled with the compiler in compatibility mode, and is the same as the library used with C++ 4.2. The other version is compiled from the same source code, but with the compiler in standard mode. The source-code interface is the same, but the binary code in the library has the standard-mode ABI. See Section 1.3, Binary Compatibility Issues.



In standard mode, you get standard iostreams by default. If you use the standard form of header names (without ".h"), you get the standard headers, with all declarations in namespace std.

Four of the standard headers are also provided in a form ending with ".h" that makes the header names available in the global namespace via using-declarations.

These headers are a Sun extension, and code that depends on them might not be portable. These headers allow you to compile existing (simple) iostream code without having to change the code, even though standard iostreams are used instead of classic iostreams. For example, CODE EXAMPLE 4-2 will compile with either classic iostreams or with the Sun implementation of standard iostreams.


CODE EXAMPLE 4-1 Using Standard iostream Name Forms
#include <iostream>
int main()
{
    std::cout << "Hello, world!" << std::endl;
}

CODE EXAMPLE 4-2 Using Classic iostream Name Forms
#include <iostream.h>
int main()
{
    cout << "Hello, world!" << endl;
}

 

Not all classic iostream code is compatible with standard iostreams. If your classic iostream code does not compile, you must either modify your code or use classic iostreams entirely.

To use classic iostreams in standard mode, use the compiler option -library=iostream on the CC command line. When this option is used, a special directory is searched that contains the classic iostream header files, and the classic iostream runtime library is linked with your program. You must use this option on all compilations that make up your program as well as on the final link phase or you will get inconsistent program results.



Note - Mixing old and new forms of iostreams--including the standard input and output streams cin, cout, and cerr--in the same program can cause severe problems and is not recommended.



With classic iostreams, you can write your own forward declarations for iostream classes instead of including one of the iostream headers. For example:


CODE EXAMPLE 4-3 Forward Declaration With Classic iostreams
// valid for classic iostreams only
class istream;
class ostream; 
class Myclass;
istream& operator>>(istream&, MyClass&);
ostream& operator<<(ostream&, const MyClass&);

This approach will not work for standard iostreams, because classic names (istream, ofstream, streambuf, and so forth) are not the names of classes in standard iostreams. They are typedefs referring to specializations of class templates.

With standard iostreams, you cannot provide your own forward declarations of iostream classes. Instead, to provide correct forward declarations of the iostream classes, include the standard header <iosfwd>.


CODE EXAMPLE 4-4 Forward Declaration With Standard iostreams
// valid for standard iostreams only
#include <iosfwd>
using std::istream;
using std::ostream;
class MyClass;
istream& operator>>(istream&, MyClass&);
ostream& operator<<(ostream&, const MyClass&);

To write code that will work with both standard and classic iostreams, you can include the full headers instead of using forward declarations. For example:


CODE EXAMPLE 4-5 Code for Both Classic and Standard iostreams
// valid for classic and standard iostreams with Sun C++
#include <iostream.h>
class MyClass;
istream& operator>>(istream&, MyClass&);
ostream& operator<<(ostream&, const MyClass&);


4.2 Task (Coroutine) Library

The coroutine library, accessed through the <task.h> header, is no longer supported. Compared to the coroutine library, Solaris threads are better integrated into the language development tools (particularly the debugger) and the operating system.


4.3 Rogue Wave Tools.h++

The C++ compiler contains two versions of the Tools.h++ library:

To use the standard iostreams version of the library, use the -library=rwtools7_std option.

Refer to the C++ User's Guide or the CC(1) man page for more information about accessing Tools.h++.


4.4 C Library Headers

In compatibility mode, you use the standard headers from C as before. The headers are in the /usr/include directory, supplied with the Solaris software version you are using.

The C++ standard has changed the definition of the standard C headers.

For clarification, the headers being discussed are the 17 headers defined by the ISO C standard (ISO 9899:1990) plus its later addendum (1994):


<assert.h>  <ctype.h>   <errno.h>   <float.h>   <iso646.h> 
<limits.h>  <locale.h>  <math.h>    <setjmp.h>  <signal.h> 
<stdarg.h>  <stdio.h>   <stdlib.h>  <string.h>  <time.h>  
<wchar.h>   <wctype.h>

The hundreds of other headers that reside in and below the /usr/include directory are not affected by this language change because they are not part of the C language standard.

You can include and use any of these headers in a C++ program the same as in previous versions of Sun C++, but some restrictions apply.

The C++ standard requires that the names of types, objects, and functions in these headers appear in namespace std as well as in the global namespace. If you compile in standard mode, you must use the version of these headers that is supplied with the C++ compiler. If you use the wrong headers, your program can fail to compile or link.

With the Solaris 8 operating environment, the standard C headers in /usr/include are correct for C++, and are used by the C++ compiler automatically. That is, if you write


#include <stdio.h>

you will get the Solaris version of stdio.h when compiling on the Solaris 8 operating environment. With the Solaris 8 operating environment, there is no restriction against using the explicit path name in the include statement. However, use of path names, such as </usr/include/stdio.h>, does make the code unportable.

The C++ standard also introduces a second version of each of the 17 standard C headers. For each header of the form <NAME.h>, there is an additional header of the form <cNAME>. That is, the trailing ".h" is dropped, and a leading "c" is added. Some examples: <cstdio>, <cstring>, <cctype>.

These headers contain the names from the original form of the header but appear only in namespace std. An example of use according to the C++ standard is:


#include <cstdio>
int main() { 
    printf("Hello, ");       // Error, printf unknown
    std::printf("world!\n"); // OK
}

Because the code uses <cstdio> instead of <stdio.h>, the name printf appears only in namespace std and not in the global namespace. You must either qualify the name printf, or add a using-declaration:


#include <cstdio>
using std::printf;
int main() {
    printf("Hello, ");       // OK
    std::printf("world!\n"); // OK
}

The standard C headers in /usr/include contain many declarations that are not allowed by the C standard. The declarations are there for historical reasons, primarily because UNIX systems have traditionally had the extra declarations in those headers, or because other standards (like POSIX or XOPEN) require them. For continued compatibility, these extra names appear in the Sun C++ versions of the <NAME.h> headers, but only in the global namespace. These extra names do not appear in the <cNAME> versions of the headers.

Because these new headers have never been used in any previous program, there is no compatibility or historical issue. Consequently, you might not find the <cNAME> headers to be useful for general programming. If you want to write maximally portable standard C++ code, however, be assured that the <cNAME> headers do not contain any unportable declarations. The following example uses <stdio.h>:


#include <stdio.h>
extern FILE* f; // std::FILE would also be OK
int func1() { return fileno(f); }      // OK
int func2() { return std::fileno(f); } // Error

The following example uses <cstdio>:


#include <cstdio>
extern std::FILE* f; // FILE is only in namespace std
int func1() { return fileno(f); }      // Error
int func2() { return std::fileno(f); } // Error

Function fileno is an extra function that for compatibility continues to appear in <stdio.h>, but only in the global namespace, not in namespace std. Because it is an extra function, it does not appear in <cstdio> at all.

The C++ standard allows using both the <NAME.h> and <cNAME> versions of the standard C headers in the same compilation unit. Although you probably would not do this on purpose, it can happen when you include, for example, <cstdlib> in your own code, and some project header you use includes <stdlib.h>.


4.5 Standard Header Implementation

The C++ User's Guide explains in detail how standard headers are implemented along with the reasons for the implementation method. When you include any of the standard C or C++ headers, the compiler actually searches for a file with the specified name suffixed by ".SUNWCCh". For example, <string> causes a search for <string.SUNWCCh> and <string.h> causes a search for <string.h.SUNWCCh>. The compiler's include directory contains both spellings of the names, and each pair of spellings refers to the same file. For example, in directory include/CC/Cstd you find both string and string.SUNWCCh. They refer to the same file, the one you get when you include <string>.

In error messages and debugger information, the suffix is suppressed. If you include <string>, error message and debugger references to that file mention string. File dependency information uses the name string.SUNWCCh to avoid problems with default makefile rules regarding unsuffixed names. If you want to search for just header files (using the find command, for example) you can look for the .SUNWCCh suffix.