United States |
![]() |
![]() |
|
Previous | Contents | Index |
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 |
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:
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.
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:
- A single message identifier
- The keyword for a single message group as follows:
- all---All messages issued by the compiler
- check---All messages about potentially poor coding practices
- portable---All messages about portability
- A single message identifier enclosed in parentheses
- A single message group name enclosed in parentheses
- A comma-separated list of message identifiers or group names (freely mixed) enclosed in parentheses
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 missingreturnenable
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 ) |
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:
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.
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 |
Table 2-2 lists other names predefined by Compaq C++.
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). |
On Compaq Tru64 UNIX systems, Compaq C++ supports the predefined macro names described in Table 2-3.
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.
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 |
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:
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:
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 |
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. |
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 |
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 |
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:
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:
volatile volatile int x; |
volatile struct employee { char *name; int birthdate; /* name, birthdate, job_code, and salary are */ int job_code; /* treated as though declared with volatile. */ float salary; } a,b; /* All members of a and b are volatile-qualified */ struct employee2 { char *name; volatile int birthdate; /* Only this member is qualified */ int job_code; float salary; } c, d; |
const int *intptr; volatile int x; intptr = &x; |
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; } } |
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:
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
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!! } |
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:
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.
The compiler supports the following message information options. Both are disabled by default.
"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.cxxNote 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 |
|