Updated 2004/12/10

Sun[tm] Studio 10: C++ FAQ



Contents

  1. New Questions For This Release


  2. Versions, Patches, and Support


  3. Compiler Compatibility


  4. Coding and Diagnostics


  5. Library Compatibility


  6. Compile-Time Performance


  7. Run-Time Performance




A. New Questions For This Release



A. Versions, Patches, and Support

  1. How can I dependably identify the C++ compiler in each new release?
     
  2. What is the difference between standard and classic I/O streams? Can you recommend text books on the subject?
     
  3. How can I tell which C++ compiler versions are compatible?
     
  4. What RogueWave libraries are "certified" for use with Sun Studio 9?
     
  5. How can I figure out which patches exist and what problems are solved in the current patches?
     
  6. Do I need to have patches to libC.so.5 and libCrun.so.1?
     

  1. How can I dependably identify the C++ compiler in each new release?

    Every compiler predefines some macros that identify it. Compiler vendors tend to keep these predefined macros stable from release to release, and we in particular document them as a stable public interface.

    A good way to find out what compiler you have is to write a small program that tests for predefined macros and outputs a string suitable for your intended use. You can also write a pseudo-program and compile it with -E (or the equivalent for other compilers).

    See 'macros' in the index of the C++ User's Guide for a list of predefined C++ compiler macros. In particular, the value of __SUNPRO_CC, which is a three-digit hex number. The first digit is the major release. The second digit is the minor release. The third digit is the micro release. For example, C++ 5.6 is 0x560.

    Here are predefined macros of interest:

     
    #ifdef __sun
           Sun compiler
    #endif
    #ifdef __SUNPRO_C
           Sun C compiler /* __SUNPRO_C value is the version number */
    #endif
    #ifdef __SUNPRO_CC
           Sun C++ compiler /* __SUNPRO_CC value is the version number */
    #endif
    #ifdef __sparc
           generate code for SPARC architecture
    #endif
    #ifdef __sparcv9
           generate code for 64-bit SPARC architecture
    #endif
    #ifdef __i386
           generate code for Intel architecture
    #endif

  2. What is the difference between standard and classic I/O streams? Can you recommend text books on the subject?

    The design and implementation of the two libraries are entirely different. The programming interface for simple I/O is quite similar. For more complex operations, such as writing your own stream classes or manipulators, it is quite different.

    The classic iostream library in this version is compatible with the version shipped with C++ 3.x and 4.x. In addition to the documentation provided with this release, here is a reference book:

    • Steve Teale
      C++ IOStreams Handbook
      Addison-Wesley 1993

    The standard iostream library is described by the C++ Standard. In addition to the documentation provided with this release, here are two good references:

    • Nicolai Josuttis
      The C++ Standard Library
      Addison-Wesley 1999
      (A tutorial on the entire C++ standard library)


    • Angelika Langer and Klaus Kreft
      Standard C++ IOStreams and Locales
      Addison-Wesley 1999
      (A tutorial on just iostreams and locales.)

    Source code for implementing simple I/O looks the same for both versions of iostreams. To ease the transition, we provide the non-standard headers <iostream.h>, <fstream.h>, and <strstream.h> for standard iostreams. They provide a similar set of declarations in the global namespace as are found in classic iostreams. If you use these headers, you will get the standard iostreams by default. To get classic iostreams, you should compile and link with -library=iostream.

    For example, the following code works with both classic and standard iostreams using our compiler, although not with all compilers.

    #include <iostream.h>
    
    class myclass {
    public:
            myclass(int i) : k(i) { }
            friend ostream& operator<<(ostream&, const myclass&);
    private:
            int k;
    };
    
    // user-written output operator
    ostream& operator<<(ostream& os, const myclass& m)
    {
        os << m.k;
        return os;
    }
    
    int main()
    {
        // simple I/O using cout, cin
        cout << "Enter a number: " << endl;
        int val;
        if( ! (cin >> val) ) {
            cout << "Invalid entry, using zero" << endl;
            val = 0;
        }
    
        // using the user-written output operator
        myclass m(val);
        cout << "Value is " << m << endl;
    }
    

    This code compiles and runs using Sun compilers in any of these ways:

    example% CC example.cc # standard mode with standard iostreams
    example% CC -library=iostream example.cc # standard mode with classic iostreams
    example% CC -compat=4 example.cc # C++ 4.2 compatbility mode
    
  3. How can I tell which C++ compiler versions are compatible?

    First, a definition: "Upward compatible" means that object code compiled with an older compiler can be linked with code from a later compiler, as long as the compiler that is used in the final link is the latest compiler in the mix.

    The C++ 4.0, 4.1, and 4.2 compilers are upward compatible. (There are some "name mangling" issues among the compiler versions that are documented in the C++ 4.2 manuals.)

    The C++ 5.0 through 5.6 compilers in compatibility mode (-compat) are upward compatible with the 4.2 compiler. The actual object code from C++ 4.2 and versions 5.0 through 5.6 is fully compatible, but debugging information (stabs) emitted by later compilers is not compatible with earlier debuggers.

    The C++ 5.0 through 5.6 compilers in default standard mode are upward compatible. The actual object code is fully compatible, but debugging information (stabs) emitted by later compilers is not compatible with earlier debuggers.

  4. What RogueWave libraries are "certified" for use with Sun Studio 9?

    We cannot reliably track which vendors have certified which versions of their products for use with which versions of our compilers. And making sure this FAQ is always up to date would be harder still. You have to check with the vendor to find out whether they have tested their product with any particular version of the C++ compiler.

    However, some RogueWave libraries ship with our compilers, and we implicitly certify that the versions we ship work together.

  5. How can I figure out which patches exist and what problems are solved in the current patches?

    For the most up-to-date information on product patches, check the Developer's Portal at
    ( http://developers.sun.com/prodtech/cc).

    Product patches can be downloaded from http://sunsolve.sun.com.

  6. Do I need to have patches to libC.so.5 and libCrun.so.1?

    Typically, the Solaris[tm] operating system ships with the most recent versions of these libraries. However, due to bug fixes and some performance improvements, there are often patches to these libraries. These patches are always cumulative and always backward compatible, so it is a good idea to pick up the latest patch available from http://sunsolve.sun.com. The following table shows the current matrix of patch IDs as of November 2004.

    Please check for the latest package in the database. The package is called SUNWlibC (32bit) and SUNWlibCx(64bit). Use the Advanced Search page and look for libCrun. Be sure to limit the search to 'Patch Descriptions' instead of 'All Support Resources' which is the default.

    Table 1: libC and libCrun Patches
    Patch ID Solaris
    Operating System
    Architecture

    108434-18

    8

    SPARC/v8

    108435-18

    8

    SPARC/v9

    108436-16

    8

    x86

    111711-12

    9

    SPARC/v8

    111712-12

    9

    SPARC/v9

    111713-09

    9

    x86



B. Compiler Compatibility

  1. I recently patched my Solaris[tm] Operating System and now my code won't compile. What happened?
     
  2. Can I mix compatibility mode (-compat) code with standard mode code?
     
  3. How do I mix C++ or C programs with F77, F90, or F95 programs?
     

  1. I recently patched my Solaris OS and now my code won't compile. What happened?

    New math functions available in recent Solaris versions can cause previously valid code to become invalid.

    The functions in <math.h> previously had versions only for type double. The new Solaris headers and library have overloads for types float and long double as well. To avoid an ambiguous call you might need to add explicit casts when calling these functions with integer arguments. For example:

    #include <math.h>
    extern int x;
    double      z1 = sin(x); // now ambiguous
    double      z2 = sin( (double)x ); // OK
    float       z3 = sin( (float)x ); // OK
    long double z4 = sin( (long double)x ); //  OK
    

    The Solaris patches listed below provide full ANSI C++ <cmath> and <math.h> library support as implemented in the libm patch for Solaris 8 and 9.

    • Solaris 9 sparc (PatchId 111722-04)
    • Solaris 9 i386 (PatchId 111728-03)
    • Solaris 8 sparc (PatchId 111721-04)
    • Solaris 8 i386 (PatchId 112757-01)

  2. Can I mix compatibility mode (-compat) code with standard mode code?

    We recommend against, and do not support, mixing code in the same program, even through "plug-ins" or dynamically-loaded libraries for the following reasons:

    • The layout of class objects is different.

    • The calling sequence for functions is different.

    • The "name mangling" is different.

    • The exception-handling methods conflict.

    • The presence of two sets of iostream objects connected to the same file descriptors cause problems.

    Even when the two parts (compatibility mode and standard mode) of the program do not communicate, the program could crash immediately if an exception is thrown anywhere in the code.

    You can link compatibility mode and standard mode object files together under some circumstances. The subject is covered in detail in the C++ Migration Guide that ships with the compiler. Refer to "Mixing Old and New Binaries" in Chapter 1. This guide is available online at http://docs.sun.com.

  3. How do I mix C++ or C programs with F77, F90, or F95 programs?

    Starting with Workshop 6 update 1 (compiler version 5.2), you can use the -xlang={f90|f95|f77} option. This option tells the driver to figure out exactly which libraries need to be on the link line and to figure out the order in which they need to appear.

    The -xlang option is not available for the C compiler. To mix C and Fortran routines, you must compile them with cc and link them using the Fortran linker.



C. Coding and Diagnostics

  1. Why do I get errors and warnings involving file foo.cc when I'm not compiling or including foo.cc in my program?
     
  2. Why do I get "duplicate definition" error messages when I compile the foo.i file that is generated from the -P preprocessing option?
     
  3. Why am I getting an error when I link a SPARC V9 archive library into a dynamic library? It worked in Sun Studio 8.
     
  4. What causes this message: "SunWS_cache: Error: Lock attempt failed for SunWS_cache"?
     
  5. Why do I get the following warning from the linker: "ld: warning: symbol 'clog' has differing types"?
     
  6. Why does my multi-threaded program using STLport crash when I compile it with -xarch=v8plus or -xarch=v8plusa?
     
  7. Why does the compiler now say that a call to abs() is ambiguous?
     
  8. When do temporary objects get destroyed?
     
  9. Why does the compiler report an ambiguity for the standard exception class?
     
  10. Why does C++ 5.3 and later emit errors about throw specifications on my derived virtual functions?
     
  11. Why do template instances turn up missing when I link my program? The instances seem to be in the template cache.
     
  12. Why do I get a warning about a function not being expanded when I use +w2 and not when I use +w2 +d?
     
  13. Can I use the -ptr option to have multiple template repositories, or to share repositories among different projects? If not, what can I do?
     
  14. Why does fprintf("%s",NULL) cause a segmentation fault?
     
  15. Depending on how I call sqrt(), I get different signs for the imaginary part of the square root of a complex number. What's the reason for this?
     
  16. A friend function in a class template does not get instantiated and I get link-time errors. This worked with C++ 5.0, why doesn't it work now?
     
  17. Why does the compiler say a member of an enclosing class is not accessible from a nested class?
     
  18. What causes the "pure virtual function call" message at run time?
     
  19. Why does the compiler say that a derived-class virtual function hides a base-class virtual function with a different signature? My other compiler doesn't complain about the code.
     

  1. Why do I get errors and warnings involving file foo.cc when I'm not compiling or including foo.cc in my program?

    If a header file foo.h has template declarations, the compiler by default searches for a file foo with a C++ file extension (foo.c, foo.cc, foo.C, foo.cpp, foo.c++) and includes it automatically if it is found. Refer to the C++ User's Guide section titled "Template Definitions Searching" for details.

    If you have a file foo.cc that you don't intend to be treated this way, you have two options:

    • Change the name of the .h or the .cc file to eliminate the name match.
    • Disable the automatic search for template definition files by specifying the -template=no%extdef option. However, that option disables all searches for separate template definitions. The C++ standard library implementation relies on the compiler finding the separate definitions. You must then include all template definitions explicitly in your code so you cannot use the definitions-separate model.

      Refer to the C++ User's Guide sections 5.2.1 and 5.2.2 for further discussion of the template definitions model or refer to the index of the C++ User's Guide for pointers to descriptions of the definitions separate and definitions included models.

  2. Why do I get "duplicate definition" error messages when I compile the foo.i file that is generated from the -P preprocessing option?

    By default, the compiler allows template definitions to be separate from their declarations. Refer to the C++ User's Guide sections 5.2.1 and 5.2.2 for further discussion of the template definitions model or refer to the index of the C++ User's Guide for pointers to descriptions of the definitions separate and definitions included models.

    When the compiler sees a declaration without a definition of a needed template in a .h or .i file, it looks for a template definition file. A file with the same name as the .h or .i file but with one of the extensions c, cc, C, c++, or cpp, is assumed to have template definitions. If such a file exists, it is included automatically, as documented in the C++ manual.

    Suppose file foo.cc is compiled with -P, thus generating foo.i, and has a declaration for a needed template but no definition. The compiler looks for a definition file and finds foo.cc. The foo.cc file gets included automatically, which results in duplicate definitions.

    You can turn off the compiler's search for template definitions by specifying the -template=no%extdef option. However, that option disables all searches for separate template definitions. The C++ standard library implementation relies on the compiler finding the separate definitions.

    For this case, where you want to compile a .i file, just rename the file, giving it a unique name. Then you don't have to disable separate template compilation. For example:

            CC -P foo.cc
            mv foo.i foo_prep.i
            CC -c foo_prep.i
    

    Note that this will not work if you use the -E option to generate pre-processed files. The -E option keeps a record of the included file names, so a .cc file can still be found automatically.

  3. Why am I getting an error when I link a SPARC V9 archive library into a dynamic library? It worked in Sun Studio 8.

    The new, default, compiler-address code-model for V9 is -xcode=abs44, which improves performance over the previous -xcode=abs64. However, the new code model is not usable within dynamic libraries. There are two solutions to the problem.

    • Recompile the object files with -xcode=pic13 or -xcode=32. This method is preferred, and nearly always the right thing.

    • Recompile the object files with -xcode=abs64. This method results in dynamic libraries that are not sharable. Each process must rewrite the library as it is copied into separate areas of memory. The method is useful for applications that run for a very long time under tight performance constraints and low system sharing.

  4. What causes this message: "SunWS_cache: Error: Lock attempt failed for SunWS_cache"?

    There are two main causes for the "lock attempt failed" error message about the template cache:

    • Sometimes a compilation aborts or is killed in such a way that it does not release the lock it is holding on the cache. This situation could occur in old compiler versions. Newer versions and current patches to older compilers ensure that the lock is released no matter how the compiler exits. You could remove just the lock file, but the cache is probably corrupted, and will cause further problems. The safest fix is to delete the entire template cache.

    • The template cache must be writable by the compiler process. Refer to the umask(1) man page for more information. In particular, you must be sure that the umask of a process that creates the cache or files in it allows writing by other processes that need to access the same cache. If the directory is mounted on an NFS file system, the system must be mounted for read/write.

  5. Why do I get the following warning from the linker: "ld: warning: symbol 'clog' has differing types"?

    The linker warns about the pair of weak symbols that have different types when you link libm.so.2 and the classic iostream library in the same program. You can ignore the warning.

    The default math library on Solaris 10 is libm.so.2 and it contains the complex log function 'clog' in the global namespace as required by the C99 standard. If you use C++ classic iostreams by specifying -compat=4 or by specifying -library=iostream, you get the buffered standard error stream 'clog' in the global namespace. (Standard iostreams does not have this conflicting symbol.)

    We have adjusted headers and libraries to silently rename each of these 'clog' symbols so that you can use both in one program. However, we must retain the original symbol spellings as weak symbols in each of the libraries, so that old binaries looking for the original symbols can continue to link.

    Be sure to get iostream and math declarations by including the appropriate system headers rather than declaring any of these entities yourself.

  6. Why does my multi-threaded program using STLport crash when I compile it with -xarch=v8plus or -xarch=v8plusa?

    STLport originally had a bug that prevented correct multi-threaded operation when compiling with -xarch=v8plus or -xarch=v8plusa. The bug was fixed, but the fix involved a change in the STLport headers, and a change in some STLport objects. Code compiled using earlier versions of the STLport headers needs to be recompiled with C++ 5.6 or with a patched earlier release before being linked into new programs.

  7. Why does the compiler now say that a call to abs() is ambiguous?

    The C++ Standard in section 26.5 requires the following overloads of the abs function:

    • In <stdlib.h> and <cstdlib>

      int  abs(int);
      long abs(long);
      

    • <math.h> and <cmath>

      float       abs(float);
      double      abs(double);
      long double abs(long double);
      

    Until fairly recently, the only version of abs available on Solaris was the traditional int version. If you invoked abs with any numeric type, the value was implicitly converted to type int, and the int version of abs was called, assuming that you included <stdlib.h> or <cstdlib>.

    Recent updates of Solaris headers and libraries now comply with the C++ standard regarding math functions.

    If you include, for example, <math.h> but not <stdlib.h>, and invoke abs with an integer argument, the compiler must choose among the three floating-point versions of the functions. An integer value can be converted to any of the floating-point types, and neither conversion is preferred over the others. Reference: C++ standard section 13.3.3. The function call is therefore ambiguous. You will get an ambiguity error using any compiler that conforms to the C++ Standard.

    If you invoke the abs function with integer arguments, you should include standard header <stdlib.h> or <cstdlib> to be sure you get the correct declarations for it. If you invoke abs with floating-point values, you should also include <math.h> or <cmath>.

    Here's a simple recommended programming practice: if you include <math.h> or <cmath>, also include <stdlib.h> or <cstdlib>.

    Similar considerations apply to other math functions, like cos or sqrt. Solaris headers and libraries now comply with the C++ Standard, supplying float, double, and long double overloaded versions of the functions. If you invoke, for example, sqrt with an integer value, the code formerly compiled because only one version of sqrt was available. With three floating-point versions available, you must cast the integer value to the floating-point type that you want.

            double root_2 = sqrt(2); // error
            double root_2 = sqrt(2.0); // OK
            double x = sqrt(int_value); // error
            double x = sqrt(double(int_value)); // OK
    

  8. When do temporary objects get destroyed?

    The compiler creates a temporary object sometimes for convenience, and sometimes because the language rules require it. For example, a value returned by a function is a temporary object, and the result of a type conversion is a temporary object.

    The original C++ rule was that the temporary object ("temp") could be destroyed at any time up until the end of the block in which it was created. Sun C++ compilers destroyed temps at the end of the block (closing right brace).

    After much debate over several years, the C++ Committee settled on a fixed place where temps should be destroyed: at the end of the complete expression in which the temp is created. Usually that is at the end of the statement in which the expression appears. That is the rule in the C++ Standard.

    We found that many programs using Sun C++ depended (perhaps unintentionally) on temps living until the end of the block. Accordingly, we left the compiler default behavior unchanged: Temporary objects by default are destroyed at the end of the block in which they are created.

    If you want standard-conforming behavior, where temps are destroyed at the end of the complete expression in which they are created, use the compile-time option -features=tmplife.

    You do not need to use the option consistently throughout a program. Temporary objects created in a module will be destroyed at the end of the expression or the end of the block in that module depending on the option in effect at compilation time.

  9. Why does the compiler report an ambiguity for the standard exception class?

    On Solaris, standard header <math.h> has a declaration for a struct "exception", as required by standard Unix. If you bring the C++ standard exception class into global scope with a using-declaration or using-directive, it creates a conflict.

    // Example 1
    
    #include <math.h>
    #include <exception>
    using namespace std; // using-declaration
    exception E;  // error, exception is ambiguous
    
    // Example 2:
    
    #include <math.h>
    #include <exception>
    using std::exception; // using-directive
    exception E;  // error, multiple declaration for exception
    

    Name resolution is slightly different for using-declarations compared to using-directives, so the error messages are not quite the same.

    Workarounds:

    1. Use <cmath> instead of <math.h>. On Solaris, <cmath> contains only the declarations specified by the C and C++ standards. If you need Unix-specific features of <math.h>, you can't use this workaround.


    2. Don't write using std::exception; when you also use <math.h>. Write std::exception explicitly, or use a typedef, to access the standard exception class as in this example:
      #include <math.h>
      #include <exception>
      std::exception E; // OK
      typedef std::exception stdException; // OK
      stdException F; // OK
      
    3. Don't write using namespace std;.
      The C++ namespace std contains so many names that you are likely to have conflicts with application code or third-party libraries when you use this directive in real-world code. (Books and articles about C++ programming sometimes have this using-directive to reduce the size of small examples.) Use individual using-declarations or explicitly qualify names.


  10. Why does C++ 5.3 and later emit errors about throw specifications on my derived virtual functions?

    A C++ rule newly enforced by the C++ compiler since version 5.3 is that a virtual function in a derived class can allow only the exceptions that are allowed by the function it overrides. The overriding function can be more restrictive, but not less restrictive. Consider the following example:

    class Base {
    public:
    	// might throw an int exception, but no others
    	virtual void f() throw(int);
    };
    class Der1 : public Base {
    public:
            virtual void f() throw(int); // ok, same specification
    };
    class Der2 : public Base {
    public:
            virtual void f() throw(); // ok, more restrictive
    };
    class Der3 : public Base {
    public:
            virtual void f() throw(int, long); // error, can't allow long
    };
    class Der4 : public Base {
    public:
            virtual void f() throw(char*); // error, can't allow char*
    };
    class Der5 : public Base {
    public:
            virtual void f(); // error, allows any exception
    };
    

    This code shows the reason for the C++ rule:

    #include "base.h" // declares class Base
    void foo(Base* bp) throw()
    {
        try {
           bp->f();
        }
        catch(int) {
        }
    }
    

    Since Base::f() is declared to throw only an int exception, function foo can catch int exceptions, and declare that it allows no exceptions to escape. Suppose someone later declared class Der5, where the overriding function could throw any exception, and passed a Der5 pointer to foo. Function foo would become invalid, even though nothing is wrong with the code visible when function foo is compiled.

  11. Why do template instances turn up missing when I link my program? The instances seem to be in the template cache.
     

    The template cache maintains a list of dependencies between the object files that the compiler generates, and the template instances in the cache. Note, however, that the compiler now only uses the template cache when you specify -instances=extern. If you move or rename object files, or combine object files into a library, you lose the connection to the cache. Here are two alternatives:

    • The link lines also need -instances=extern.

    • Generate object files directly into the final directory. The template cache will be in that same directory.

      Do not do this:

      example% CC -c -instances=extern f1.cc
      example% mv f1.o /new/location/for/files
      

      Do this instead:

      example% CC -c -instances=extern f1.cc -o /new/location/for/files/f1.o
      

      You can encapsulate the process in makefile macros.

    • You can create intermediate archive (.a) files using CC -xar. Each archive then contains all the template instances used by the objects in the archive. You then link those archives into the final program. Some template instances are duplicated in different archives, but the linker keeps only one of each.

      example% CC -c -instances=extern f1.cc f2.cc f3.cc
      example% CC -xar f1.o f2.o f3.o -o temp1.a
      example% CC -c -instances=extern f4.cc f5.cc f6.cc
      example% CC -xar f4.o f5.0 f6.0 -o temp2.a
      example% CC -c -instances=extern main.cc
      example% CC main.o temp1.a temp2.a -o main
      
  12. Why do I get a warning about a function not being expanded when I use +w2 and not when I use +w2 +d?

    The C++ compiler has two kinds of inlining: C++ inline function inlining, which is done by the parser, and optimization inlining, which is done by the code generator. The C and Fortran compilers have only optimization inlining. (The same code generator is used for all compilers on a platform.)

    The C++ compiler's parser attempts to expand inline any function that is declared implicitly or explicitly as inline. If the function is too large, the parser emits a warning only when you use the +w2 option. The +d option prevents the parser from attempting to inline any function. This is why the warning disappears when you use +d. (The -g option also turns off the inlining of C++ inline functions.) The -xO options do not affect this type of inlining.

    The optimization inlining does not depend on the programming language. When you select an optimization level of -xO4 or higher, the code generator examines all functions, independent of how they were declared in source code, and replaces function calls with inline code wherever it thinks the replacement will be beneficial. No messages are emitted about optimization inlining (or its failure to inline functions). The +d option does not affect optimization inlining.

  13. Can I use the -ptr option to have multiple template repositories, or to share repositories among different projects? If not, what can I do?

    The -ptr option is not supported in versions 5.0 through 5.6. Although it was available in version 4.2, it did not always work as users expected, and it caused many problems.

    The best advice is not to share repositories among different projects. The sharing of repositories is likely to cause problems more severe than the problem you are trying to solve. Compile only one project in any one directory. Use a different directory for the binaries associated with a different project.

    Beginning with version 5.0, the compiler puts a template repository in the same directory as the object file being generated. If you want to use multiple repositories for one project, generate an object file in the directory where you want the associated repository to be. At link time, all the repositories associated with the object files will automatically be searched for template instances. No compiler options are required.

  14. Why does fprintf("%s",NULL) cause a segmentation fault?

    Some applications erroneously assume that a null character pointer should be treated the same as a pointer to a null string. A segmentation violation occurs in these applications when a null character pointer is accessed.

    There are several reasons for not having the *printf() family of functions check for null pointers. These include, but are not limited to the following reasons:

    • Doing so provides a false sense of security. It makes programmers think that passing null pointers to printf() is OK.

    • It encourages programmers to write non-portable code. ANSI C, XPG3, XPG4, SVID2, and SVID3 say that printf("%s", pointer) needs to have pointer point to a null terminated array of characters.

    • It makes debugging harder. If the programmer passes a null pointer to printf() and the program drops core, it is easy to use a debugger to find which printf() call gave the bad pointer. However, if printf() hid the bug by printing "(null pointer)," then other programs in a pipeline are likely to try interpreting "(null pointer)" when they are expecting some real data. At that point it may be impossible to determine where the real problem is hidden.

    If you have an application that passes null pointers to *printf, you can use a special shared object /usr/lib/0@0.so.1 that provides a mechanism for establishing a value of 0 at location 0. Because this library masks all errors involving the dereference of a null pointer of any type, you should use this library only as a temporary workaround until you can correct the code.

  15. Depending on how I call sqrt(), I get different signs for the imaginary part of the square root of a complex number. What's the reason for this?

    The implementation of this function is aligned with the C99 csqrt Annex G specification. For example, here's the output from the following code example :

    complex sqrt (3.87267e-17, 0.632456)
    float sqrt (3.87267e-17, -0.632456)

    • Example using libcomplex in compatibility mode:

      #include <iostream.h>
      #include <math.h>
      #include <complex.h>
       
      int main ()
      {
            complex ctemp(-0.4,0.0);
            complex c1(1.0,0.0);
            double  dtemp(-0.4);
            cout<< "complex sqrt "<< sqrt(ctemp)<<endl;
            cout<< "float sqrt   "<< sqrt(c1*dtemp)<<endl;
      }
      
    • Example using libCstd in standard mode:

      #include <iostream>
      #include <math.h>
      #include <complex>
       
      using namespace std;
       
      int main ()
      {
           complex<double> ctemp(-0.4,0.0);
           complex<double> c1(1.0,0.0);
           double  dtemp(-0.4);
           cout<< "complex sqrt "<< sqrt(ctemp)<<endl;
           cout<< "float sqrt   "<< sqrt(c1*dtemp)<<endl;
      }
      
    • The sqrt function for complex is implemented using atan2. The following example illustrates the problem by using atan2. The output of this program is:

      c=-0.000000  b=-0.400000  atan2(c, b)=-3.141593
      a=0.000000  b=-0.400000  atan2(a, b)=3.141593
      

      In one case, the output of atan2 is negative and in the other case it's positive. It depends on whether -0.0 or 0.0 gets passed as the first argument.

      #include <stdio.h>
      #include <math.h>
       
      int main()
      {
          double a = 0.0;
          double b = -0.4;
          double c = a*b;
          double d = atan2(c, b);
          double e = atan2(a, b);
          printf("c=%f  b=%f  atan2(c, b)=%f\n", c, b, d);
          printf("a=%f  b=%f  atan2(a, b)=%f\n", a, b, e);
      }
      
  16. A friend function in a class template does not get instantiated and I get link-time errors. This worked with C++ 5.0, why doesn't it work now?

    The following test case compiles and links without errors with the C++ 5.0 compiler but causes link-time errors with later versions of the compiler:

    example% cat t.c
    
    #include <ostream>
    
    using std::ostream;
    
    template <class T> 
    class TList {
    public:
      friend ostream& operator<< (ostream&, const TList&);
    };
    
    template <class T>
    ostream& operator<< (ostream& os, const TList<T>& l)
    {
      return os; 
    }
    
    class OrderedEntityList {
    public:
      TList<int> *Items; 
      ostream& Print(ostream &) const;
    };
    
    ostream& 
    OrderedEntityList::Print(ostream& os) const
    {
      os << *Items;
      return os;
    }
    
    main()
    {
    }
    
    example% CC t.c
    
    Undefined			first referenced
    symbol  			    in file
    std::basic_ostream<char,std::char_traits<char> 
    >&operator<<(std::basic_ostream<char,std::char_traits<char> >&,const 
    TList<int>&) 4421826.o
    
    ld: fatal: Symbol referencing errors. No output written to a.out
    

    The test case is invalid according to the standard. The problem is that the declaration

    friend ostream& operator<< (ostream&, const TList&);
    

    does not refer to any template instance.

    The unqualified name lookup cannot match a template declaration even if one were visible at the point of the friend declaration. To get the friend declaration to match a template, you need either to declare it as a template function, or qualify the name.

    Either way, the declaration for the template must be visible at the point of the friend declaration.

    To summarize, the friend declaration does not refer to a template, but it declares a function that is the best match to the function call. (A non-template function is preferred over a template function if they are otherwise equal.)

    To following code is valid:

    template <class T> class TList; 
    // so we can declare the operator<< template
    
    template <class T> 
    ostream& 
    operator<< (ostream& os, const TList<T>& l) 
    { 
      return os; 
    } 
    
    template <class T> 
    class TList {
    public :
      // note the scope qualification on the function name
      friend ostream& ::operator<< (ostream&, const TList&);
    };
    
  17. Why does the compiler say a member of an enclosing class is not accessible from a nested class?
    class Outer {
        typedef int my_int;
        static int k;
        class Inner {
            my_int j;          // error, my_int not accessible
            int foo() {
                    return k; // error, k not accessible
    		}
    	};
    };
    

    According to the ARM and to the C++ Standard, a nested class has no special access to members of the enclosing class. Since my_int and k are private in Outer, only friends of Outer have access to them. In order to make a nested class a friend, you must first forward-declare the class, then make it a friend, as the following example shows:

    class Outer {
        typedef int my_int;
        static int k;
    															
    	// add these two lines ahead of the class definition 
        class Inner;  
        friend class Inner;
    																			
        class Inner {
            my_int j;         // OK
            int foo() {
                    return k; // OK
    		}
    	};
    };
    
  18. What causes the "pure virtual function call" message at run time?

    A "pure virtual function called" message always arises because of an error in the program. The error occurs in either of the following two ways:

    • You can cause this error by passing the "this" parameter from a constructor or destructor of an abstract class to an outside function. During construction and destruction, "this" has the type of the constructor's or destructor's own class, not the type of the class ultimately being constructed. You can then wind up trying to call a pure virtual function. Consider the following example:
      class Abstract;
      
      void f(Abstract*);
      
      class Abstract {
      public:
              virtual void m() = 0; // pure virtual function
              Abstract() { f(this); }   // constructor passes "this"
      };
      
      void f(Abstract* p)
      {
              p->m();
      }
      

      When f is called from the Abstract constructor, "this" has the type "Abstract*", and function f attempts to call the pure virtual function m.

    • You can also cause this error by trying to call a pure virtual function that has been defined without using explicit qualification. You can provide a body for a pure virtual function, but it can be called only by qualifying the name at the point of the call, bypassing the virtual-call mechanism.
      class Abstract {
      public:
              virtual void m() = 0; // body provided later
              void g();
      };
      
      void Abstract::m() { ... } // definition of m
      
      void Abstract::g()
      {
              m(); // error, tries to call pure virtual m
              Abstract::m(); // OK, call is fully qualified
      }
      
  19. Why does the compiler say that a derived-class virtual function hides a base-class virtual function with a different signature? My other compiler doesn't complain about the code.

    The C++ rule is that overloading occurs only within one scope, never across scopes. A base class is considered to be in a scope that surrounds the scope of a derived class. Any name declared in a derived class therefore hides, and cannot overload, any function in a base class. This fundamental C++ rule predates the ARM.

    If another compiler does not complain, it is doing you a disservice, because the code will not behave as you probably expect. Our compiler issues a warning while accepting the code. (The code is legal, but probably does not do what you want.)

    If you wish to include base-class functions in an overloaded set, you must do something to bring the base-class functions into the current scope. If you are compiling in default standard mode, you can add a using-declaration:

    class Base {
    public:
            virtual int    foo(int);
            virtual double foo(double);
    };
    
    class Derived : public Base {
    public:
            using Base::foo; // add base-class functions to overload set
            virtual double foo(double); // override base-class version
    };
    


D. Library Compatibility

  1. How do I get a C++ standard library (stdlib) that is fully compliant? What functionality does the current libCstd not support?
     
  2. I need the C++ standard template library (STL). Where do I get it? Is there one for compatibility mode (-compat)?
     
  3. What standard library functionality is missing from libCstd?
     
  4. What are the consequences of the missing standard library functionality?
     
  5. Is there a version of tools7 library that works with standard streams? Will there be a tools8 available soon?
     

  1. How do I get a C++ standard library (stdlib) that is fully compliant? What functionality does the current libCstd not support?

    This release includes the STLport Standard Library implementation version 4.5.3 as an optional standard library. STLport has good conformance to the C++ standard, and also has some popular extensions. However, it is not binary compatible with the standard library that is used by default.

    The current libCstd was developed for version 5.0 of the C++ compiler. That version did not support templates as members of classes. Some parts of the standard library require member templates, meaning that some functionality is missing. The missing functionality mostly shows up in container classes that have constructor templates allowing implicit type conversions. You have to write explicit conversions in your source code as a workaround.

    Beginning with version 5.1, the C++ compiler supports templates as members of classes, and can support a standard-conforming library. We cannot update the library without breaking source and binary compatibility, so we continue to ship a libCstd with the same limitations.

    You can find public implementations of standard libraries at gnu and SGI web sites, and you can also purchase libraries from vendors such as RogueWave and Dinkumware. See also the following question about the STL.

  2. I need the C++ standard template library (STL). Where do I get it? Is there one for compatibility mode (-compat)?

    The C++ compiler now supports the STLport Standard Library implementation version 4.5.3. libCstd is still the default library, but the STLport product is now available as an alternative. This release includes both a static archive called libstlport.a and a dynamic library called libstlport.so.

    Issue the following compiler option to turn off libCstd and use STLport:

    -library=stlport4

    Both the default C++ standard library libCstd as well as STLport contain the STL. You can use a different version of the standard library, but doing so is risky and good results are not guaranteed.

    To plug in a different STL, use the -library=no%Cstd option and point the compiler to your header files and library of choice. If the replacement library does not have its own iostreams library, and if you can use "classic" iostreams instead of standard iostreams, add -library=iostream to the command line. For detailed instructions, see "Replacing the C++ Standard Library" in the C++ User's Guide that ships with the compiler. This guide is available online at http://docs.sun.com.

  3. What standard library functionality is missing from libCstd?

    The standard library was originally (in C++ 5.0) built without support for features which required member template and partial specialization in the compiler. Although these features have been available since C++ 5.1, they cannot be turned on in the standard library because they would compromise backward compatibilitiy. The following is a list of missing functionality for each disabled feature.

    • Disabled feature: member template functions

      • In class complex in <complex>:

        template <class X> complex<T>& operator= (const complex<X>& rhs)
        template <class X> complex<T>& operator+= (const complex<X>& rhs)
        template <class X> complex<T>& operator-= (const complex<X>& rhs)
        template <class X> complex<T>& operator*= (const complex<X>& rhs)
        template <class X> complex<T>& operator/= (const complex<X>&)


      • In class pair in <utility>:

        template<class U, class V> pair(const pair<U, V> &p);

      • In class locale in <locale>:

        template <class Facet> locale combine(const locale& other);

      • In class auto_Ptr in <memory>:

        auto_ptr(auto_ptr<Y>&);
        auto_ptr<Y>& operator =(auto_ptr<Y>&);
        template <class Y> operator auto_ptr_ref<Y>();
        template <class Y> operator auto_ptr<Y>();

      • In class list in <list>:

        Member template sort.

      • In most template classes:

        Template constructors.

    • Disabled feature: member template classes

      In class auto_ptr in <memory>:

      template <class Y> class auto_ptr_ref{};
      auto_ptr(auto_ptr(ref<X>&);

    • Disabled feature: overloading of function template arguments that are partial specializations

      In <deque>, <map>, <set>, <string>, <vector> and <iterator> the following template functions (non-member) are not supported:

      • For classes map, multimap, set, multiset, basic_string, vector, reverse_iterator, and istream_iterator:

        bool operator!= ()

      • For classes map, multimap, set, multiset, basic_string, vector and reverse_iterator:

        bool operator> ()
        bool operator>= ()
        bool operator<= ()

      • For classes map, multimap, set, multiset, basic_string, and vector:

        void swap()

    • Disabled feature: partial specialization of template classes with default parameters

    In <algorithm>, the following template functions (non-member) are not supported:

    count(), count_if()

    In <iterator>, the following templates are not supported:

    template <class Iterator> struct iterator_traits {}
    template <class T> struct iterator_traits<T*> {}
    template <class T> struct iterator_traits<const T*>{}
    template typename iterator_traits::difference_type distance(InputIterator first, InputIterator last);

  4. What are the consequences of the missing standard library functionality?

    Some code that is valid according to the C++ standard will not compile.

    The most common example is creating maps where the first element of the pair could be const but isn't declared that way. The member constructor template would convert pair<T, U> to pair<const T, U> implicitly when needed. Because that constructor is missing, you get compilation errors instead.

    Since you are not allowed to change the first member of a pair in a map anyway, the simplest fix is to use an explicit const when creating the pair type. For example, instead of pair<int, T> use pair<const int, T>; instead of map<int, T> use map<const int, T>.

  5. Is there a version of tools7 library that works with standard streams? Will there be a tools8 available soon?

    Yes there is, but only with C++ 5.3, 5.4, 5.5, and 5.6. Use the -library=rwtools7_std command to compile and link with this library.

    RogueWave has changed the way Tools.h++ works, and now supplies it only as part of their SourcePro product. There is no Tools.h++ version 8, as such.



E. Compile-Time Performance

  1. How come I'm not seeing an improvement in compile time even though I've started using the precompiled header facility of the C++ compiler?
     
  2. Why does a large file take so much longer to compile than a shorter one?
     
  3. I have seen enormous compile times with compiler versions 5.0 and version 5.1 (compared to 4.2). Can I expect this problem to be fixed in the near future?
     
  4. We have noticed much larger binaries compared to the 4.2 version of the compiler. Is there a solution to this?
     
  5. Can a single compilation process be distributed onto multiple processors? More generally, does a multiprocessor (MP) system always have better compile-time performance?
     

  1. How come I'm not seeing an improvement in compile time even though I've started using the precompiled header facility of the C++ compiler?

    Using precompiled headers does not guarantee faster compile times. Precompiled headers impose some overhead that is not present when you compile files directly. To gain a performance advantage, the precompiled headers must have some redundancy that precompilation can eliminate.

    For example, a program that is highly likely to benefit from precompilation is one that includes many system headers, iostreams, STL headers, and project headers. Those files contain conditionally-compiled code. Some headers are included multiple times, and the compiler must scan over the entire file if only to discover there is nothing to do in the redundant includes. System headers typically have hundreds of macros to expand.

    Using a precompiled header means opening one file instead of dozens. The multiple includes that do nothing are eliminated, as are comments and extra white space. The macros in the headers are pre-expanded. Typically, these savings add up to a significant reduction in compile time.

  2. Why does a large file take so much longer to compile than a shorter one?

    The size of the file is probably not the issue so here are three likely causes for the delay.

    • The size of functions in the file and the level of optimization

      Large functions at high optimizations take a long time to process, and can require lots of memory. If the code uses large macros extensively, a function that looks small might become very large after macro expansion.

      Try compiling without any optimization (no -xO? or -O? option). If the compilation completes quickly, the problem is probably one or more very large functions in the file, and the time and memory necessary to optimize it.

      In addition, make sure the computer used for compilation has plenty of physical memory for the compilation run. If you don't have enough memory, the optimization phase can cause thrashing.

    • Inline functions

      Inline functions (in C and C++) act like macros where compilation time is concerned. When a function call is expanded inline, it can turn into a lot of code. The compiler then is dealing with one large function instead of 2 or more small functions.

      Compilations often proceed more quickly when you disable function inlining. Of course, the resulting code will probably run more slowly.

      See the description of -xinline and "Using Inline Functions" in the C++ User's Guide for more information.

    • C++ class templates

      C++ templates cause the compiler to generate code based on the templates invoked. One line of source code can require the compiler to generate one or more template functions. It's not that templates themselves slow down compilation significantly, but that the compiler has more code to process than is apparent by looking at the original source code.

      For example, if it were not for the standard library already having the functions, this line of code

      cout << "value = " << x << endl;

      would cause the compiler to generate 241 functions.

  3. I have seen enormous compile times with compiler versions 5.0 and version 5.1 (compared to 4.2). Can I expect this problem to be fixed in the near future?

    We have improved compile times considerably in 5.1patch01, version 5.2, 5.3, 5.4, 5.5, and 5.6. If you are unhappy with the compiler's performance, keep the following recommendations in mind:

    1. In some extreme cases, inlining causes huge bloat. When either the -xO4 or -xO5 option is used, the code generator automatically inlines some functions. You may need to use a lower optimization level such as -xO3 You can use the -xinline option to prevent the automatic inlining of specific functions by the optimizer.

    2. Turn off the explicit inlining of larger functions. Refer below for a discussion of explicit inlining.

  4. We have noticed much larger binaries compared to the 4.2 version of the compiler. Is there a solution to this?

    The binaries increase in size for -g compiles because, starting with version 5.0, the compiler generated lots of template debugging information . The 5.1 compiler greatly reduced the size of the generated debug information for many kinds of programs. The 5.2, 5.3, 5.4, 5.5, and 5.6 compilers have added further improvements. In many cases, the binary decreases from 25% to over 50% in size. The improvements show up mostly for code using namespaces, templates, and class hierarchies with many levels of inheritance.

  5. Can a single compilation process be distributed onto multiple processors? More generally, does a multiprocessor (MP) system always have better compile-time performance?

    The compiler itself is not multithreaded. You can expect better performance with MP systems, because the computer always has many other processes running at the same time as any one compilation.

    If you use dmake (one of the tools that ships with the compiler), you can run multiple compilations simultaneously.



F. Run-Time Performance

  1. Standard library streams are slower than gcc or KAI streams. This is a performance hit for me. Is there a solution in sight?
     
  2. Does C++ always inline functions marked with "inline" keyword? Why didn't I see functions inlined even though I wrote them that way?
     

  1. Standard library streams are slower than gcc or KAI streams. This is a performance hit for me. Is there a solution in sight?

    You can either specify the new C++ compiler option -sync_stdio=no at link time to fix this problem or add a call to the sync_with_stdio(false) function and recompile.

    The major performance problem with stdlib 2.1.1 is that it synchronizes C stdio with C++ streams by default. Each output to cout is flushed immediately. If your program does a lot of output to cout but not to stdout, the excess buffer flushing can add significantly to the run-time performance of the program. The C++ standard requires this behavior, but not all implementations meet the standard. The following program demonstrates the synchronization problem. It must print "Hello beautiful world" followed by a newline:

        #include <iostream>
        #include <stdio.h>
        int main()
        {
            std::cout << "Hello ";
            printf("beautiful ");
            std::cout << "world";
            printf("\n");
        }
    
    If cout and stdout are independently buffered, the output could be scrambled.

    If you cannot recompile the executable, specify the new C++ compiler option -sync_stdio=no at link time. This option causes sync_with_stdio( ) to be called at program initialization before any program output can occur.

    If you can recompile, add a call to the sync_with_stdio(false) function before any program output thereby specifying that the output does not need to be synchronized. Here is a sample call:

            #include <iostream>
            int main(int argc, char** argv)
            {
                    std::ios::sync_with_stdio(false);
            }
    

    The call to sync_with_stdio should be the first one in your program.

  2. See the C++ User's Guide or the C++ man page CC(1) for more information on -sync_stdio.

  3. Does C++ always inline functions marked with inline keyword? Why didn't I see functions inlined even though I wrote them that way?

    Fundamentally, the compiler treats the inline declaration as a guidance and attempts to inline the function. In compiler versions 5.1 through 5.6, the inlining algorithm has been revamped to make it understand more constructs. However, there are still cases where it will not succeed. The restrictions are:

    • Some rarely executed function calls are not expanded in the 5.2 through 5.6 C++ compilers. This change helps achieve a better balance of compilation speed, output code size, and run-time speed.

      For example, expressions used in static variable initialization are only executed once and thus function calls in those expressions are not expanded. Note that the inline function func might not be expanded when called in an initialization expression of static variables, it could still be inlined in other places. Similarly, function calls in exception handlers might not be expanded, because those code is rarely executed.

    • Recursive functions are inlined only to the first call level. The compiler cannot inline recursive function calls indefinitely. The current implementation stops at the first call to any function that is being inlined.  

    • Sometimes even calls to small functions are not inlined. The reason for this is that the total expanded size may be too large. For example, func1 calls func2, and func2 calls func3, and so forth. Even if each of these functions is small and there are no recursive calls, the combined expanded size could be too large for the compiler to expand all of them.

      Many standard template functions are small, but have deep call chains. In those cases, only a few levels of calls are expanded.

    • C++ inline functions that contain goto statements, loops, and try/catch statements are not inlined by the compiler. However, they might be inlined by the optimizer at the -xO4 level.  

    • The compiler does not inline large functions. Both the compiler and the optimizer of the C++ compiler place a limit on the size of inlined functions. This limitation is our general recommendation. For special cases, please consult with technical support to learn about the internal options that raise or lower this size limitation.  

    • A virtual function cannot be inlined, even though it is never redefined in subclasses. The reason is that the compiler can not know whether a different compilation unit contains a subclass and a redefinition of the virtual function.  

    Note that in some previous versions, functions with complicated if-statements and return-statements could not be inlined. This limitation has been removed. Also, the default limitation on inline function size has been raised. With some programs, these changes will cause more functions to be inlined and can result in slower compilations and more code generation.

    To completely eliminate the inlining of C++ inline functions, use the +d option.

    Separately, the optimizer inlines functions at higher optimization levels (-xO4) based on the results of control flow and so forth. This inlining is automatic and is done irrespective of whether you declare a function "inline" or not.

Updated 2004/12/10

Copyright © 2004 Sun Microsystems, Inc., All rights reserved. Use is subject to license terms.