United States    
COMPAQ STORE | PRODUCTS |
SERVICES | SUPPORT | CONTACT US | SEARCH
cxxtitle.gif (12116 bytes)
Compaq C++

Compaq C++
Using Compaq C++ for Tru64 UNIX


Previous Contents Index


Chapter 2
Compaq C++ Implementation

This chapter discusses the features and characteristics specific to the Compaq C++ implementation, including pragmas, predefined names, numerical limits, and other implementation-dependent aspects of the language definition.

2.1 Implementation-Specific Attributes

This section describes pragmas, predefined names, and limits placed on the number of characters and arguments used in Compaq C++ programs.

2.1.1 #pragma Preprocessor Directive

The #pragma preprocessor directive is a standard method for implementing features that differ from one compiler to the next. This section describes pragmas specifically implemented in the compiler for Compaq Tru64 UNIX systems.

Although certain #pragma directives are subject to macro expansion, not all of those new to the language in this and later releases are subject to such expansion. Therefore, users should not write programs that rely on macro expansion of the newer pragmas.

The following #pragma directives are subject to macro expansion. A macro reference can occur anywhere after the pragma keyword.
builtins inline linkage 2 use_linkage 2
dictionary 1 noinline module extern_model
member_alignment message define_template extern_prefix


1Not supported; specific to C on OpenVMS
2Not supported; specific to C

Note that the compiler accepts double underscore forms of keywords for compatibility with user-written header files.

2.1.1.1 #pragma define_template Directive

The #pragma define_template preprocessor directive instructs the compiler to define a template with the arguments specified in the pragma. This pragma has the following syntax:

#pragma define_template name < template-argument-list >

For example, the following statement instructs the compiler to define the template mytempl with the arguments arg1 and arg2:


#pragma define_template mytempl<arg1, arg2> 

For more information on how to use templates with the #pragma
define_template directive, see Chapter 5.

2.1.1.2 #pragma instantiate Directive

The compiler provides several other pragmas that provide finer control over the instantiation process. Instantiation pragmas can be used to control the instantiation of specific template entities or sets of template entities. There are two instantiation pragmas:

For more information on how to use templates with the #pragma instantiate directive, see Chapter 5.

2.1.1.3 #pragma environment Directive

The #pragma environment preprocessor directive offers a way to single-handedly set, save, or restore the states of context pragmas. This directive protects include files from contexts set by encompassing programs and protects encompassing programs from contexts that could be set in header files that they include.

On Compaq Tru64 UNIX systems, the #pragma environment directive affects the following pragmas:

This pragma has the following syntax:

#pragma environment command_line
#pragma environment header_defaults
#pragma environment restore
#pragma environment save

command_line

Sets, as specified on the command line, the states of all the context pragmas. You can use this pragma to protect header files from environment pragmas that take effect before the header file is included.

header_defaults

Sets the states of all the context pragmas to their default values for Compaq Tru64 UNIX systems. This is almost equivalent to the situation in which a program with no command-line options and no pragmas is compiled; except that this pragma sets the pragma message state to #pragma nostandard, as is appropriate for system header files.

restore

Restores the current state of every pragma that has an associated context.

save

Saves the current state of every pragma that has an associated context.

Someone who creates a general purpose library, distributed as an archive or shared library, typically distributes header files that specify the interface to the library. For calls to the library to work, the header file must normally use the same member alignment, pointer size, extern model, and other compilation options as when the library was built.

The header file should contain pragmas to save the user's compilation options, set the correct compilation options for the library, and then restore the user's compilation options when the include file ends. The pragmas let library users choose whatever compilation options are appropriate for their own code without unintentionally affecting the interface to the library.

Not only is the #pragma environment preprocessor directive more convenient than explicitly specifying each individual pragma that controls a compilation option, it is also upwardly compatible. As new pragmas are added to the compiler , #pragma environment header_defaults will be enhanced to set the state of the new pragmas to their default. Thus, a header file that uses #pragma environment sets a known state for not only the current pragmas in the compiler, but future pragmas as well.

Without requiring further changes to the source code, you can use #pragma environment to protect header files from things like language extensions and enhancements that might introduce additional contexts.

A header file can selectively inherit the state of a pragma from the including file and then use additional pragmas as needed to set the compilation to nondefault states. For example:


#ifdef __PRAGMA_ENVIRONMENT 
#pragma __environment save  (1)
#pragma __environment header_defaults (2)
#pragma member_alignment restore (3)
#pragma member_alignment save (4)
#endif 
. 
.  /* contents of header file */ 
. 
#ifdef __PRAGMA_ENVIRONMENT 
#pragma __environment restore 
#endif 

In this example:

  1. Saves the state of all context pragmas
  2. Sets the default compilation environment
  3. Pops the member alignment context from the #pragma member_alignment stack that was pushed by #pragma __environment save (restoring the member alignment context to its preexisting state)
  4. Pushes the member alignment context back onto the stack so that the #pragma __environment restore can pop off the entry

Thus, the header file is protected from all pragmas, except for the member alignment context that the header file was meant to inherit.

2.1.1.4 #pragma extern_model Directive

The #pragma extern_model directive is an OpenVMS pragma that has no effect on Compaq Tru64 UNIX systems.

2.1.1.5 #pragma extern_prefix Directive

The The #pragma extern_prefix directive is an OpenVMS pragma that cannot be used on Compaq Tru64 UNIX systems.

2.1.1.6 #pragma ident Directive

The #pragma ident directive enables insertion of an identification string into the object and executable (usually a version number). The string can be found by using the strings command or, if the string contains a specific recognized substring, by using the what command. For more information on the strings and what commands, see the reference pages.

In version 4.n of Compaq's Compaq Tru64 UNIX, the contents of the string are placed in the .rconst section of the object file and in the executable. In the next major release of the operating system, the contents of the string are placed in the .comment section of the object and the executable. The .comment section is present in the executable on disk but is not mapped into virtural memory at load-time.

The mcs command allows users to perform operations on the comment section .comment of (e)COFF object files. This section can contain information such as the "ident" string from a source file and other information used by components of the Compaq Tru64 UNIX development environment. Users can optionally add their own information to the comment section by using the mcs tool.

For additional information, refer to the Revision Contol System (RCS) and Source Code Control System (SCCS) reference pages.

The pragma has the following syntax:

#pragma ident "user-defined string"

2.1.1.7 #pragma [no]inline Directive

This is a C pragma that cannot be used in C++. Use the inline keyword instead.

2.1.1.8 #pragma [no]member_alignment Directive

By default, the compiler aligns structure members so that members are stored on the next boundary appropriate to the type of the member; that is, bytes are on the next byte boundary, words are on the next word boundary, and so on.

You can use the #pragma member_alignment preprocessor directive to explicitly specify member alignment. For example, using #pragma member_alignment aligns a long variable on the next longword boundary, and it aligns a short variable on the next word boundary.

Using #pragma nomember_alignment causes the compiler to align structure members on the next byte boundary regardless of the type of the member. The only exception to this is for bit-field members.

If used, the nomember_alignment pragma remains in effect until the compiler encounters the member_alignment pragma.

This pragma has the following syntax:

#pragma member_alignment
#pragma member_alignment save
#pragma member_alignment restore
#pragma nomember_alignment [base_alignment]

The base_alignment is word, longword, quadword, and octaword.

restore

Restores the current setting of the member_alignment pragma.

save

Saves the current setting of the member_alignment pragma.

2.1.1.9 #pragma message Directive

The #pragma message preprocessor directive controls the kinds of individual diagnostic messages or groups of messages that the compiler issues.

Default severities used by the compiler can be changed only if they are informationals, warnings or discretionary errors. Attempts to change more severe severities are ignored. If a message severity has not been altered by the command line and is not currently being controlled by a pragma, the compiler checks to see whether the message severity should be changed because of the "quiet" state. If not, the message is issued using the default severity.

Error message severities start out with command line severities applied to default compiler severities. Pragma message severities are then applied. In general, pragma severities override command line severities, which override default severities. The single exception this is that command line options can always be used to downgrade messages. However, command line options cannnot be used to raise the severity of messages currently controlled by pragmas.

The #pragma message directive has the following syntax:

#pragma message disable (message-list)
#pragma message enable (message-list)
#pragma message restore
#pragma message save
#pragma message error
#pragma message informational
#pragma message warning

disable

Suppresses the compiler-issued messages specified in the message-list argument. The message-list argument can be any one of the following:

The message identifier is the name following the message text when the -verbose option is set. A D: preceding the message identifier indicates that the message is discretionary; that is, you can change its severity or disable it. For example, consider the following message:


Non-void function "name" does not contain a return statement. (D:missingreturn). 

The message identifier is missingreturn. To prevent the compiler from issuing this message, use the following directive:


#pragma message disable missingreturn 

enable

Enables the compiler to issue the messages specified in the message-list argument.

restore

Restores the saved state of enabling or disabling compiler messages.

save

Saves the current state of enabling or disabling compiler messages.

The save and restore options are useful primarily within header files. See Section 2.1.1.5.

2.1.1.10 #pragma module Directive

The #pragma module directive is equivalent to the VAX C compatible #module directive. You can use the #pragma module directive when compiling in any mode.

The #pragma module directive has the following syntax:
#pragma module identifier identifier
#pragma module identifier string

The first parameter must be an identifier and specifies the module name. This is primarily used by the linker on OpenVMS systems, but is also used in the heading of the listing file. The second parameter specifies the optional identification. See Section 2.1.1.6 for more information about this.

2.1.1.11 #pragma once Directive

The #pragma once preprocessor directive specifies that the header file is evaluated only once.

The #pragma once directive has the following format:

#pragma once

2.1.1.12 #pragma pack Directive

The #pragma pack preprocessor directive specifies packing alignment for structure and union members. Whereas the packing alignment of structures and unions is set for an entire translation unit by the -ZpN (N = 1, 2, or 4) and the -nomember_align option, the packing alignment is set at the data-declaration level by the pack pragma. The pragma takes effect at the first structure or union declaration after the pragma is seen; the pragma has no effect on definitions.

The #pragma pack directive has the following format:

#pragma pack [(n)]

When you use #pragma pack(n), where n is 1, 2, 4, 8, or 16, each structure member after the first is stored on the smaller member type or n-byte boundaries. If you use #pragma pack without an argument, structure members are packed to the value specified by -Zp. The default -Zp packing size is -Zp8.

The compiler also supports the following enhanced syntax:

#pragma pack( [ [ { push | pop}, ] [ identifier, ] ] [ n ] )

This syntax allows you to combine program components into a single translation unit if the various components use pack pragmas to specify different packing alignments.

Each occurrence of a pack pragma with a push argument stores the current packing alignment on an internal compiler stack. The pragma's argument list is read from left to right. If you use push, the current packing value is stored. If you provide a value for n, that value becomes the new packing value. If you specify an identifier, a name of your choosing, the identifier is associated with the new packing value.

Each occurrence of a pack pragma with a pop argument retrieves the value at the top of an internal compiler stack and makes that value the new packing alignment. If you use pop and the internal compiler stack is empty, the alignment value is that set from the command-line and a warning is issued. If you use pop and specify a value for n, that value becomes the new packing value. If you use pop and specify an identifier, all values stored on the stack are removed from the stack until a matching identifier is found. The packing value associated with the identifier is also removed from the stack and the packing value that existed just before the identifier was pushed becomes the new packing value. If no matching identifier is found, the packing value set from the command line is used and a level-one warning is issued. The default packing alignment is 8.

The new, enhanced functionality of the pack pragma allows you to write header files that ensure that packing values are the same before and after the header file is encountered:


/* File name: include1.h 
*/ 
#pragma pack( push, enter_include1 ) 
/* Your include-file code ... */ 
#pragma pack( pop, enter_include1 ) 
/* End of include1.h */ 

In the previous example, the current pack value is associated with the identifier enter_include1 and pushed, remembered, on entry to the header file. The pack pragma at the end of the header file removes all intervening pack values that may have occurred in the header file and removes the pack value associated with enter_include1. The header file thus ensures that the pack value is the same before and after the header file.

The new functionality also allows you to use code, such as header files, that uses pack pragmas to set packing alignments that differ from the packing value set in your code:


#pragma pack( push, before_include1 ) 
#include "include1.h" 
#pragma pack( pop, before_include1 ) 
In the previous example, your code is protected from any changes to the packing value that might occur in include.h.

2.1.1.13 #pragma pointer_size Directive

The #pragma pointer_size preprocessor directive controls pointer size allocation for the following:

For this pragma to have any effect, you must specify -xtaso, -xtaso_short, -vptr_size, or -vptr_size_short on the cxx command.

This pragma has the following syntax:

#pragma pointer_size {long|64}
#pragma pointer_size {short|32}
#pragma pointer_size restore
#pragma pointer_size save

long, or 64

Sets as 64 bits all pointer sizes in declarations that follow this directive, until the compiler encounters another #pragma pointer_size directive.

short, or 32

Sets as 32 bits all pointer sizes in declarations that follow this directive, until the compiler encounters another #pragma pointer_size directive.

restore

Restores the saved pointer size from the pointer size stack.

save

Saves the current pointer size on a pointer size stack.

The save and restore option are particularly useful for specifying mixed pointer support and for protecting header files that interface to older objects. Objects compiled with multiple pointer size pragmas will not be compatible with old objects, and the compiler cannot discern that incompatible objects are being mixed.

Use of short pointers is restricted to Compaq C++ and the C compilers resident on Compaq Tru64 UNIX systems. Programs should not attempt to pass short pointers from C++ routines to routines written in any other language except the C programming language.

Compaq C++ might require explicit conversion of short pointers to long pointers in applications that use short pointers. You should first port those applications in which you are considering using short pointers, and then analyze them to determine if short pointers would be beneficial.

A difference in the size of a pointer in a function declaration is not sufficient to overload a function.

The compiler issues an error-level diagnostic if:

2.1.1.14 #pragma required_pointer_size Directive

The #pragma required_pointer_size preprocessor directive controls pointer size allocation in the same way as #pragma pointer_size but without interaction with command-line options. This pragma is always enabled, whether or not you specify any pointer size options on the command line. The same syntax, precautions, and restrictions pertain as with #pragma pointer_size. Neither the pragma name nor its arguments are subject to macro expansion.

2.1.1.15 #pragma required_vptr_size Directive

The #pragma required_vptr_size preprocessor directive controls pointer size allocation in the same way as #pragma required_pointer_size, but it applies to virtual function pointers and virtual bases in a C++ class object. This pragma has the following syntax:

#pragma required_vptr_size {long|64}
#pragma required_vptr_size {short|32}
#pragma required_vptr_size restore
#pragma required_vptr_size save

The parameters have the same meaning as with #pragma required_pointer_size (see Section 2.1.1.13).

The #pragma required_vptr_size directive takes effect at the opening brace of a class declaration. This pragma is always enabled, whether or not you specify any pointer size options on the command line.

2.1.1.16 #pragma [no]standard Directive

Use the #pragma nostandard and #pragma standard preprocessor directives to disable all but the most significant messages from header file processing.

This pragma has the following syntax:

#pragma [no]standard

Use #pragma nostandard to mark the start of reduced message activity.

Use #pragma standard to mark the resumption of normal message activity.

2.1.2 Protecting System Header Files

It is important to protect system header files from generating diagnostics and from user-specified changes to member alignment and pointer sizes.

To provide the necessary protection you can do one of the following:

2.1.2.1 Using the Compiler's Header File Protection Option

With this option, you can place special header files in a directory. the compiler processes these special header files before and after each file included with the #include directive from this directory. These special header files are named:

The compiler checks for files with these special names when processing #include directives. If the special prologue file exists in the same directory as a file with the #include directive, the contents of the prologue file are processed just before the file included with the #include directive. Similarly, if the epilogue file exists in the same directory as the file included with the #include directive, it is processed just after that file.

For convenience, you can protect header files using the script supplied in the following directory:


/usr/lib/cmplrs/cxx/protect_system_headers.sh 

This script creates, in all directories in a directory tree that contain header files, symbolic links to Compaq-supplied header prologue and epilogue files.

The default directory tree root assumed by the script is /usr/include, but you can specify other roots.

2.1.2.2 Modifying Each Header File

If you choose to modify each header file that needs protection, you can use the #pragma environment directive, as in the following example:


#pragma __environment save              // Save pointer size 
#pragma __environment header_defaults   // set to system defaults 
 
// existing header file 
 
#pragma__environment restore            // Restore pointer size 

See Section 2.1.1.3 for more information about using this pragma.

2.1.3 Predefined Names

Table 2-1 lists the predefined C++ macros used by Compaq C++. For information on using predefined macros in header files in the common language environment, see Section 3.1.

Table 2-1 Predefined Macros
Macro Description
_BOOL_EXISTS Indicates that bool is a type or keyword
__BOOL_IS_A_RESERVED_WORD Indicates that bool is a keyword
__DATE__ 1 A string literal containing the date of the translation in the form Mmm dd yyyy , or Mmm d yyyy if the value of the date is less than 10
__FILE__ 1 A string literal containing the name of the source file being compiled
__IEEE_FLOAT Identifies floating-point format for compiling the program. The value is always 1 for Compaq's Compaq Tru64 UNIX.
__LINE__ 1 A decimal constant containing the current line number in the C++ source file
__PRAGMA_ENVIRONMENT Indicates that that the pragma environment directive is supported.
__TIME__ 1 A string literal containing the time of the translation in the form of hh:mm:ss
_WCHAR_T Indicates that wchar_t is a keyword


1Cannot be redefined or undefined

Table 2-2 lists other names predefined by Compaq C++.

Table 2-2 Other Predefined Names
Name Description
__cplusplus 1 Language identification name.
__DECCXX Language identification name.
__EXCEPTIONS Indicates that the compiler supports standard C++ exceptions.
__IEEE_FLOAT Indicates that the compiler supports IEEE floating point.
__INITIAL_POINTER_SIZE A decimal constant containing the initial pointer size allocation, as specified by the command line. Valid values are: 0 (no pointer size option set), 32 (short or 32-bit pointers allocated), and 64 (long or 64-bit pointers allocated).


1Cannot be redefined or undefined

On Compaq Tru64 UNIX systems, Compaq C++ supports the predefined macro names described in Table 2-3.

Table 2-3 Predefined Macros Specific to Compaq Tru64 UNIX Systems
Name Description
__alpha System identification name
__arch64__ Identifies systems with 64-bit architecture
__digital__ System identification name
__osf__ System identification name
__unix System identification name
__unix__ System identification name

C++ programmers using both Compaq Tru64 UNIX and OpenVMS Alpha operating systems should use the predefined macro __alpha for code that is intended to be portable from one system to the other.

Predefined Implementation Compatibility Macros

Table 2-4 shows the macro names for the listed command-line options.

Table 2-4 Implementation Compatibility Macros
Command-line Option Macro Name
-fprm __FLT_ROUNDS
-ieee __IEEE_FP
-implicit_include __IMPLICIT_INCLUDE_ENABLED
-long_double_size 1 __X_FLOAT
-global_array_new __GLOBAL_ARRAY_NEW
-pch , -create_pch , -use_pch __PCH_ENABLED
-pthreads _REENTRANT
-rtti __RTTI
-stdnew __STDNEW
-std cfront, -cfront __CFRONT , __STD_CFRONT
-std ms, -ms __MS , __STD_MS
-std arm __STD_ARM
-std ansi __STD_ANSI
-std strict_ansi __STD_STRICT_ANSI
-std strict_ansi_errors __STD_STRICT_ANSI_ERRORS
-threads _REENTRANT, _PTHREAD_USED_D4
-using_std __IMPLICIT_USING_STD


1To be implemented in next major Release of Compaq's Compaq Tru64 UNIX operating system

Predefined __DECCXX_VER Version Number Macro

The __DECCXX_VER macro provides an integer encoding of the compiler version-identifier string that is suitable for use in a preprocessor #if expression, such that a larger number corresponds to a more recent version.

The format of the compiler version-identifier string is:


TMM.mm-eee

Where:

The format of the integer encoding for __DECCXX_VER is:


vvuuteeee

Where:

Table 2-5 __DECC_VER Version-Type Encodings
Type Numerical Encoding Description
T 6 Field-test version
S 8 Customer special
V 9 Officially supported version

The following describes how the __DECCXX_VER integer value is calculated from the compiler version-identifier string:

  1. The major version is multiplied by 10000000.
  2. The minor version (the digits between the period (.) and any edit suffix) is multiplied by 100000 and added to the suffix value (the suffix value has a range of 0 to 999).
  3. If the character immediately preceding the first digit of the major version number is one of those listed in Table 2-5, its numerical encoding is multiplied by 10000.
  4. The preceding values are added together.

The following examples show how different compiler version-identifier strings map to __DECCXX_VER encodings:


ident           __DECCXX_VER 
string          vvuuteeee
 
T5.2-003   -->   50260003 
V6.0-001   -->   60090001 

2.1.4 Translation Limits

The only translation limits imposed in the compiler are as follows:
Limit Meaning
32,767 Characters in an internal identifier or macro name.
32,767 Characters in a logical or physical source line.
32,767 Bytes in the representation of a string literal. This limit does not apply to string literals formed by concatenation.
32,767 Significant characters in an external identifier. A warning is issued if such an identifier is truncated.

2.1.5 Numerical Limits

The numerical limits, as defined in the header files <limits.h> and <float.h>, are as follows:

Numerical limits not described in this list are defined in the International C++ Standard.

2.1.6 Argument-Passing and Return Mechanisms

Even if an argument is declared pass by value, the compiler passes arrays, functions, and class objects with a constructor or destructor by reference. In the first two cases, this behavior is defined by the language standard. In the case of a class with a constructor or a destructor, the compiler calls a copy constructor to copy the object to a temporary location, and then it passes the address of that location to the called function. All other objects are passed by value.

If the return value of a function is a class that has defined a constructor or destructor or is greater than 64 bits, storage is allocated by the caller and the address to this storage is passed in the first parameter to the called function. The called function uses the storage provided to construct the return value.

2.2 Implementation Extensions and Features

This section describes the extensions and implementation-specific features of Compaq C++ for Tru64 UNIX systems.

2.2.1 Identifiers

In Compaq C++, the dollar sign ($) is a valid character in an identifier.

For each external function with C++ linkage, the compiler decorates the function name with a representation of the function's type.

2.2.1.1 External Name Encoding

The compiler uses the external name encoding scheme described in the The Annotated C++ Reference Manual, §7.2.1c.

For the basic types, the external name encoding scheme is exactly the same as that described in The Annotated C++ Reference Manual, as follows:
Type Encoding
void v
char c
short s
int i
long l
float f
double d
long double r
... e
bool jb
wchar_t jw

Class names are encoded as described in The Annotated C++ Reference Manual, except that the Compaq C++ compiler uses the lowercase q instead of uppercase Q, and denotes the qualifier count as a decimal number followed by an underscore, as follows:
Class Notation Encoding
simple Complex 7Complex
qualified X::YY q2_1x2yy

Type modifiers are encoded as follows:
Modifier Encoding
const k
signed g
volatile w
unsigned u
__unaligned b

Type declarators are encoded as follows:
Type Notation Encoding
array [10] a10_
function () x
pointer * p
pointer to member S::* m1S
reference & n
unnamed enumeration type   h

2.2.1.2 Character Limit for Long Names

If an external name has more than 1022 characters, the compiler truncates the name to 1022 characters. The compiler keeps the first 1015 characters intact, reduces (hashes) the remaining characters to a string of 7 characters, and appends the 7 hashed characters to the first 1015.

2.2.2 Order of Static Object Initialization

Static objects are initialized in declaration order within a compilation unit and in link order across compilation units.

2.2.3 Integral Conversions

When demoting an integer to a signed integer, if the value is too large to be represented, the result is truncated and the high-order bits are discarded.

Conversions between signed and unsigned integers of the same size involve no representation change.

2.2.4 Floating-Point Conversions

When converting an integer to a floating-point number that cannot exactly represent the original value, rounds off the result of the conversion to the nearest value that can be represented exactly.

When the result of converting a floating-point number to an integer or other floating-point number at compile time cannot be represented, the compiler issues a diagnostic message.

When converting an integral number or a double floating-point number to a floating-point number that cannot exactly represent the original value, rounds off the result to the nearest value of type float.

When demoting a double value to float, if the converted value is within range but cannot exactly represent the original value, the compiler rounds off the result to the nearest representable float value.

The compiler performs similar rounding for demotions from long double to double or float.

2.2.5 Explicit Type Conversion

In Compaq C++, the expression T() (where T is a simple type specifier) creates an rvalue of the specified type, whose value is determined by default initialization. According to The C++ Programming Language, 3rd Edition, the behavior is undefined if the type is not a class with a constructor, but the ANSI working draft removes this restriction. With this change you can now write:


    int i=int(); // i must be initialized to 0 

2.2.6 The sizeof Operator

The type of the sizeof operator is size_t. In the header file <stddef.h>, this type is defined as unsigned long, which is the type of the integer that holds the maximum size of an object.

2.2.7 Explicit Type Conversion

A pointer takes up the same amount of memory storage as objects of type long (or the unsigned equivalent). Therefore, a pointer can convert to any of these types and back again without changing its value. No scaling occurs and the representation of the value is unchanged.

Conversions to and from a shorter integer and a pointer are similar to conversions to and from a shorter integer and unsigned long. If the shorter integer type was signed, conversion fills the high-order bits of the pointer with copies of the sign bit.

2.2.8 Multiplicative Operators

The semantics of the division (/) and remainder (%) operator are as follows:

In the following cases of undefined behavior detected at compile time, the compiler issues a warning:

2.2.9 Additive Operators

You can subtract pointers to members of the same array. The result is the number of elements between the two array members, and is of type ptrdiff_t. In the header file <stddef.h>, the compiler defines this type as long.

2.2.10 Shift Operators

The expression E1 >> E2 shifts E1 to the right E2 positions. If E1 has a signed type, the compiler fills the vacated high-order bits of the shifted value E1 with a copy of E1's sign bit (arithmetic shift).

2.2.11 Equality Operators

When comparing two pointers to members, the compiler guarantees equality if either of the following conditions hold:

When comparing two pointers to members, the compiler guarantees inequality if either of the following conditions hold:

When created by different address expressions, two pointers to members may compare either as equal or as unequal if they produce the same member when applied to the same object.

2.2.12 volatile Type Specifier

For variables that are modifiable in ways unknown to the compiler, use the volatile type specifier. Declaring an object to be volatile means that every reference to the object in the source code results in a reference to memory. Volatile quantities are also referenced atomically in the hardware; that is, they are fetched and stored with single assembler instructions. Attempts to make a volatile quantity be declared as not aligned incurs a compiler error.

Any object whose type includes the volatile type qualifier indicates that the object should not be subject to compiler optimizations altering references to, or modifications of, the object.

Optimizations that are defeated by using the volatile specifier can be categorized as follows:

An object without the volatile specifier does not compel the compiler to perform these optimizations; it indicates that the compiler has the freedom to apply the optimizations depending on program context and compiler optimization level.

The volatile specifier forces the compiler to allocate memory for the volatile object, and to always access the object from memory. This qualifier is often used to declare that an object can be accessed in some way not under the compiler's control. Therefore, an object qualified by the volatile keyword can be modified or accessed in ways by other processes or hardware, and is especially vulnerable to side effects.

The following rules apply to the use of the volatile specifier:

2.2.13 __unaligned Type Specifier

Use this data-type specifier in pointer definitions to indicate to the compiler that the data pointed to is not properly aligned on a correct address. (To be properly aligned, the address of an object must be a multiple of the size of the type. For example, two-byte objects must be aligned on even addresses.)

When data is accessed through a pointer declared __unaligned, the compiler generates the additional code necessary to copy or store the data without causing alignment errors. It is best to avoid use of misaligned data altogether, but in some cases the usage may be justified by the need to access packed structures, or by other considerations.

Here is an example of a typical use of __unaligned:


typedef enum {int_kind, float_kind, double_kind} kind; 
void foo(void *ptr, kind k) { 
    switch (k) { 
    case int_kind: 
        printf("%d", *(__unaligned int *)ptr); 
        break; 
    case float_kind: 
        printf("%f", *(__unaligned float *)ptr); 
        break; 
    case double_kind: 
        printf("%f", *(__unaligned double *)ptr); 
        break; 
    } 
} 

2.2.14 Linkage Specifications

Specifying linkage other than "C++" or "C" generates a compile-time error.

In object files, the compiler decorates with type information the names of functions with C++ linkage. This permits overloading and provides rudimentary type checking across compilation units. The type-encoding algorithm used is similar to that given in §7.2.1c of The Annotated C++ Reference Manual (see Section 2.2.1.1).

2.2.15 Class Layout

The alignment requirements and sizes of structure components affect the structure's alignment and size. A structure can begin on any byte boundary and can occupy any integral number of bytes.

2.2.15.1 Structure Alignment

Structure alignment represents the maximum alignment required by any member within the structure. When the structure or union is a member of an array, padding is added to ensure that the size of a record, in bytes, is a multiple of its alignment.

Components of a structure are laid out in memory in the order in which they are declared. The first component has the same address as the entire structure. Padding is inserted between components to satisfy alignment requirements of individual components.

2.2.15.2 Bit-Fields

The presence of bit-fields causes the alignment of the whole structure or union to be at least the same as that of the bit-field's base type.

For bit-fields (including zero-length bit-fields) not immediately declared following other bit-fields, their base type imposes the alignment requirements (less than that of type int). Within the alignment unit (of the same size as the bit-field's base type), bit-fields are allocated from low order to high order. If a bit-field immediately follows another bit-field, the bits are packed into adjacent space in the same unit, if sufficient space remains; otherwise, padding is inserted at the end of the first bit-field and the second bit-field is put into the next unit.

Bit-fields of base type char must be smaller than 8 bits. Bit-fields of base type short must be smaller than 16 bits.

2.2.15.3 Access Specifiers

The layout of a class is unaffected by the presence of access specifiers.

2.2.15.4 Class Subobject Offsets

A class object that has one or more base classes contains instances of its base classes as subobjects. The offsets of nonvirtual base class subobjects are less than the offsets of any data members that are not part of base class subobjects.

The offsets of nonvirtual base classes increase in derivation order. The offset of the first nonvirtual base class subobject of any class is 0. For single inheritance, the address of a class object is always the same as the address of its base class subobject.

If a class has virtual functions, an object of that class contains a pointer to a virtual function table (VFPTR).

If a class has virtual base classes, an object of that class contains a pointer to a virtual base class table (VBPTR).

For a class with no base classes, the offset of a VFPTR or VBPTR is greater than the offset of any data members. Thus, the offset of the first data member of a class with no base classes is 0, which facilitates interoperability with other languages. If the leftmost base class of a class with base classes has a VFPTR, a VBPTR, or both, and is not virtual, the class and its base class share the table or tables.

The offsets of virtual base class subobjects are greater than the offset of any data member, and increase in the order of derivation of the virtual base classes. In increasing order, a class object contains the following:

  1. Nonvirtual base class subobjects
  2. Data members
  3. VFPTR (if required)
  4. VBPTR (if required)
  5. Virtual base class subobjects

Consider the following example:


class B1 
{ 
 int x[1]; 
}; 
class B2 : virtual B1 
{ 
 int y[2]; 
 virtual int fl(); 
}; 
class B3 : virtual B2, virtual B1 
{ 
 int z[3]; 
 virtual int f2(); 
}; 
class D : B3 
{ 
 int a[4]; 
 virtual int f1(), f2(), f3(); 
}; 

Figure 2-1 shows the layout of an object of D class for this example.

Figure 2-1 Layout of an Object of D Class


2.2.16 Multiple Base Classes

Within a class object, base class subobjects are allocated in derivation order; that is, immediate base classes are allocated in the order in which they appear in the class declaration.

2.2.17 Temporary Objects

Under the following conditions, the compiler creates temporary objects for class objects with constructors:

Variations in the compiler generation of such temporary objects can adversely affect their reliability in user programs. The compiler avoids introducing a temporary object whenever it discovers that the temporary object is not needed for accurate compilation. Therefore, you should modify or write your programs so as not to depend on side effects in the constructors or destructors of temporary objects.

2.2.17.1 Nonconstant Reference Initialization with a Temporary Object

If your program tries to initialize a nonconstant reference with a temporary object, the compiler generates a warning. For example:


struct A { 
  A(int); 
}; 
void f(A& ar); 
 
void g() { 
  f(5);  // warning!! 
} 

2.2.18 Exception Handling

The compiler optimizes the implementation of exception handling for normal execution, as follows:

In Compaq C++, a procedure with handlers has no intrinsic overhead. For example, procedures with handlers do not have frame pointers or additional register usage.

Some procedures without explicit handlers may have implicit handlers. The compiler creates a handler for each automatic object that has a destructor. The compiler also creates handlers for constructors that initialize subobjects that have destructors. In such a constructor, the compiler creates a handler for each member with a destructor, and a handler for each base class with a destructor.

The -nocleanup option suppresses generation of such implicit handlers, which results in an executable file that is slightly smaller. You should use this option for programs that do not use exception handling or that do not require destruction of automatic objects during exception processing.

Exception specifications in function prototypes that conflict with function definitions are illegal. Chapter 8 describes how the compiler handles such conflicts. Beware of such conflicting exception specifications, particularly if you have exception specifications in prototypes that are in header files accessible to other programs.

2.2.19 File Inclusion

The #include directive inserts external text into the macro stream delivered to the compiler. Programmers often use this directive to include global definitions for use with Compaq C++ functions and macros in the program stream.

The #include directive has the following search path semantics:

2.2.20 Nested Enums and Overloading

The C++ language allows programmers to give distinct functions the same name, and uses either overloading or class scope to differentiate the functions:


void f(int); 
void f(int *); 
class C {void f(int);}; 
class D {void f(int);}; 

Yet, linkers cannot interpret overloaded parameter types or classes, and they display error messages if there is more than one definition of any external symbol. C++ compilers, including Compaq C++, solve this problem by assigning a unique mangled name (also called type safe linkage name) to every function. These unique mangled names allow the linker to tell the overloaded functions apart.

The compiler forms a mangled name, in part, by appending an encoding of the parameter types of the function to the function's name, and if the function is a member function, the function name is qualified by the names of the classes within which it is nested.

For example, for the function declarations at the beginning of this section, the compiler might generate the mangled names f__Xi, f__XPi, f__1CXi, and f__1DXi respectively. In these names, i means a parameter type was int, P means "pointer to", 1C means nested within class C, and 1D means nested within class D.

There is a flaw in the name mangling scheme used by the compiler that can cause problems in uncommon cases. The compiler fails to note in the encoding of an enum type in a mangled name whether the enum type was nested within a class. This can cause distinct overloaded functions to be assigned the same mangled name:


#include <stdlib.h> 
struct C1 {enum E {red, blue};}; 
struct C2 {enum E {red, blue};}; 
 
extern "C" int printf(const char *, ...); 
void f(C1::E x) {printf("f(C1::E)\n");} 
void f(C2::E x) {printf("f(C2::E)\n");} 
 
int main() 
{ 
    f(C1::red); 
    f(C2::red); 
    return EXIT_SUCCESS; 
} 

In the previous example, the two overloaded functions named f differ only in that one takes an argument of enum type C1::E and the other takes an argument of enum type C2::E. Because the compiler fails to include the names of the classes containing the enum type in the mangled name, both functions have mangled names that indicate the argument type is just E. This causes both functions to receive the same mangled name.

In some cases, the compiler detects this problem at compile-time and issues a message that both functions have the same type-safe linkage. In other cases, the compiler issues no message, but the linker complains about duplicate symbol definitions.

If you encounter such problems, you can recompile using the
-distinguish_nested_enums command line switch. This causes the compiler to include the name of class or classes that an enum is nested within when forming a mangled name. This eliminates cases where different functions receive the same mangled name.

Because the -distinguish_nested_enums command-line switch changes the external symbols the compiler produces, you can get undefined symbol messages from the linker if some modules are compiled with -distinguish_nested_enums and some are compiled without it. Because of this, -distinguish_nested_enums might make it difficult to link against old object files or libraries of code.

If you compile your code with -distinguish_nested_enums and try to link against a library that was compiled without the -distinguish_nested_enums command line switch, you will receive an undefined symbol message from the linker if you attempt to call a function from the library that takes an argument of a nested enum type. The mangled name of the function in the library will be different from the mangled name your code is using to call the function.

2.2.21 Guiding Declarations

A guiding declaration is a function declaration that matches a function template, does not introduce a function definition (implies an instantiation of the template body and not a explicit specialization), and is subject to different argument matching rules than those that apply to the template itself---therefore affecting overload resolution. Consider the following example:


template  void f(T) { ... } 
void f(int);                // guiding declaration in non strict_ansi mode 

Because there is no concept of guiding declaration in the current version of the C++ standard, guiding_decls_allowed is FALSE by default. This means, in the example above, that function f is not regarded as an instance of function template f. Furthermore, it means that there are two functions named f that take an int parameter: the one that is explicitly declared, and the one that is an instance of the template. A call of f(0) would invoke the former, whereas a call of f<>(0) would be required to invoke the latter.

2.3 Run-time Type Identification

The compiler emits type information for run-time type identification (RTTI) in the object module with the virtual function table, for classes that have virtual function tables.

You can specify the -[no]rtti option to enable or disable support for RTTI (runtime type identification) features: dynamic_cast and typeid. Disabling runtime type identification can also save space in your object file because static information to describe polymorphic C++ types is not generated. The default is to enable runtime type information features and generate static information in the object file.

Specifying -nortti does not disable exception handling.

The type information for the class may include references to the type information for each base class and information on how to convert to each. The typeinfo references are mangled in the form __T__<class>.

2.4 Message Control Options

The compiler supports the following message control options. The options apply only to discretionary, warning, and informational messages. The tag variable can be the keyword all, a tag obtained from -msg_display_tag, or a number obtained from -msg_display_number. The tag variable is preferred.

-msg_inform tag,...

Alter message(s) severity to informational.

-msg_warn tag,...

Alter message(s) severity to warning.

-msg_error tag,...

Alter message(s) severity to error

-msg_enable tag,...

Enable specific messages that normally would not be issued when using -msg_quiet. You can also use this option to enable messages disabled with -msg_disable.

-msg_disable tag,...

Disable message. This can be used for any nonerror message.

-msg_quiet

Be more like Version 5.n error reporting. Fewer messages are issued using this option. This is the default in arm mode (-std arm). All other modes default to -nomsg_quiet.

You can use the msg_enable option with this option to enable specific messages normally disabled using -msg_quiet.

2.5 Message Information Options

The compiler supports the following message information options. Both are disabled by default.

Note

"D" (meaning discretionary) indicates that the severity of the message can be controlled from the command line. The message number can be used as the tag in the message control options described in Section 2.4. If "D" is not displayed with the message number, any attempt to control the message is ignored.

-msg_display_tag

A more descriptive tag is issued at the end of each message issued. "D" indicates the severity of the message can be controlled from the command line. The tag displayed can be used as the tag in the message control options described in Section 2.4.

Example:


cxx -msg_display_tag t.cxx 
cxx: ... is nonstandard ("int" assumed) (D:nonstd_implicit_int) 
cxx -msg_disable nonstd_implicit_int  t.cxx 

Note that you can change the severity of a diagnostic message if the message is discretionary. For example, -msg_inform nonstd_implicit_int changes the severity to an informational. These options interact with -w0, -w1, and -w2.

-msg_display_number

The error number is displayed at the beginning of each message issued.

Example:


cxx -msg_display_number t.cxx 
cxx: Warning: t.cxx, line 1: #117-D non-void function "f" ... 
cxx -msg_disable 117 t.cxx 


Previous Next Contents Index
  

1.800.AT.COMPAQ

privacy and legal statement