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 and Linux Alpha systems.
Note that the compiler accepts double underscore forms of keywords (for example, #pragma __inline ) for compatibility with user-written header files.
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 |
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 Section 5.4.
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 Section 5.4.
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 and Linux Alpha systems, the #pragma environment directive affects the following pragmas:
#pragma member_alignment/pack
#pragma message
#pragma extern_model
[Tru64] #pragma pointer_size/required_pointer_size
#pragma required_vptr_size
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 and Linux Alpha 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_prefix Directive
The
#pragma extern_prefix
directive is an OpenVMS pragma that cannot be used on Compaq
Tru64 UNIX and Linux Alpha systems.
2.1.1.5 #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.6 #pragma [no]inline Directive
This is a C pragma that cannot be used in C++. Use the
inline keyword instead.
2.1.1.7 #pragma intrinsic Directive
The #pragma intrinsic directive specifies that calls to the specified functions are intrinsic. Intrinsic functions are functions in which the compiler generates optimize code in certain situations, possibly avoiding a function call.
The #pragma intrinsic directive has the following syntax:
#pragma intrinsic (function1[,function2, ...]) |
You can use this directive to make intrinsic the default form of functions that have intrinsic forms. The following functions have intrinsic forms:
abs fabs labs alloca |
You can use the #pragma function directive to override the #pragma intrinsic directive for specified functions.
The function must have a declaration visable at the time the compiler
encounters the
#pragma intrinsic
directive. The compiler takes no action if the compiler does not
recognize the specified function name as an intrinsic.
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] |
When #pragma member_alignment is used, the compiler aligns structure members on the next boundary appropriate to the type of the member, rather than on the next byte. For example, a long variable is aligned on the next longword boundary; a short variable is aligned on the next word boundary. Consider the following example:
#pragma nomember_alignment struct x { char c; int b; }; #pragma member_alignment struct y { char c; /*3 bytes of filler follow c */ int b; }; main () { printf( "The sizeof y is: %d\n", sizeof (struct y) ); printf( "The sizeof x is: %d\n", sizeof (struct x) ); } |
The optional base_alignment parameter can be used to specify the base-alignment of the structure. Use one of the following keywords for the base_alignment:
byte (1 byte)
word (2 bytes)
longword (4 bytes)
quadword (8 bytes)
octaword (16 bytes)
The
#pragma member_alignment save
and
#pragma member_alignment restore
directives can be used to save the current state of the
member_alignment
and to restore the previous state, respectively. This feature is
necessary for writing header files that require
member_alignment
or
nomember_alignment
, or that require inclusion in a
member_alignment
that is already set.
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:
- 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.3.
2.1.1.10 #pragma module Directive
When you compile source files to create an object file, the compiler assigns the first of the file names specified in the compilation unit to the name of the object file. The compiler adds the .o file extension to the object file. Internally, the operating system and the debugger recognize the object module by the file extension.
To change the system-recognized module name and version number, use the #pragma module directive.
You can find the module name and the module version number listed in the compiler listing file and the linker load map.
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.5 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 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 the 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 [Tru64]
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:
__DECC_include_prologue.h
__DECC_include_epilogue.h
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 Macro Names
The following tables list predefined C++ macros used by Compaq C++. These names are useful in preprocessor #ifdef and #if defined directives to isolate code intended for one of the particular cases. The names can be used anywhere you use other defined names, including within macros.
For information on using predefined macros in header files in the common language environment, see Section 3.1.
__cplusplus | Defined by the C++ compiler when compiling source code. |
__LINE__ | The current line number (a decimal integer). |
__FILE__ | The current file name (a character string). |
__DATE__ | The source file's compilation date (a character string literal of the form "Mmm dd yyyy", where the month names are the same as those generated by the asctime function, and the first character of "dd" is a space character if the value is less than 10.) |
__STDC__ | [Linux] Compiler supports ANSI C language constructs. |
__TIME__ | The source file's compilation time (a character string literal of the form "hh:mm:ss"), as in the time generated by the asctime function. |
Macro Name | Description |
---|---|
__alpha 1, __alpha__, __ALPHA | System has an Alpha processor. |
__arch64__ | System with 64-bit architecture. |
__digital__ | System identification name on Tru64 UNIX. |
__linux, __linux__ 1 | System is a Linux Alpha system. |
linux | [Linux] System is a Linux Alpha system. Provided for GNU compatibility; use of this macro is not recommended. However, some open-source libraries use it. |
__osf__ | System is a Tru64 UNIX system. |
__unix, __unix__ | System is a UNIX system. |
unix | [Linux] System is a UNIX system. Provided for GNU compatibility; use of this macro is not recommended. |
Macro Name | Description |
---|---|
_BOOL_EXISTS | Indicates that bool is a typedef or keyword |
__BOOL_IS_A_RESERVED_WORD | Indicates that bool is a keyword |
__DECCXX | Compiler is Compaq C++. |
__DECCXX_VER | Compiler version identifier in string form. |
__IEEE_FLOAT | The compiler supports IEEE floating point. |
__INITIAL_POINTER_SIZE | [Tru64] A decimal constant containing the initial pointer size allocation, as specified by the command line. Legal values on Tru64 UNIX are: 0 (no pointer size option set), 32 (short or 32-bit pointers allocated), and 64 (long or 64-bit pointers allocated). The only legal value on Linux Alpha is 0. |
__INTRINSICS | Compaq C++ recognizes intrinsic functions. |
__LANGUAGE_C__, __LANGUAGE_C,
LANGUAGE_C |
[Linux] Compiler translates C language constructs and keywords. Provided for GNU compatibility; use of these macros is not recommended. |
_LONGLONG | Compiler supports "long long" type declarations. |
__PRAGMA_ENVIRONMENT | The compiler supports pragma environment. |
__STDC_VERSION__ | Version number of ANSI C support, in string form; not defined by the C++ compiler. |
_SYSTYPE_BSD | Compiler supports BSD type system headers. |
_WCHAR_T 1, __WCHAR_T 2 | wchar_t is a typedef or a keyword. |
__X_FLOAT | System has default long double size of 128 bits. |
Predefined Language Dialect Macro Names
Table 2-4 shows the macro names defined by specifying the listed command-line options.
Command-line Option | Macro Name |
---|---|
-std arm | __STD_ARM |
-std ansi | __STD_ANSI |
-std cfront, -cfront | __CFRONT, __STD_CFRONT |
-std gnu | __STD_GNU |
-std ms, -ms | __MS, __STD_MS |
-std strict_ansi | __STD_STRICT_ANSI, __PURE_CNAME |
-std strict_ansi_errors |
__STD_STRICT_ANSI_ERRORS,
__PURE_CNAME |
Predefined Implementation Compatibility Macro Names
Table 2-5 shows the macro names defined by specifying the listed command-line options.
Command-line Option | Macro Name |
---|---|
-fprm | __FLT_ROUNDS |
-global_array_new | __GLOBAL_ARRAY_NEW |
-ieee | __IEEE_FP |
-implicit_include | __IMPLICIT_INCLUDE_ENABLED |
-long_double_size | __X_FLOAT |
-model ansi | __MODEL_ANSI |
-model arm | __MODEL_ARM |
-pch , -create_pch , -use_pch | __PCH_ENABLED |
-pthreads | _REENTRANT |
-pure_cname | __PURE_CNAME |
-rtti | __RTTI |
-stdnew | __STDNEW |
-threads | _REENTRANT, _PTHREAD_USED_D4 |
-using_std | __IMPLICIT_USING_STD |
Predefined __DECCXX_VER Version Number Macro
These macros provide 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 --> 50,260,003 V6.0-001 --> 60,090,001 |
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. Linking further limits external identifiers to 1022 characters. |
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 C++
International 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 that have non-trivial copy constructors or destructors by reference. In the first two cases, this behavior is defined by the language standard.
Class objects have non-trivial copy constructors if they have user-declared copy constructors, or if they have virtual bases, virtual functions, or non-static data members that require virtual table initialization.
In the case of a class with a non-trivial copy 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 a non-trivial
copy constructor or destructor or is greater than 64 bits, storage is
allocated by the caller, and the address of 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 and Linux Alpha 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 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
The order of static objects is defined by the implementation. Relying
on this order is not good programming practice. For Compaq C++,
static objects are initialized in declaration order within a
compilation unit. On Tru64 UNIX the order across compilation units
is the order on the link. On Linux Alpha, the order across
compilation units is reversed. The order is a function of the linker,
not the compiler, and is the same as g++ on Linux.
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, the compiler 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, the compiler 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>
, 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:
Integer overflow
Division by 0
Remainder by 0
You can subtract pointers to members of the same array or a pointer
that points just past the end of an 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 as equal if they produce the same member when applied to
the same object; otherwise, they are unequal.
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.
2.2.15 Temporary Objects
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.15.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. 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.17 File Inclusion
The #include directive inserts external text into the source 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:
Inheritance means that one object or file derives some of its contents by virtual copying from another object or file. In the case of C++ header files, for example, one header file includes another and then replaces or adds something.
If the inheriting header file and the base header file have different names, inheritance is straightforward: simply write #include "base" in the inheriting file.
However, if it is necessary to give the inheriting file the same name as the base file, inheritance is less straightforward.
For example, suppose an application program uses the system header file sys/signal.h , but the version of /usr/include/sys/signal.h on a particular system does not do what the program expects. You could define a local version, perhaps with the name /usr/local/include/sys/signal.h , to override or add to the one supplied by the system. Using the -I option for compilation, you could then write a file sys/signal.h that does what the application program expects. But if you try to include the standard sys/signal.h in your version using #include <sys/signal.h> , the result is an infinite recursion and a fatal compilation error.
Specifying #include </usr/include/sys/signal.h> would include the correct file, but that technique makes maintenance difficult because it assumes that the location of the system header files will never change.
You should therefore use the #include_next directive, which means "Include the next file with this name." This directive works like #include but starts searching the list of header file directories after the directory in which the current file was found.
Suppose you specify
-I /usr/local/include
, and the list of directories to search also includes
/usr/include
. Then suppose that both directories contain a file named
sys/signal.h
. Specifying
#include <sys/signal.h>
finds your version of the file in the
/usr/local/include
directory. If that file contains
#include_next <sys/signal.h>
, the search starts after that directory and finds the correct system
standard file in
/usr/include
.
2.2.19 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);}; |
All compilers, including Compaq C++, use name mangling to assign unique names to these functions. These unique mangled names allow the linker to tell the overloaded functions apart.
The compiler forms a mangled name by appending an encoding of the parameter types of the function to the function's name. If the function is a member function, the compiler qualifies the function name 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 generates 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.20 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 <class T> void f(T) { printf("In template f\n"); } void f(int); int main() { f(0); // invokes non-template f f<>(0.0); // invokes template f return 0; } void f(int) { printf("In non-template f\n"); } |
Because there is no concept of guiding declaration in the current
version of the C++ International Standard, the function
f
in the example is not regarded as an instance of function
template f
. Furthermore, there are two functions named
f
that take an
int
parameter. A call of
f(0)
would invoke the former, while 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 Implementation of Polymorphism During Object Construction
During object construction, the compiler allocates intermediate virtual function tables on the constructor's stack for each virtual base class. If the object currently under construction is a subobject of a more derived object, the compiler uses the local virtual function tables. Each table's Run-Time Type Identification contains the offset to convert successfully from the virtual base class to the object being constructed. This offset is known at runtime through the object's virtual base table and can be assigned into the corresponding local virtual function table's RTTI information. Virtual function calls that occur during construction can then correctly adjust the "this" pointer.
Compiler-generated thunks to this pointers ("interludes") take advantage of the RTTI's offset:
void __INTER__1S_funcname_2Vn_type(Vn *this) { this = this + this.vptr[RTTI_INDEX].offset; funcname(this); } |
Again, this offset represents the proper delta to convert from a virtual base class to the more derived class that overrode the virtual function declared within the virtual base class. For example:
struct V { V() { f(); } virtual void f() { g(); } virtual void g() { printf("V::g, this = %x\n", this); } }; struct S : virtual V { S() { f(); } virtual void g() { printf("S::g, this = %x\n", this); } }; struct D : virtual S { D() { f(); } virtual void g() { printf("D::g, this = %x\n", this); } }; S's constructor would be rewritten as follows: S() { if (most_derived) { Assign btbls; Call virtual base class constructors; } if (!most_derived) { /* For each virtual base generate the following code */ /* Vn represents a particular virtual base class */ int *__local_vtbl__2Vn1S[Vn_vtbl_compile_time_known_size]; memmove(__local_vtbl__2Vn1S, __vtbl__2Vn1S, Vn_vtbl_compile_time_known_size); __local_vtbl__2Vn1S[RTTI_INDEX].offset = S.bptr[Vn]; Vn.vptr = &__local_vtbl__2Vn1S; } else { Assign vtbls like we do today; } Call non-virtual direct base class constructors /* body of S's constructor */ if (!most_derived) { Reassign vtbls like we do today; } } |
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,...
Reduce message(s) severity to informational.-msg_warn tag,...
Reduce message(s) severity to warning.-msg_error tag,...
Increase message(s) severity to error-msg_enable tag,...
Enable specific messages that would normally not be issued. You can also use this option to re-enable messages disabled with -msg_disable .-msg_disable tag,...
Disable message. 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.5. 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.5.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 |
![]() |
![]() |