Updated 2002/03/25

Forte [tm] Developer 7 FAQ: C++



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

  1. Why does the compiler report an ambiguity for the standard exception class?
     
  2. Why does C++ 5.3 emit errors about throw specifications on my derived virtual functions?
     
  3. What is the difference between standard and classic I/O streams? Can you recommend text books on the subject?
     
  4. Why do template instances turn up missing when I link my program? The instances seem to be in the template cache.
     

  1. 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.


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

    A C++ rule newly enforced by the 5.3 C++ compiler 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.

  3. 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.

    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;
    }
    
  4. 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. 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:

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

      Do not do this:

      CC -c f1.cc
      mv f1.o /new/location/for/files
      

      Do this instead:

      CC -c 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 f1.cc f2.cc f3.cc
      example% CC -xar f1.o f2.o f3.o -o temp1.a
      example% CC -c f4.cc f5.cc f6.cc
      example% CC -xar f4.o f5.0 f6.0 -o temp2.a
      example% CC -c main.cc
      example% CC main.o temp1.a temp2.a -o main
      


B. Versions, Patches, and Support

  1. How can I tell which C++ compiler versions are compatible?
     
  2. What RogueWave libraries are "certified" for use with Forte Developer 7?
     
  3. How can I figure out which patches exist and what problems are solved in the current patches?
     
  4. Do I need to have patches to libC.so.5 and libCrun.so.1?
     

  1. 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, 5.1, 5.2, 5.3, and 5.4 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, 5.1, 5.2, 5.3, and 5.4 is fully compatible, but debugging information (stabs) emitted by later compilers is not compatible with earlier debuggers.

    The C++ 5.0, 5.1, 5.2, 5.3, and 5.4 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.

  2. What RogueWave libraries are "certified" for use with Forte Developer 7?

    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.

  3. 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 Hot News Web page
    ( http://www.sun.com/forte/developer/hotnews.html) occasionally.

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

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

    The compiler ships with the latest SUNWlibC patches as of the compiler release date.

    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. The following table shows the current matrix of patch IDs as of March 2002.

    Please check for the latest package in the database. The package is called SUNWlibC (32bit) and SUNWlibCx(64bit).

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

    106327-13

    7  

    SPARC/v8

    106300-14

    7  

    SPARC/v9

    108434-06

    8

    SPARC/v8

    108435-06

    8

    SPARC/v9



C. Compiler Compatibility

  1. Can I mix compatibility mode (-compat) code with standard mode code?
     
  2. How do I mix C++ or C programs with F77, F90, or F95 programs?
     

  1. 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.

  2. 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.



D. Coding and Diagnostics

  1. Why do I get a warning about a function not being expanded when I use +w2 and not when I use +w2 +d?
     
  2. Can I use the -ptr option to have multiple template repositories, or to share repositories among different projects? If not, what can I do?
     
  3. I see an error message like this occassionally: SunWS_cache: Information: Database is locked, waiting... What does this mean? What causes it? Is any action expected of me? How do I get rid of it?
     
  4. Why does fprintf("%s",NULL) cause a segmentation fault?
     
  5. 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?
     
  6. 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?
     
  7. Why does the compiler say a member of an enclosing class is not accessible from a nested class?
     
  8. What causes the "pure virtual function call" message at run time?
     
  9. 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 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.

  2. 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, 5.1, 5.2, 5.3, or 5.4. 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.

  3. I see an error message like this occassionally: SunWS_cache: Information: Database is locked, waiting... What does this mean? What causes it? Is any action expected of me? How do I get rid of it?

    When a program that uses templates is compiled, the compiler locks the output SunWS_cache directory whenever it needs to update the compilation state information in SunWS_cache/CC_state. If multiple processes that use templates try to compile in the same directory, only one process will acquire a lock at a time.

    The C++ compiler versions 4.2, 5.0, and 5.1 issue a message "SunWS_cache: Information: Database is locked, waiting..." whenever a process is waiting to acquire a lock. This message is just for informational purposes and should be ignored. The message means that some other compilation process has locked the database. When the other job is over, the current job will continue. You could get this message, say, by using dmake. You cannot get rid of this message. The latest C++ 5.2 patch, the C++ 5.3 compiler, and the C++ 5.4 compiler use a different locking scheme and do not issue this message.

  4. Why does printf("%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.

  5. 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);
      }
      
  6. 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&);
    };
    
  7. 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
    		}
    	};
    };
    
  8. 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
      }
      
  9. 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
    };
    


E. 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 STLport's Standard Library implementation version 4.5.2 as an optional standard library in FD7. STLport has good conformance to the C++ standard, and also has some popular extentions. 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 STLport's Standard Library implementation version 4.5.2. libCstd is still the default library, but STLport's 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 compatiblitiy. 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*> {}

  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 and 5.4. Use the -library=rwtools7_std command to 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.



F. Compile-Time Perfromance

  1. 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?
     
  2. We have noticed much larger binaries compared to the 4.2 version of the compiler. Is there a solution to this?
     
  3. Can a single compilation process be distributed onto multiple processors? More generally, does a multiprocessor (MP) system always have better compile-time performance?
     

  1. 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, and 5.4. 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 to Question G.2 for a discussion of explicit inlining.

    3. By default, all instances are put into the object's SunWS_cache subdirectory. If there is a huge amount of instantiation, then repository management can take up to 95% of compilation times. Check if you can use one of the template instantiation models that bypasses the template repository; explicit, global, static, and semiexplicit. Use the -instances=model option to specify the desired model. If your compile-time problem is instantiation, use of a different instantiation model can dramatically decrease compile times.

      Note that these alternative models impose programming style restrictions. For more information about template instantiation models, see "Compiling Templates" in the C++ User's Guide that ships with the compiler. This guide is available online at http://docs.sun.com.

  2. 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 and 5.4 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.

  3. 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. If the simultaneous compilations require use of the template cache, contention for access to the cache can be a bottleneck. You should investigate options for avoiding the cache, such as the options described in 4c of this FAQ. For more information, see the "Compiling Templates" chapter in the C++ User's Guide that ships with the compiler. This guide is available online at http://docs.sun.com.



G. Run-Time Performance

  1. How does the 5.4 compiler's runtime compare to earlier versions and with respect to gcc?
     
  2. Does C++ always inline functions marked with "inline" keyword? Why didn't I see functions inlined even though I wrote them that way?
     
  3. stdlib streams are slower than gcc or KAI streams. This is a performance hit for me. Is there a solution in sight?
     

  1. How does the 5.4 compiler's runtime compare to earlier versions and with respect to gcc?

    As with any benchmark, your results will vary. The following tables lists some of the commonly used benchmarks and shows how the compiler stacks up against those on a 450MHz UltraSPARC II system.

    The table lists two kinds of performance numbers for a set of standard benchmark tests.

    The entries ending with "s", such as 354.84s or 1.3s, represent time in seconds. The entries ending with "ms", such as 17ms or 168435ms, represent time in milliseconds. A smaller value means a faster program, so smaller is better.

    All other entries, except for the Stepanov entry, show the number of operations per unit time. A higher number means a faster program, so bigger is better.

    For Stepanov, the ratio measures the "abstraction penalty", and a smaller number is better.

    Table 2: C++ Runtime Comparisons in Seconds
    Test 5.4 5.3 5.2 5.0 gcc2.95.2 kcc3.9a

    eon

    234s

    234s

    235s

    262s

    452s

    250s

     

    161s

    160s

    160s

    182s

    309s

    169s

     

    288s

    300s

    293s

    325s

    561s

    318s

    Haney

    1.51s

    1.58s

    1.96s

    2.69s

    2.57s

    NA

     

    3.78s

    3.80s

    3.80s

    3.77s

    5.69s

    NA

     

    2.55s

    2.59s

    2.59s

    2.39s

    3.24s

    NA

    oopack

    0.4s

    0.5s

    0.4s

    1.1s

    1.8s

    0.4s

     

    0.5s

    0.5s

    0.6s

    2.6s

    10.5s

    0.5s

     

    0.6s

    1.1s

    1.1s

    2.1s

    5.2s

    0.6s

     

    0.2s

    0.3s

    2.0s

    2.6s

    2.8s

    0.2s

    fft(ratio)

    146

    128

    144

    134

    89.0

    111

    fib(ratio)

    182

    210

    209

    94

    88.6

    260

    infilife(ratio)

    96

    94

    95

    97

    89.8

    106

    life(ratio)

    81

    80

    137

    150

    84.2

    133

    Stepanov

    12.12s/6.59

    12.27s/7.29

    12.77s/7.28

    18.60s/3.51

    5.35/0.52

    9.60/4.06

    C++Bench

    2306s

    2,273s

    2,356s

    2,459s

    2,815s

    3,253s



    Table 3: Runtime Test Results for libCstd in Seconds or Millseconds
    Test 5.4 5.3 5.2 5.0 gcc2.95.2 kcc3.9a

    Espresso

    101s

    100s

    104s

    109s

    95s

    88s

    slowio

    21s

    18s

    40s

    36s

    11.5s

     

    map<1,000,000>

    1,518ms

    1,808ms

    1,613ms

    N/A

    2,192ms

    2,581ms

     

    1,979ms

    2,248ms

    2,318ms

     

    3,619ms

    3,551ms

     

    851ms

    865ms

    883ms

     

    1,257ms

    1,815ms

    list<50,000>

    16ms

    16ms

    11ms

    13ms

    16ms

    60ms

     

    117,162ms

    130,687ms

    82,685ms

    138,613ms

    195,252ms

    169,058ms

     

    4ms

    4ms

    4ms

    6ms

    7ms

    44ms

    strings

    1,658ms

    1,759ms

    2,958ms

    1903ms

    575ms

    1,912ms



  2. 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, 5.2, 5.3, and 5.4, 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:

    • Starting in the 5.2, 5.3, and 5.4 C++ compilers, some rarely executed function calls are not expanded. 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.  

    • Functions with local static variables are not inlined. This limitation might be removed in future releases.

    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.

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

    We have made some improvements in the default stream library in C++ 5.4. If you are having problems, here are some workarounds:

    • Use the -library=iostreams option. This uses "classic" iostreams instead of standard streams. These classes are known to be efficient. Unfortunately, using the classic iostreams means that you cannot use stdlib features and the whole program has to be compiled using -library=iostreams. Use of classic iostreams may also require some source code changes.


    • Use STLport by specifying -library=stlport4, along with the -library=iostreams option. In both standard mode (the default) and compatibility mode (-compat) this seems to give a fairly reasonable performance (within 50% of gcc).


    • Use stdlib2.2.3 provided by RogueWave. Unfortunately, this library is not compatible with stdlib2.1.1, which is the default library provided with versions 5.0, 5.1, and 5.2 of the compiler, and therefore may not work if the application requires other middleware.
Updated 2002/03/25

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