|
|
HP C++
|
Previous | Contents | Index |
This chapter discusses the features and characteristics specific to the HP C++ implementation, including pragmas, predefined names, numerical limits, and other implementation-dependent aspects of the language definition.
This section describes pragmas, predefined names, and limits placed on the number of characters and arguments used in HP C++ programs.
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 HP 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.
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.
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 HP 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 HP 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.
The #pragma extern_prefix directive is an OpenVMS pragma that cannot be used on HP Tru64 UNIX and Linux Alpha systems.
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 HP's HP 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 HP 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" |
This is a C pragma that cannot be used in C++. Use the inline keyword instead.
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.
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.
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.
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.
The #pragma once preprocessor directive specifies that the header file is evaluated only once.
The #pragma once directive has the following format:
#pragma once |
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 HP C++ and the C compilers resident on HP 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.
HP 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.
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.
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.
Use the #pragma weak preprocessor directive to create weak symbols. Weak symbols are symbols that can be overridden by strong symbols. Symbols are strong by default.
This pragma has the following syntax:
#pragma weak weak_name #pragma weak weak_name = strong_name #pragma weak ( weak_name, strong_name ) |
The first form of the pragma makes the specified name a weak symbol. The other two make a weak symbol that refers to the same symbol as the other name.
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:
__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 HP-supplied header prologue and epilogue files.
The default directory tree root assumed by the script is /usr/include , but you can specify other roots.
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.
The following tables list predefined C++ macros used by HP 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.
Predefined Language Dialect Macro Names
Table 2-4 shows the macro names defined by specifying the listed command-line options.
Predefined Implementation Compatibility Macro Names
Table 2-5 shows the macro names defined by specifying the listed command-line options.
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.
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.
This section describes the extensions and implementation-specific features of HP C++ for Tru64 UNIX and Linux Alpha systems.
In HP 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.
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.
The order of static objects is defined by the implementation. Relying on this order is not good programming practice. For HP 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.
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.
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 .
In HP 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.
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.
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 .
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).
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.
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.
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.
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 HP 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.
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 HP 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 .
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 HP 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.
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.
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> .
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 |
|