United States |
|
|
||
Chapter 9
|
This initial implementation of 64-bit pointers is subject to change based on customer feedback. |
The introduction of 64-bit address space in OpenVMS greatly increases the amount of memory available to applications. Compaq C++ has been enhanced to permit use of this memory. The compiler provides a great deal of flexibility about how this memory can be used. Conceptually, this flexibility can be viewed as four models for development:
In a 32-bit development environment, all pointers are 32-bits long and only 2 gigabytes of address space is available. This is the default and was the only option that was available before this version of the compiler. In a 64-bit development environment, all pointers are 64-bits long and the address space is over a billion gigabytes.
Working in a homogeneous 32-bit or 64-bit environment is the prefered and recommended way to do development. Compaq C++ for OpenVMS, combined with the C Run-Time library, provide a seemless environment for development. It should be possible for a well written, portable program developed using 32-bit pointers to be recompiled and relinked to use 64-bit pointers.
Because it is not always possible or desirable to work in a homogeneous pointer environment. Compaq C++ supports mixed pointer sizes, however, it requires greater care by developers. Some contexts where heterogeneous pointer sizes might be used are:
When the memory requirements of a 32-bit application begins to exceed 2 gigabytes, the most straight forward solution is to convert the application to be a 64-bit application. Since practical considerations, like the size of the application or the lack of source code for all parts can prevent this, the alternative approach of isolating the use of 64-bit pointers to a small portion of the application may be preferable. In this situation, development would continue in the 32-bit environment, using long pointers when necessary.
When doing 64-bit development, there are times when it becomes necessary or desirable to use 32-bit pointers. The most common instance is interfacing with a 32-bit library. Another is to save space, because 64-bit pointers consume twice as much memory as 32-bit pointers. In this situtation, development could be done in a 64-bit environment, using short pointers when necessary.
Limited empirical envidence suggests that using 32-bit pointers to save space can reduce memory consumption by approximately 25% but at the cost of greater complexity and the creation of potentially unneccessary constraints in the application.
Besides pointer size, the following components of the development environment determine whether it is a 32- or 64-bit environment:
Memory allocators control where in the address space memory is allocated. Memory can be allocated in 32- or 64-bit space independent of the pointer size. The default memory allocator is appropriate for the development environment being used.
Libraries in a 32-bit environment expect pointers to be 32-bits and memory to reside in the 32-bit address space, while libraries in the 64-bit environment expect pointers to be 64-bits. Compaq C++ for OpenVMS ships with two libraries: one for the 32-bit environment and one for the 64-bit environment. In addition to supporting the 64-bit environment, the second library also supports the new object model refered to as model ANSI.
When compiling /POINTER_SIZE=LONG , the STL template classes (such as string, and set, map) can be used only when /MODEL=ANSI is specified. |
The C Runtime is a single library that supports both environments. See the Compaq C Run-Time Library Reference Manual for OpenVMS Systems for information about how support for both environments was achieved with a single library. See Section 9.5 for a discussion of why it is difficult to produce a single C++ library to support both environments.
The new ANSI object model allows the compiler to better conform to the ANSI/ISO C++ standard while providing the 64-bit development environment. This object model is specified using the /MODEL=ANSI compiler and link options. To build a 64-bit application using the ANSI object model, you enter commands in the following format:
$ cxx /model=ansi filename.cxx $ cxxlink/model=ansi filename |
The new ANSI object model is not compatible with the old object model. You must compile and link your entire application with one model or the other. |
In C++, the primary memory allocator is new . Use of the default allocators causes memory to be allocated that is appropriate for the default pointer size for the module (not the current pointer size). Specialized placement-new allocators can be used to control where an object is allocated. The header newext.hxx contains the following definitions:
enum addr32_t (addr_32 }; enum addr64_t {addr_64 }; #pragma pointer_size short void *operator new(addr32_t, size_t s) { return _malloc32(s); } void *operator new[](addr32_t, size_t s) { return _malloc32(s); } #pragma pointer_size long void *operator new(addr64_t, size_t s) { return _malloc64(s); } void *operator new[](addr64_t, size_t s) { return _malloc64(s); } |
Use of the allocators from the C Run Time is also possible. You can select a specific C allocator by adding a prefix underbar and either 32 or 64 as a suffix.
Function | 32-bit | 64-bit |
malloc | _malloc32 | _malloc64 |
calloc | _calloc32 | _calloc64 |
realloc | _realloc32 | _realloc64 |
strdup | _strdup32 | _strdup64 |
When attempting to mix pointer sizes in your program, distinguish between the concepts of pointer size and memory allocators. The pointer size dictates the maximum amount of address space a pointer can reference, while the allocator controls the where the memory will be allocated.
A library implemented with 64-bit pointers that uses only a 32-bit allocator can with care be used by an application that uses 32-bit pointers. If the library uses a 64-bit allocator, the application cannot reference any pointers returned. To a large extent, it is the memory allocator, not the pointer size, that determines interoperability.
In addition to allocators, other functions in the C Run Time Library, such as strcpy , are affected by pointer size. As with the alloators, the C++ compiler calls a version of the routine is for the development environment. See the Compaq C Run-Time Library Reference Manual for OpenVMS Systems for more details.
The following qualifiers, pragmas, and predefined macros control pointer size:
The /MODEL=ANSI qualifier enables the new ANSI object model. This model implies /POINTER_SIZE=LONG in addition to supporting new C++ constructs that could not be supported in the object object model designed to support the ARM definition of the language. This option must be specified during compilation and linking.
The /POINTER_SIZE qualifier lets you specify a value of 64 or 32 (or LONG or SHORT) as the default pointer size within the compilation unit. You can compile one set of modules using 32-bit pointers and another set using 64-bit pointers. Take care when these two separate groups of modules call each other.
The default is /NOPOINTER_SIZE, which has the following effects:
This default represents no change from previous versions of Compaq C++.
Specifying /POINTER_SIZE with a keyword value (32, 64, SHORT, or LONG) has the following effects:
Use of the /POINTER_SIZE qualifier also influences the processing of Compaq C RTL header files:
See the Compaq C Run-Time Library Reference Manual for OpenVMS Systems for more information on the impact of 64-bit pointer support on Compaq C++ RTL functions.
The __INITIAL_POINTER_SIZE preprocessor macro is useful for header-file authors to determine:
Header-file code can then be conditionalized using the following preprocessor directives:
#if defined (__INITIAL_POINTER_SIZE) /* Compiler supports 64-bit pointers */ #if __INITIAL_POINTER_SIZE > 0 /* Application uses 64-bit pointers */ #if __INITIAL_POINTER_SIZE == 32 /* Application uses some 64-bit pointers, but default RTL routines are 32-bit.*/ #if __INITIAL_POINTER_SIZE == 64 /* Application uses 64-bit pointers and default RTL routines are 64-bit. */ |
The #pragma pointer_size and #pragma required_pointer_size preprocessor directives can be used to change the pointer size currently in effect within a compilation unit. You can default pointers to 32-bits and then declare specific pointers within the module as 64-bits. In this case, you also need to specifically call the appropriate allocator to obtain memory from the 64-bit memory area.
These pragmas have the following format:
#pragma pointer_size keyword #pragma required_pointer_size keyword |
The keyword is one of the following:
{ short |32} | 32-bit pointer |
{ long |64} | 64-bit pointer |
save | Saves the current pointer size |
restore | Restores the current pointer size to its last saved state |
The #pragma pointer_size and #pragma required_pointer_size directives work essentially the same way, except that #pragma required_pointer_size always takes effect regardless of command-line qualifiers, while #pragma pointer_size is in effect only when the /POINTER_SIZE command-line qualifier is used.
By changing the command-line qualifier, #pragma pointer_size allows a program to be built using 64-bit features as purely as a 32-bit program.
The #pragma required_pointer_size is intended for use in header files where interfaces to system data structures must use a specific pointer size regardless of how the program is compiled.
An alternative to controling the pointer size is #pragma environment . This pragma controls all compiler states that include pointer size. This pragma is fully documented in Section 2.1.1.2. The primary change for support of long pointers is the addition of a new cxx_header_defaults keyword.
This new keyword is similar to the header_defaults keyword, but differs in the effect on pointer_size . With header_defaults, pointer_size is made short, while with cxx_header_defaults, the pointer_size depends on the model being used. When developing in model ANSI, the pointer_size is 64 bits; in model ARM (the default), it is 32 bits.
The pointer-size qualifiers and pragmas affect only a limited number of constructs in the C++ language itself. At places where the syntax creates a pointer type, the pointer-size context determines the size of that type. Pointer-size context is defined by the most recent pragma (or command-line qualifier) affecting pointer size.
Here are examples of places in the syntax where a pointer type is created:
int **p; // Declaration ip = (int **)i; // Cast |
void foo(int ia[10][20]) {} // Means the following: void foo(int (*ia)[20]) {} |
void foo (int func()): // Means the following: void foo (int (*)() func); |
typedef int a_type[10]; void foo (a_type ia); // Means the following: void foo (int *ia); |
The following special cases are not affected by pointer-size context:
#pragma pointer_size 64 main(int argc, char **argv) { ASSERT(sizeof(argv[0]) == 4); } |
#pragma pointer_size 64 ASSERT(sizeof("x" + 0) == 8); #pragma pointer_size 32 ASSERT(sizeof("x" + 0) == 4); |
#pragma pointer_size 32 sizeof(&foo) == 32 #pragma pointer_size 64 sizeof(&foo) == 64 sizeof(&s ->next) == sizeof(s) |
class foo { public: void f(); void f2(); }; #pragma required_pointer_size short void foo::f() { sizeof(this)==4 } // this is short #pragma required_pointer_size long void foo::f2() #pragma required_pointer_size short { sizeof(this)==8; } // this is long |
An application can use both 32-bit and 64-bit addresses. The following semantics apply when mixing pointers:
Take note of the following general header-file considerations:
fprintf(FILE *, const char *, ...); |
Be aware that pointer-size controls are not unique in the way they affect header files; other features that affect data layout have similar impact. For example, most header files should be compiled with 32-bit pointers regardless of pointer-size context. Also, most system header files (on Alpha systems) must be compiled with member_alignment regardless of user pragmas or qualifiers.
To address this issue more generally, you can use the pragma environment directive to save context and set header defaults at the beginning of each header file, and then to restore context at the end. See Section 2.1.1.2 for a description of pragma environment .
Consider the following suggestions to avoid problems related to pointer size:
Although Compaq C and C++ allow mixing pointer sizes, mixed pointers can cause certain types of error when used incorrectly. Consider the following examples:
#pragma pointer_size long int *y=_malloc64(); // Y is a 64 bit pointer #pragma pointer_size short int *x=y; // X is a 32 bit pointer, which results in truncation. |
int i,j; #pragma pointer_size short int *ptr=&i; int **pptr=&ptr; #pragma pointer_size long int **lptr=pptr; *lptr = &j; // miswrite: 8 bytes write, but points to 4 byte ptr. ptr = *lptr; // misread: 8 bytes read, but points to 4 byte ptr. |
Furthermore, the following C++ features discourage the use of mixed pointers:
#pragma pointer_size long class myObject { char *myData; public: myObject() { myData = new char[1000]; } ~myObject() { delete[] myData; } char *getData() { return myData; } }; #pragma pointer_size short myObject *ptr = new myObject(); // 32 bit pointer to object in 32 bit space char *data = ptr->getData(); // 32 bit pointer truncated 64 bit pointer to data in 64 bit space |
// C implementation of API void API_f1(int); #pragma pointer_size short void API_f2(int *); #pragma pointer_size long void API_f2_64(int*); void API_f3(int); //C++ implementatin of API class BASE { public: virtual void f1(int); #pragma pointer_size short virtual void f2(int *); #pragma pointer_size long virtual void f2_64(int*); }; class API : public BASE { public: virtual void f3(int); } |
struct FILE { char *buffer; }; FILE *fopen(const char *,,,); int fclose(FILE*); |
template <class T> class short_pointer<T> { #pragma pointer_size save #pragma pointer_size short T *ptr; #pragma pointer_size restore public: short_pointer(const T*x) { ptr =x; } operator T*() { return ptr; } } template <class T> class long_pointer<T> { #pragma pointer_size save #pragma pointer_size short T *ptr; #pragma pointer_size restore public: long_pointer(const T*x) { ptr =x; } operator T*() { return ptr; } } void func(short_pointer<int> x) { *x = 5; } void func(long_pointer<int> x) { *x = 5; } |
Previous | Next | Contents | Index |
privacy statement and legal notices |