Jump to content United States-English
HP.com Home Products and Services Support and Drivers Solutions How to Buy
» Contact HP
HP.com home

Compaq C++ User Documentation

 

Compaq C++

Compaq C++
Using Compaq C++ for Tru64 UNIX and Linux Alpha


Previous Contents Index


Chapter 4
Porting to Compaq C++

Compaq C++ implements the International C++ Standard, with some differences, as described in the Compaq C++ Release Notes for Tru64 UNIX .

This language differs significantly from The Annotated C++ Reference Manual, implemented by the Version 5.n compilers. When switching from a Version 5.n compiler, you might need to modify your source files, especially if you use the default language mode. In addition, language changes can affect the run-time behavior of your programs. If you want to compile existing source code with minimal source changes, compile using the -std arm option. See The C++ Standard Library for information on and changes to the Standard Library.

This chapter describes ways to avoid having the compiler reject program code that previously worked with other C++ implementations that adhere less strictly to the C++ language definition. References to applicable portions of The C++ Programming Language, 3rd Edition indicate where you can find additional help.

4.1 Compatibility with Other C++ Compilers

In default mode ( -std ansi ), the compiler implements most features of the International C++ Standard including:

  • Run-time type identification (RTTI), with dynamic_cast and the typeid operator (see Section 2.3)
  • New-style casts ( static_cast , reinterpret_cast , and const_cast ).
  • Array new and delete

For compatibility with previous versions, the compiler provides the following language mode options:

-std ansi

Specify this option if you want an ANSI C++ compiler that supports some commonly used extensions and is somewhat less strict than the standard. This is the default compiler mode.

If you want to use ANSI mode but find that the compiler generates too many diagnostics in that mode, you can use the -msg_quiet option with the -std ansi option. The -msg_quiet option relaxes error checking and suppresses or reduces the severity of many diagnostics. It also suppresses many warnings that are generated in ANSI mode but were not issued by Version 5.6. For information on message control options, see Message Control Options in Chapter 2 .

-std arm

Specify this option if you want to compile programs developed using Version 5.n and want to minimize source changes.

If you usually want your compilations done in this mode and don't want to specify -std arm on each cxx command, define environment variable DEC_CXX as follows:


     setenv DEC_CXX "-std arm" 

You can place a single vertical bar ("|") within the variable. All arguments preceding the bar are evaluated before any explicit command-line arguments; all arguments following the bar are evaluated afterwards. In the absence of a vertical bar, all arguments are evaluated before the explicit command-line arguments.

Specify -v to obtain the definition of DEC_CXX For example, assuming the definition of DEC_CXX above, the cxx -v command results in the following:


     % cxx -v 
     $DEC_CXX contains: -std arm 

To enhance compatibility with other C++ compilers, Compaq C++ supports options that direct the compiler to interpret the source program according to certain rules followed by other implementations:

-std cfront

Specify this option if you want to compile programs developed using cfront or a compiler based on cfront.

-std gnu

Use this option if you want to compile programs developed using the GNU compiler. This option also defines the __STD_GNU macro. The following changes in behavior are provided for compatibility with the GNU C++ compiler:
  • These options are enabled by default:


    -alternative_tokens, -tlocal, and -no_implicit_include 
    
  • Access control is not enforced for types defined inside a class.
  • Unrecognized character escape sequences in string literals produce an informational instead of a warning message.
  • The __inline keyword is enabled and is equivalent to inline .
  • When overloading, enum types are treated as integral types.

The following known incompatibilities are not addressed in the -std gnu mode:

  • The compiler strictly enforces the requirement to define functions before they are used. This requirement also applies to built-in functions such as strlen .

-std ms

Specify this option if you want the compiler to accept additional Microsoft Visual C++ extensions.

-std strict_ansi

Specify this option if you want the compiler to enforce the ANSI C++ standard strictly but permit some ANSI violations that should be errors to be warnings.

-std strict_ansi_errors

Specify this option if you want strict_ansi and also want errors to be issued for all ANSI violations.

With either -std ms or -std cfront you might also want to specify -msg_quiet to reduce the number of diagnostic messages generated. For details, see Message Control Options in Chapter 2 .

4.2 Compatibility With Version 5.n Compilers [Tru64]

This section provides information about differences between the current compiler and Version 5.n compilers.

4.2.1 Compiler Version Options [Tru64]

This release provides the following compiler version options:

-newcxx

Invokes the Version 6.3 compiler. This is the default.

-oldcxx

Invokes a bug fix update to the Version 5.7 compiler.

The -oldcxx option is provided for cases where the Version 6.n compiler requires excessive source changes or for problems in using that compiler. If extensive source changes are required to correct errors, try using the -std arm option. For excessive warnings, try the -msg_quiet option.

If you want -oldcxx to be the default, define the DEC_CXX environment variable as follows:


     setenv DEC_CXX "-oldcxx" 

Before you can use the -oldcxx option, the optional -oldcxx subset must be installed. See the Compaq C++ Installation Guide for Tru64 UNIX .

4.2.2 Language Differences [Tru64]

Be aware of the following language differences between the current compiler and Version 5.n compilers:

  • The most important language differences result from the implementation of the International C++ Standard in Version 6.n. If you want to compile existing source code with minimal source changes, specify the -std arm option.
  • Because the current compiler performs more error checking than previous versions, it generates significantly more diagnostic messages. However, you can use the -msg_quiet option to relax error checking and reduce the severity of many diagnostics. See Compaq C++ Implementation .
    With some option combinations, you might see unwanted diagnostics from system header files. If so, you might obtain better results if you protect your system header files using the script provided in /usr/lib/cmplrs/cxx/protect_system_headers.sh. For more information, see Protecting System Header Files .
  • The current compiler fixes several class member access bugs in previous versions. Some illegal programs that compiled cleanly with previous versions require modification to compile with the current version.
  • The following keywords, introduced with the International C++ Standard, are always keywords in all compiler modes:


    bool, const_cast, explicit, export, false, mutable, dynamic_cast, 
    reinterpret_cast, static_cast, truce, typeid, typename, wchar_t 
    

    Alternative representation keywords are as follows:


    and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor, xor_eq 
    
  • Creation of temporaries and their lifetimes vary among compiler modes.
  • Taking the address of a bit field is not allowed in the current version.
  • Macro expansion in pragmas can give different results in the current and previous versions.
  • The following are distinct types in the current version; they were the same type in previous versions:


       typedef void (*PF)();             // Pointer to an extern "C++" function 
       extern "C" typedef void (*PCF)(); // Pointer to an extern "C" function 
       void f(PF); 
       void f(PCF); 
    
  • The current version does not allow converting a pointer to member from a derived class to a virtual base class.
  • Calling a nonstatic member function through a null pointer is undefined behavior. Certain cases that used to run without errors in previous versions no longer run in the current version. For example:


         #include <iostream.h> 
     
         struct A { 
             int a; 
         }; 
     
         struct D : public virtual A 
         { 
             A* toA(){ return (A*) this; } 
         }; 
     
         main () 
         { 
             D* d = NULL; 
             A* ad = d->toA();  // will seg fault 
             if (ad==NULL) cout << "ok"; 
         } 
    
  • In the current version, bool is a built in type. In previous versions, it is user defined, typically as int in system header files. Mangling therefore differs between the current and previous versions.
    In the current version, the size of bool is 1. In previous versions, bool is user defined, typically as int with a size of 4.
    In the current version, the size of a boolean expression (sizeof(a && b)) is 1. In previous versions, the size is 4, independent of the size of bool .
  • The current version does not cause pragmas to become effective within function bodies when scanning template definitions.
  • The current version does not permit dropping qualifiers on pointer assignments. For example, the following is not permitted:


         volatile int *vptr;  int *ptr=vptr;  
    
  • The current version does not allow the "virtual" storage class modifier to be used with member function definitions outside a class.
  • The current version does not allow declaration of pointers to members of type void. For example, the following is not allowed:


    typedef void Z::* any_ptom; 
    

4.2.3 Implementation Differences [Tru64]

Users should be aware of the following implementation differences between the current and previous versions of the compiler:

  • The automatic template instantiation model has been redesigned for the current version. See Using Templates .
  • The current version does not support the -show statistics option implemented in Version 5.7.
  • The current version drops qualifiers on parameters when determining the function type, as dictated by the International C++ Standard. For instance, in the following example, the function declarations are the same function.


         f(const int p1); 
         f(int p1); 
    

    For compatibility with previous versions, if qualifiers are included in function declarations, they are also included in the mangled name.
  • The current and previous versions differ in interpreting undefined behavior, as when incrementing takes effect in this example:


         f(i++, i++); 
    
  • The current version cannot handle a #pragma define_template that spans multiple lines without the backslash ( \ ) delimiter. Version 5.6 can handle this without problems.
  • The current version displays #line number in -E output. The previous version displays #number .
  • After encountering an illegal multibyte character sequence, the current version issues a warning diagnostic and continues processing. The previous version issues an error and stops processing.

4.2.4 Library Differences [Tru64]

Aspects of memory allocation and deallocation have changed. For detailed information, see The C++ Standard Library .

4.3 Using Classes

This section discusses porting issues pertaining to C++ classes.

4.3.1 Friend Declarations

When making friend declarations, use the elaborated form of type specifier. The following code fragment implements the legal and comments out the illegal friend declaration:


class Y; 
class Z; 
class X; 
   //friend Y;  ** not legal 
   friend class Z; // legal 
};      

4.3.2 Member Access

Unlike some older C++ implementations, Compaq C++ strictly enforces accessibility rules for public , protected , and private members of a base class. For more information, see The C++ Programming Language, 3rd Edition.

4.3.3 Base Class Initializers

Unlike some older C++ implementations, Compaq C++ requires you to use the base class name in the initializer for a derived class. The following code fragment implements a legal initializer and comments out an illegal initializer:


class Base { 
    // ...
public: 
    Base (int); 
}; 
class Derived : public Base { 
    // ...
public: 
    // Derived(int i) : (i)  {/* ...*/}    ** not legal        
    Derived(int i) : Base(i) {/* ...*/} // ** legal, supplies class name 
}; 

For more information, see The C++ Programming Language, 3rd Edition.

4.4 Undefined Global Symbols for Static Data Members

When a static data member is declared, the compiler issues a reference to the external identifier in the object code, which must be resolved by a definition. On Compaq Tru64 UNIX and Linux Alpha systems, the compiler does not support the declaration anachronism shown in The C++ Programming Language, 3rd Edition.

For example, consider the following code fragment:


#include <stdio.h> 
class C { 
public: 
        static int i; 
        }; 
//missing definition 
//int C::i = 5; 
int main () 
{ 
    int x; 
    x=C::i; 
    printf("x %d\n",x); 
    return 0; 
} 

The compiler does not issue any messages during compilation; however, when you attempt to link a program containing this code, the linker issues a message similar to the following:


ld: 
Error: Undefined: 
i__1C 

4.5 Functions and Function Declaration Considerations

Compaq C++ requires the use of function definitions as described in The C++ Programming Language, 3rd Edition. For examples of outdated syntax not allowed in Compaq C++, see The C++ Programming Language, 3rd Edition.

Because all linkage specifications for a name must agree, function prototypes are not permitted if the function is later declared as an inline function. The following code is an example of such a conflicting function declaration:


int f(); 
inline int f() { return l; } 

In this example, f is declared with both internal and external linkage, which causes a compiler error.

Similarly, the declaration int f(i,j) causes an error even when the declaration is defined with the "C" linkage specification, because the linkage specification has no effect on the semantics of the declaration.

4.6 Using Pointers

This section explains how to use pointers effectively in Compaq C++.

4.6.1 Pointer Conversions

In Compaq C++, you cannot implicitly convert a const pointer to a nonconstant pointer. For example, char * and const char * are not equivalent; explicitly performing such a cast can lead to unexpected results.

For more information, see The C++ Programming Language, 3rd Edition.

4.6.2 Bound Pointers

Binding a pointer to a member function with a particular object as an argument to the function is not allowed in Compaq C++. For more information on the illegality of casting bound pointers, see The C++ Programming Language, 3rd Edition.

4.6.3 Constants in Function Returns

Because the return value cannot be an lvalue, a constant in a function return has no effect on the semantics of the return. However, using a constant in a function return does affect the type signature. For example:


static int f1( int a, int b) {;} 
const int (* const (f2[])) (int a, int b) = {f1}; 

In this example, the referenced type of the pointer value f1 in the initializer for f2[] is function (signed int, signed int) , which returns signed int . This is incompatible with function (signed int, signed int) , which returns const signed int .

You can omit the const of int because it affects only the constant return signature.

4.6.4 Pointers to Constants

The following example shows a type mismatch between a pointer to a char and a pointer to a const char :


void foo (const char* argv[]) {} 
int main() 
{ 
        static char* args[2] = {"foo","bar"}; 
/* 'In this statement, the referenced type of the pointer value 
 "args" is "pointer to char"' which is not compatible with 
 "pointer to const char"'*/ 
        foo (args); 
return 0; 
} 

You can correct this example by changing static char to static const char . Use an explicit type cast to get an argument match only if no other option is available; such a cast may break on some C++ implementations.

4.7 Using typedefs

Using a synonym after a class , struct , or union prefix is illegal. Using a synonym in the names for constructors and destructors within the class declaration itself is also illegal.

In the following example, the illegal typedef specifier is commented out:


typedef struct { /* ...*/ } foo; 
// typedef struct foo foobar;             ** not legal 

For more information, see The C++ Programming Language, 3rd Edition.

4.8 Initializing References

Compaq C++ warns against initializing nonconstant references to refer to temporary objects. The following example demonstrates the problems that can result:


static void f() 
{ 
    int i = 5; 
    i++;        // OK 
    int &ri = 23; 
    ri++;       // In the initializer for ri, the initialization of a 
                // non-const reference requires a temporary for "23". 
} 

The issue of reference initialization arises most often in assignment operators and in copy constructors. Wherever possible, declare all reference arguments as const .

For more information, see The C++ Programming Language, 3rd Edition.

4.9 Using the switch and goto Statements

Branching around a declaration with an explicit or implicit initializer is not legal, unless the declaration is in an inner block that is completely bypassed. To satisfy this constraint, enclose the declaration in a block. For example:


int i; 
 
switch (i) { 
case 1: 
    int l = 0;     //not initialized at this case label 
    myint m = 0;   //not initialized at this case label 
    { 
    int j = 0;     // legal within the braces 
    myint m = 0;   // legal within the braces 
    } 
case 2: 
    break; 
// ...
} 

For more information on using the switch statement, see of The C++ Programming Language, 3rd Edition.

4.10 Using Volatile Objects

You must supply the meaning of copy constructing and assigning from volatile objects, because the compiler generates no copy constructors or assignment operators that copy or assign from volatile objects. The following example contains examples of such errors, as noted in the comments:


class A { 
public: 
  A() { } 
  // A(volatile A&) { } 
  // operator=(volatile A&) { return 0; } 
}; 
 
void foo() 
{ 
  volatile A va; 
  A a; 
 
  A cca(va);  // error - cannot copy construct from volatile object 
  a = va;     // error - cannot assign from volatile object 
 
  return; 
} 

For more information, see The C++ Programming Language, 3rd Edition.

4.11 Preprocessing

Compaq C++ allows identifiers, but not expressions, on the #ifdef preprocessor directive. For example:


// this is not legal 
// #ifdef KERNEL && !defined(__POSIX_SOURCE) 

The following is the legal alternative:


// use this instead 
#if defined(KERNEL) && !defined(__POSIX_SOURCE) 

For more information, see The C++ Programming Language, 3rd Edition.

4.12 Managing Memory

The proper way to manage memory for a class is to overload the new and delete operators. This is in contrast to some older C++ implementations, which let you manage memory through assignment to the this pointer.

For more information, see The C++ Programming Language, 3rd Edition.

Program developers must take care that any user-defined new operators always return pointers to quadword aligned memory.

4.13 Size-of-Array Argument to delete Operator

If a size-of-array argument accompanies a delete operator, the compiler ignores the argument and issues a warning. The following example includes an anachronistic use of the delete operator:


int main() 
{ 
        int *a = new int [20]; 
        int *b = new int [20]; 
        delete[20] a;     //old-style; argument ignored, warning issued 
        delete[] b; 
return 0; 
} 

4.14 Flushing the Output Buffer

Do not depend on the newline character (\ n ) to flush your terminal output buffer. A previous stream implementation might have done so, but this behavior is not in conformance with Version 2.0 of the AT&T iostream library. If you want to flush the output buffer, use the endl manipulator or the flush member function.

4.15 Missing Parenthesis Error Message

Situations occur in which a simple typographical error generates a missing parenthesis error message. In the following example, the class name CaseSensitive is incorrectly specified as Casesensitive in the constructor declaration:


class CaseSensitive { 
    void Test( const Casesensitive &foo ); 
}; 

As the compiler parses the argument declaration list:

  1. It interprets const as a type specifier.
  2. It interprets Casesensitive as a different indentifier.

Among the next legal tokens are the equal sign, comma, and closing parenthesis. Upon finding an ampersand, the compiler expects a closing parenthesis. With all other possibilities exhausted, the compiler has what appears to be a legal argument declaration list, after which the closing parenthesis is the only allowable token. The compiler expected one thing but encountered something else. Often, inserting newline characters can isolate the offending token.

4.16 Link Using cxx

Use the cxx command, not the ld command, to build a binary; otherwise, the run-time startup and other crucial code will not build properly. Some symptoms of linking incorrectly are

  • Segmentation faults (signal SEGV ) occur while running your program
  • Undefined symbols
  • Static constructors not being executed

4.17 Source File Extensions

Compaq C++ automatically treats files with a .c extension as C language source files and passes the files to the cc command. For Compaq C++ to compile them, your files must have one of the following extensions:


.cxx        .CXX 
.cpp        .CPP 
.cc         .CC 
.C          .C++ 

You also can use the -x option to direct the compiler to ignore file-name extensions and treat all named files, other than those with an .a or .o extension, as C++ source files.

4.18 Incrementing Enumerations

Some other C++ implementations let you perform integer arithmetic, including ++, on enumerated types; Compaq C++ does not allow this.

4.19 Scope of Variables Declared on a for Statement

If the for-init-statement is a declaration, the scope of the name(s) declared extends to the end of the for-statement, as shown in the following example:


int i=5; 
 
void f() 
{ 
  for (int i=0;i<10;i++); 
 
  printf("%d\n",i); 
} 

When compiled -std ansi , strict_ansi , or strict_ansi_errors , the compiler produces the results expected by the International C++ Standard. In all other modes, the compiler produces the results expected by The Annotated C++ Reference Manual; that is, the scope of the names declared extends to the end of the enclosing scope.

4.20 Guidelines for Writing Clean 64-Bit Code

Paying careful attention to data types can ensure that your code works on both 32-bit and 64-bit systems. Use the following guidelines to write clean 64-bit code:

  • Variables that should be 32 bits in size on both 32-bit systems and 64-bit Alpha systems should be declared as int (not long ). Even better would be to declare them as int32 , which is a macro you define to be int .
  • Variables that should be 32 bits in size on a 32-bit system and 64 bits in size on a 64-bit Alpha system should be declared as long . Even better would be to declare them as long-int , which is a macro you define to be long .
  • Check any variables in your code that are declared long . If the variable must be 32 bits in size, you must change its type to int .
  • Variables declared as int should not be used to hold an address; sizeof (int) does not equal sizeof (char *) on Alpha systems.
  • Remember that register variables and unsigned variables default to int (32 bits).
  • Constants are 32-bit quantities by default. Performing shift operations or bit operations on constants will give 32-bit results. You must add L to the constant to get a 64-bit result. For example:


    long foo, bar; 
    foo = 1L << bar; 
    
  • Using a 0 where you should use NULL generates a 32-bit constant. On Alpha systems, this could yield 0 in the low 32 bits and useless data in the high 32 bits when passed into a function that accepts a variable number of arguments. Using NULL from the <stdio.h> header file provides the correct value.
  • Assigning to a char is not atomic on Alpha processors before the EV56. You will obtain a load of 32 or 64 bits, followed by byte operations to extract, mask, and shift the byte, followed by a store of 32 or 64 bits.
  • Bit-fields declared as int on Alpha systems generate load/store 32 bits. Bit-fields declared as long on Alpha systems generate load/store 64 bits.
  • If you do not explicitly declare the formal parameters to functions, their sizes may not match the caller sizes. The default is int , which truncates 64-bit addresses.
  • The %d and %x format specifiers print 32 bits of data. Use %ld or %lx with printf to print 64 bits of data. You can use %p on both 32- and 64-bit systems to print the value of pointers.

For more information, see the Tru64 UNIX Migration Guide.


Previous Next Contents Index
About PDF files: The PDF files on this site can be read online or printed using Adobe® Acrobat® Reader. If you do not have this software on your system, you may download it from Adobe's website.
Privacy statement Using this site means you accept its terms C++ support
© 2008 Hewlett-Packard Development Company, L.P.