DIGITAL logo   C++ Graphic
    Updated: 21 April 1999
  Compaq C++

Compaq C++
Using Compaq C++ for OpenVMS Alpha


Previous Contents Index


Chapter 3
C++ Language Environment

This chapter describes the guidelines and procedures for customizing your language environment. It includes sections on changing your C header files to work with C++, organizing your C++ files, interfacing to other programming languages, and designing upwardly compatible C++ classes.

3.1 Using Existing C Header Files

C header files that already conform to ANSI C standards must be modified slightly to be usable by Compaq C++ programs. In particular, be sure to address the following issues:

  • Enable the proper linkage for each language.
  • Ensure that C++ keywords are not used as identifiers.
  • Reconcile any namespace and scoping differences.

The compiler provides some C header files that have been modified to work with C++, including standard ANSI C header files. These headers are in the sys$library directory.

The following sections provide details on how to properly modify your headers.

3.1.1 Providing C and C++ Linkage

To modify header files, use conditional compilation and the extern specifier.

When programming header files to be used for both C and C++ programs, use the following convention for predefined macros. The system header files also provide an example of correct usage of the predefined macros.


#if defined __cplusplus 
    /* If the functions in this header have C linkage, this 
     * will specify linkage for all C++ language compilers. 
     */ 
    extern "C" { 
#endif 
 
# if defined __DECC || defined __DECCXX 
    /* If you are using pragmas that are defined only 
     * with DEC C and DEC C++, this line is necessary 
     * for both C and C++ compilers.  A common error 
     * is to only have #ifdef __DECC, which causes 
     * the compiler to skip the conditionalized 
     * code. 
     */ 
#   pragma __extern_model __save 
#   pragma __extern_model __strict_refdef 
    extern const char some_definition []; 
#   pragma __extern_model __restore 
# endif 
 
 /* ...some data and function definitions go here... */ 
 
#if defined __cplusplus 
    }   /* matches the linkage specification at the beginning. */ 
#endif 

See §r.7.4 of The C++ Programming Language, 3nd Edition for more information on linkage specifications.

3.1.2 Resolving C++ Keyword Conflicts

If your program uses any of the following C++ language keywords as identifiers, you must replace them with nonconflicting identifiers:
asm bool catch class
const_cast delete dynamic_cast explicit
export false friend inline
mutable namespace new operator
private protected public reinterpret_cast
static_cast template this throw
true try typeid typename
virtual wchar_t    

Alternative representation keywords are as follows:


and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor, xor_eq 

3.1.3 Handling Scoping Issues

Distinctions between ANSI C and C++ include slight differences in rules concerning scope. Therefore, you may need to modify some ANSI C header files to use them with C++.

The following sample code fragment generates an error regarding incompatible types, but the root cause is the difference in scope rules between C and C++. In ANSI C, the compiler promotes tag names defined in structure or union declarations to the containing block or file scope. This does not happen in C++.


struct Screen { 
  struct _XDisplay *display; 
}; 
typedef struct _XDisplay { 
  // ...
} Display; 
 
struct Screen s1; 
Display      *s2; 
 
main() 
{ 
  s1.display = s2; 
}    

The offending line in this sample is s1.display = s2. The types of s1.display and s2 are the same in C but different in C++. You can solve the problem by adding the declaration struct _XDisplay; to the beginning of this code fragment, as follows:


struct _XDisplay; // this is the added line 
struct Screen { 
struct _XDisplay *display; 
}; 
typedef struct _XDisplay { 
  // ...
} Display; 
// ...

3.1.4 Support for <stdarg.h> and <varargs.h> Header Files

The C compiler special built-in macros defined in the header files <stdarg.h> and <varargs.h>. These step through the argument list of a routine.

Programs that take the address of a parameter, and use pointer arithmetic to step through the argument list to obtain the value of other parameters, assume that all arguments reside on the stack and that arguments appear in increasing order. These assumptions are not valid for Compaq C++. The macros in <varargs.h> can be used only by C functions with old-style definitions that are not legal in C++. To reference variable-length argument lists, use the <stdarg.h> header file.

The OpenVMS Alpha calling standard mechanism for returning structures larger than 8 bytes by value uses a hidden parameter. The parameter is a pointer to storage in the caller's frame. The va_count macro includes this parameter in its count.

3.2 Using Compaq C++ with Other Languages

The following are suggestions regarding the use of Compaq C++ with other languages:

  • Passing entities, such as classes, by reference is safest.
  • You cannot invoke class member functions from within any language other than C++.
  • Every C++ routine that will be called from the other language should be declared in C++ with extern "C". For example:


    extern "C" 
       int myroutine(int, float); 
    

    The extern "C" will cause the routine to have an unmangled name, so that you can refer to it as myroutine from a language such as Cobol or Fortran. Otherwise the routine's link name will be mangled into something like myrout__Xif.

  • If the main routine is defined in the other language, you will probably need to use the other language's command-line interface to perform your link step. To include the appropriate C++ libraries and startup file, you will need to add some arguments to the command line. The most reliable way to determine what is needed is to test with a small C++ program.

3.3 Linkage to Non-C++ Code and Data

With linkage specifications, you can both import code and data written in other languages into a Compaq C++ program and export Compaq C++ code and data for use with other languages. See §4.4 of The C++ Programming Language, 3nd Edition for details on the extern "C" declaration.

3.4 How to Organize Your C++ Code

This section explains the best way for compiler users to organize an application into files; it assumes that you are using automatic instantiation to instantiate any template classes and functions.

3.4.1 Code That Does Not Use Templates

The general rule is to place declarations in header files and place definitions in library source files. The following items belong in header files:

  • Class declarations
  • Global function declarations
  • Global data declarations

And the following items belong in library source files:

  • Static member data definitions
  • Out-of-line member function definitions
  • Out-of-line global function definitions
  • Global data definitions

Header files should be directly included by modules that need them. Because several modules may include the same header file, a header file must not contain definitions that would generate multiply defined symbols when all the modules are linked together.

Library source files should be compiled individually and then linked into your application. Because each library source file is compiled only once, the definitions it contains will exist in only one object module and multiply defined symbols are thus avoided.

For example, to create a class called "array" you would create the following two files:

Header file, arrayInt.hxx:


// arrayInt.hxx 
#ifndef ARRAY_H 
#define ARRAY_H 
 
class arrayInt { 
private: 
        int curr_size; 
        static int max_array_size; 
public: 
        arrayInt() :curr_size(0) {;}  
        arrayInt(int); 
}; 
 
#endif 

Library source file, arrayInt.cxx:


// arrayInt.cxx 
#include "arrayInt.hxx" 
 
int array::max_array_size = 256; 
 
arrayInt::arrayInt(int size) :  curr_size(size) { ...;  } 

You would then compile the arrayInt.cxx library source file using the following command:


cxx/include=[.include] arrayInt.cxx 

The resulting object file could either be linked directly into your application or placed in a library (see Section 3.4.4).

The header file uses header guards, which is a technique to prevent multiple inclusion of the same header file.

3.4.2 Code That Uses Templates

With the widespread use of templates in C++, determining the proper place to put declarations and definitions becomes more complicated.

The general rule is to place template declarations and definitions in header files, and to place specializations in library source files.

Thus, the following items belong in template declaration files:

  • Declarations of global function templates
  • Declarations of class templates
  • Declarations of global function template specializations
  • Declarations of class template specializations

The following items can be placed either in the header file with the corresponding template declaration or in a separate header file that can be implicitly included when needed. This file has the same basename as the corresponding declaration header file, with a suffix that is found by implicit inclusion. For example, if the declaration is in the header file inc1.h, these corresponding definitions could be in file inc1.cxx.

  • Definitions of out-of-line global function templates
  • Definitions of static member data of class templates
  • Definitions of out-of-line member functions of class templates

The following must be placed in library source files to prevent multiple definition errors:

  • Definitions of global function template specializations
  • Definitions of static member data specializations of class templates
  • Definitions of out-of-line class member function specializations

These guidelines also apply to nontemplate nested classes inside of template classes.

Note

Do not place definitions of nontemplate class members, nontemplate functions, or global data within template definition files; these must be placed in library source files.

All these header files should use header guards, to ensure that they are not included more that once either explicitly or by implicit inclusion.

For example, the array class from Section 3.4.1, modified to use templates, would now look as follows:

Template declaration file, array.hxx:


// array.hxx 
#ifndef ARRAY_HXX 
#define ARRAY_HXX 
 
template <class T> 
class array { 
private: 
        int curr_size; 
        static int max_array_size; 
public: 
        array() :curr_size(0) {;} 
        array(int size,const T& value = T()); 
};  
 
#endif 

Template definition file, array.cxx:


// array.cxx 
template <class T> 
int array<T>::max_array_size = 256; 
 
template <class T> 
array<T>::array(int size,const T& value )  {... ; } 

Then you would create a source file myprog.cxx that uses the array class as follows:


// myprog.cxx 
 
#include "array.hxx" 
 
main() { 
        array<int> ai; 
 
        // ...
} 

Figure 3-1 shows the placement of these files.

Figure 3-1 Placement of Template Declaration and Definition Files


You would then compile myprog.cxx in the mydir directory with the following command:


cxx/incl=[.include] myprog.cxx 

In this case, you do not need to create library source files because the static member data and out-of-line members of the array template class are instantiated at the time you compile myprog.cxx.

However, you would need to create library source files for the following cases:

  • Your template declaration file declares nontemplate classes, global functions, or global data that require definitions in a library source file.
  • A template class declares an out-of-line nontemplate friend function whose definition must be placed in a library source file.
  • Your template declaration file declares a specialization of a template class whose static member data or out-of-line member function definitions must be placed in a library source file.
  • Your template declaration file declares an out-of-line specialization of a template function, whose definition must be placed in a library source file.

3.4.3 Summary

Table 3-1 describes where to place declarations and definitions, as discussed in Section 3.4.1 and Section 3.4.2.

Table 3-1 Declaring and Defining Classes, Functions, and Data
Feature Declaration Out-of-Line Definition
Class Header file  
Static member data Within class declaration Library source file
Member function Within class declaration Library source file
Global function Header file Library source file
Global data Header file Library source file
     
Template class Template declaration file  
Static member data of template class Within template class declaration Template definition file
Member function of template class Within template class declaration Template definition file
Global template function Template declaration file Template definition file
Global, nontemplate friend function of template class Within template class declaration Library source file
Specialization of template class Template declaration file  
Specialization of template function Template declaration file Library source file

3.4.4 Creating Libraries

Libraries are useful for organizing the sources within your application as well as for providing a set of routines for other applications to use. Libraries can be either object libraries or shareable libraries. Use an object library when you want the library code to be contained within an application's image; use shareable libraries when you want multiple applications to share the same library code.

Creating a library from nontemplate code is straightforward: you simply compile each library source file and place the resulting object file in your library.

Creating a library from template code requires that you explicitly request the instantiations that you want to provide in your library. See Chapter 7 for details.

If you make your library available to other users, you must also supply the corresponding declarations and definitions that are needed at compile time. For nontemplate interfaces, you must supply the header files that declare your classes, functions, and global data. For template interfaces, you must provide your template declaration files as well as your template definition files.

For more information on creating libraries, see the OpenVMS Command Definition, Librarian, and Messages Utilities Manual and the OpenVMS Linker Utility Manual.

3.5 Sample Code for Creating OpenVMS Shareable Images

The SW_SHR sample code consists of several source modules, a command procedure and this description. Table 3-2 lists each of the constituent modules, which are located in the directory SYS$COMMON:[SYSHLP.EXAMPLES.CXX] on your system.

The code creates an OpenVMS shareable image called SW_SHR.EXE that supplies a Stopwatch class identical to the C++ Class Library's Stopwatch class. For detailed information about the Stopwatch class, refer to the DEC C++ Class Library Reference Manual.

SW_SHR also provides an instance of a Stopwatch class named G_sw that shows how to export a class instance from a shareable image. The exportation occurs in the same way that cout, cin, cerr, and clog are exported from the C++ Class Library shareable image.

Table 3-2 Shareable Image Example Files
Module Name Description
SW_DEFS.MAR Macro definitions for use by both the SW_VEC_ALPHA and SW_VEC_VAX macro source files.
SW_DEFS_ALPHA.MAR Macro definitions of globally accessible class objects defined within the shareable image.
SW_DEFS_VAX.MAR Entry point macro definitions and macro definitions of globally accessible class objects defined within the shareable image.
SW_SHARE.HXX General use macros to make exporting of global data (class instances) from shareable images more transparent to the users of class objects.
SW.HXX The definition of the Stopwatch class supplied by the shareable image.
SW.CXX Source associated with the public functions defined in SW.HXX. It also contains the declaration of the global Stopwatch (G_sw) class instance.
SW_TEST.CXX A test of each of the Stopwatch's public access points and also the G_sw class instance.
SW_BUILD.COM A DCL command procedure used to build both the shareable image and the program.
SW_SHR _ALPHA.OPT An OpenVMS Linker options file, used on OpenVMS Alpha, that contains the SYMBOL_VECTOR entry points and other shareable image linker directives.
SW_SHR _VAX.OPT An OpenVMS Linker options file, used on OpenVMS VAX, that contains shareable image linker directives.

When you create shared images on OpenVMS systems, you must export guard variables for template static data members or for static variables defined in inline functions. These guard variables, which are prefixed by __SDG and __LSG respectively, ensure that static data is initialized only once. You must also export the static variables in inlined functions and template static data members from the shared image so that they have only one definition.

3.6 Hints for Designing Upwardly Compatible C++ Classes

If you produce a library of C++ classes and expect to release future revisions of your library, you should consider the upward compatibility of your library. Having your library upwardly compatible makes upgrading to higher versions of your library easier for users. And if you design your library properly from the start, you can accomplish upward compatibility with minimal development costs.

The levels of compatibility discussed in this section are as follows:

  1. Source compatibility
  2. Link compatibility
  3. Run or binary compatibility

The format in which your library ships determines the levels of compatibility that apply:
Library Format Compatibility Level
Source format Source compatibility only
Object format Source and link compatibility
Shareable library format All three kinds of compatibility

If you break compatibility between releases, you should at least document the incompatible changes and provide hints for upgrading between releases.

3.6.1 Source Compatibility

Achieving source compatibility means that users of your library will not have to make any source code changes when they upgrade to your new library release. Their applications will compile cleanly against your updated header files and will have the same run-time behavior as with your previous release.

To maintain source compatibility, you must ensure that existing functions continue to have the same semantics from the user's standpoint. In general, you can make the following changes to your library and still maintain source compatibility:

  • Add new data members and classes.
  • Add new virtual and nonvirtual functions (as long as they do not change overload resolution of existing calls).
  • Loosen protection.
  • Change inline functions to out-of-line, and out-of-line functions to inline.
  • Change the implementation of functions.
  • Add arguments with default values to existing member functions.

3.6.2 Link Compatibility

Achieving link compatibility means that users of your library can relink an application against your new object or shareable library and not be required to recompile their sources.

What Can Change

To maintain link compatibility, the internal representation of class objects and interfaces must remain constant. In general, you can make the following changes to your library and still maintain link compatibility:

  • Change anything that is invisible to the user.
  • Change the implementation of an out-of-line function.
  • Loosen protection.
  • Add a new nonvirtual member function (as long as it does not change overload resolution for existing calls).

What Cannot Change

Because the user may be linking object modules from your previous release with object modules from your new release, the layout and size of class objects must be consistent between releases. Any user-visible interfaces must also remain unchanged; even the seemingly innocent change of adding const to an existing function will change the mangled name and thus break link compatibility.

The following are changes that you cannot make in your library:

  • Add, move, or delete data members.
  • Add, move, or delete virtual functions.
  • Change the signature of virtual and nonvirtual functions.
  • Remove nonvirtual functions.
  • Change inline function definitions.
  • Change functions from out-of-line to inline.

Designing Your C++ Classes for Link Compatibility

Although the changes you are allowed to make in your library are severely restricted when you aim for link compatibility, you can take steps to prepare for this and thereby reduce the restrictions. Compaq suggests using one of the following design approaches:

  • Set aside dummy (reserved-for-future-use) data fields and virtual functions within your classes. This assumes you can forsee how much your classes will grow and change in the future.
  • Add a level of indirection to hide your virtual functions and data fields from the user. This lets you add and change data fields and virtual functions without affecting the library user; however, there may be some disadvantages such as in performance. This approach is detailed in Effective C++, Section 34, by Scott Meyers.

3.6.3 Run Compatibility

Achieving run compatibility means that users of your library can run an application against your new shareable library and not be required to recompile or relink the application.

This requires that you follow the guidelines for link compatibility as well as any operating system guidelines for shareable libraries. On OpenVMS systems, you need to create an upwardly compatible shareable image using a transfer vector on OpenVMS VAX and a symbol table on OpenVMS Alpha. Refer to the OpenVMS Linker Utility Manual for information on creating a shareable image.

3.6.4 Additional Reading

The C++ Programming Language, 3nd Edition offers some advice on compatibility issues. Another good reference is Designing and Coding Reusable C++, Chapter 7, by Martin D. Carroll and Margaret E. Ellis.


Previous Next Contents Index

   
Burgundy bar
DIGITAL Home Feedback Search Sitemap Subscribe Help
Legal