This chapter describes DCC, the Delta/C++ compiler that allows you to use dynamic classes. It contains the following sections:
DCC, the Delta/C++ compiler, is a native compiler that contains a number of enhancements over the standard Silicon Graphics C++ compilation environment. DCC allows you the option of using dynamic classes and modifying class libraries without client recompilation. DCC also contains a few enhancements over CC, but the primary difference is the use of dynamic classes. A dynamic class is a class whose layout and location are determined at link time.
DCC and CC use the same compiler front end, and enforce many of the same restrictions to approved C++ coding style that cfront overlooks. CC uses a more conventional code generation style than DCC. DCC predefines the _DELTA symbol during the preprocessor phase of compilation. DCC creates the file delta_preload.h, which contains the _DELTA declaration.
![]() | Note: DCC- and CC-produced object files are compatible. |
Current C++ systems have been designed without considering the needs of environments that make use of shared libraries or dynamic loading. If you release a new compatible version of a library or dynamically loaded component, you must recompile the portions of the system that make use of the classes defined in the new component. This is because current C++ systems resolve all object references at compile time.
The Delta/C++ compiler is a runtime/linktime extension to C++ that allows you to make compatible changes to class definitions with minimal recompilation, minimal restrictions on the use of the language, and minimal runtime overhead. Client applications linked against a shared library will continue to run, without recompilation, even when a new, compatible version of the shared library is released. Delta/C++ resolves all object references at link time.
DCC allows you to make changes to a system under active development without having to recompile the entire system in order to test the change or to continue development.
For example, in large systems, the cost of changing a class definition is often prohibitive, not only to the developer making the change, but also to developers using the change. You may have to rebuild the entire system. Developers will frequently work around a problem instead of solving it. In the end, few improvements are made to the base system.
The DCC extensions make it possible to recompile only a small set of files and resume work.
Smart Build extends the Delta/C++ compiler to reduce build times for C++ sources significantly. Invoking the -smart flag works seamlessly with any existing build environment (for example, make). Smart Build contains two interdependent mechanisms: precompiled headers (available in DCC and CC), and Smart Build itself, which uses these headers to calculate the differences between two versions of a header file. Smart Build keeps make from recompiling all files that include a modified class definition.
For further information on Smart Build, see Chapter 5, “Smart Build.”
DCC allows applications linked against a shared library to continue to work when a new version of the library is installed. For example, consider a typical scenario:
Silicon Graphics, Inc. ships several C++ shared libraries (DSOs).
A developer builds a client application linked against those libraries.
A consumer purchases the client application.
Silicon Graphics, Inc. ships new versions of the shared libraries.
To allow the new shared libraries to continue to work with existing applications, developers often ship several versions of the shared libraries, each bearing the version as part of their name (for example, lib/Inventor.so.1.1, lib/Inventor.so.1.2). This practice has several disadvantages:
More disk space is required.
Less actual sharing occurs, because old client applications use the old version of the shared library, and new client applications use the new version.
Old client applications do not benefit from any improvements to the new shared library (for example, performance enhancements or bug fixes).
The producer of the shared library (for example, Silicon Graphics) has to ensure that all versions of the shared library are shipped. Consumers (users of the shared library) must also install all of the versions, since there is no way for them to know when they will need a particular library, and they would have to go back to their initial installation and find the right library if they didn't install all of the versions.
With DCC, new versions of C++ libraries can be shipped with the assurance that they will work with existing client applications that were formerly bound with older versions of the C++ libraries.
In addition, most library providers have to expose a private implementation of your classes, because private members must be visible to the compiler in order to lay out the class. DCC allows you to remove that portion of the class definition and still have the code work correctly.
DCC supports the use of dynamic classes. A dynamic class is one whose layout and location are determined at link time. This gives you the ability to make changes to libraries and applications without having to recompile your entire code hierarchy. You may add members, base classes, promote members, change the overrides, and reorder the members of a dynamic class. (For a complete description of what is allowed under the Delta/C++ environment, see “Delta-Compatible Changes”.)
![]() | Note: DCC, by default, considers a class to be non-dynamic. |
As a developer, you use classes in three situations (see Figure 4-1 for a graphical representation):
classes that are imported from another library
classes that are used internally only
classes that are exported to a third party
When would it be best for you to use dynamic classes?
| Import classes | You would prefer these classes to be dynamic classes to help you avoid having to do massive recompilations when you must make changes to your code, but it is up to your library provider. | |
| Internal classes | Use non-dynamic classes, unless you are doing code development using -smart (see Chapter 5, “Smart Build”). | |
| Export classes | Use dynamic classes if you are planning to change them in future releases, and want to avoid affecting users. |
The amount of classes that you import, use internally, and export varies depending on what type of code you are developing. For example, perhaps you are a library developer; you would be importing a few classes, exporting large numbers of classes, and using very few internal-only classes. See Figure 4-2 for an illustration of this distribution.
Perhaps you are developing applications. In that case, you would be importing some classes, exporting none, but using many internally, as illustrated in Figure 4-3.
DCC makes classes dynamic in any of the ways listed below.
You may make a class dynamic on a class-by-class basis with the pragma dynamic_class.
You may use the pragma this_directory_tree_is_dynamic to enable dynamic classes for a given directory hierarchy. Any class that contains a non-inline member function in that hierarchy will be dynamic (unless you specifically make that class non-dynamic; see “Disabling Dynamic Classes”).
Any class that derives from a dynamic class is made dynamic.
Any class that contains one or more instances of dynamic classes is made dynamic.
When you execute DCC, the compiler first looks for a file named delta_preload.h in the directory that contains the file you are trying to compile. If DCC does not find delta_preload.h, it searches the parent directory. DCC continues to search for a delta_preload.h file until one is found, or until it reaches the root directory.
delta_preload.h ordinarily contains one of the following pragmas:
| this_directory_tree_is_dynamic |
| |
| this_directory_tree_is_not_dynamic |
|
Searching up the directory hierarchy for delta_preload.h makes it easier for you to control when classes are dynamic for an entire project.
For example, in the directory hierarchy in Figure 4-4, you are compiling
/usr/app/src/appfile.c. If the only delta_preload.h file is found in /usr/app/src, and it contains #pragma this_directory_tree_is_dynamic, then any class definition that contains a non-inline member function and is located in a file in /usr/app/src is marked as being dynamic.
A class definition that contains a non-inline member function and is located in a file in /usr/app/include/incfile.h is not considered dynamic. If the delta_preload.h file was moved to /usr/app, classes containing a non-inline member function defined in files from the include directory are also dynamic.
The compiler searches for a delta_preload.h file for the source file being compiled and any file that is included during the compilation. This makes it possible for a library provider to make classes exported by that library dynamic without affecting classes that come from the application or other libraries.
You can do this by putting a delta_preload.h file that contains the this_directory_tree_is_dynamic pragma in the top of the directory tree that contains the include files for the library. For example, in Figure 4-4, putting a delta_preload.h file in /usr/include/library would make any class dynamic, if that class has a non-inline member function defined in a file in that directory or any of its sub-directories.
When you are writing production code, you want classes to be non-dynamic by default. During the development phase, you probably want the classes to be dynamic. Using dynamic classes in conjunction with the smart build feature of the compiler greatly decreases the turn-around time when you make a change to a class definition. The best way to do this is to create a delta_preload.h file that looks like that in Example 4-1.
#ifdef CODE_DEVELOPMENT #pragma this_directory_tree_is_dynamic #else #pragma this_directory_tree_is_not_dynamic #endif |
You must put this file at the root of the application code source directory
(/usr/app in the directory hierarchy in Figure 4-4). Use the following options during code development:
-DCODE_DEVELOPMENT -g -smart,/usr/app/HDRS |
Do not use the first two options during a production build.
You can specify all the classes defined in an entire hierarchy to be dynamic by setting a #pragma in that directory's delta_preload.h file, as shown in the command below:
#pragma this_directory_tree_is_dynamic |
The classes containing non-inline member functions defined in that directory are dynamic.
You can specify a class to be non-dynamic with a #pragma in several ways:
You may define the #pragma
#pragma nondynamic_class <class> |
where the #pragma appears before the class definition.
You may define the #pragma
#pragma nondynamic_class “regular expression” |
where the only classes affected are those that appear after the pragma, and match those listed in the regular expression. (For more information on regular expressions, see the regexp(5) reference page.)
You may define the #pragma
#pragma nondynamic_class |
without a name if it appears within a class definition.
A class that is non-dynamic may have to be implemented as a dynamic class for various reasons (for example, the class contains a member or base class that is dynamic). Such classes are called internal dynamic classes, and may not be modified in future releases (that is, from a programmer's perspective, they are non-dynamic classes).
![]() | Note: You cannot apply the nondynamic_class pragma on a class that must be internally dynamic. |
The preprocessor automatically defines the __DELTA symbol when using DCC; this allows you to conditionally compile Delta/C++-specific code changes (such as the addition of pragmas).
The following are programming errors you may encounter when you use dynamic classes via the DCC extension.
DCC generates an internal description of a class layout. If this description is not available when linking, you will see a linker error:
Delta Error: Could not find the class definition for <class>. You possibly missed providing a definition for the first virtual method or the first non-inlined method. |
The class layout description is written to the appropriate object file. (The file is the one that corresponds to the source file containing the member function described in the error message.)
The usual source of this error is that you declared a constructor but never provided a definition for it.
When the DSO is linked into a program, the class description must be available, or you will see a warning:
Delta Warning: Could not find the class definition for <class> |
(For further information on DSOs, please see “Dynamic Shared Objects” in the IRIX System Programming Guide.)
When your code is expecting a member function to be defined, but the definition was not found in the class definition, this message appears:
Delta Error: Missing the member function <function> in <class> |
There are two common reasons for this problem:
You compiled with mismatched header files; that is, one source included an older version of the class definition lacking a member function added to the newer version of the same header.
The class definition itself was missing, in which case either of the first two messages appears.
To fix the problem, compile with the correct header files.
If a class has base classes, then the class description will refer to them. If, for some reason, one of the base class definitions could not be calculated, then you will see an error:
Delta Error: invalid class <class>: derives from invalid base classes |
If two classes have the same name, and they are linked into an application or into a DSO, and the classes have a member function with the same signature, the linker will issue a warning that the member function is multiply defined. (The compiler cannot detect this condition, only the linker can.)
If you make one or more of the classes dynamic when compiling with DCC, then the likelihood of a member function colliding increases, because dynamic classes always generate out-of-line constructors, destructors, and assignment operators. When this happens, the linker, when run under DCC, generates a warning:
Delta Warning: Multiple definitions of <class> - ignoring new definition |
DCC allows you to make various changes to a class library that are backwards-compatible with applications compiled with older versions of the library. You may make only these changes to dynamic classes.
The following changes to a class library are backwards-compatible with applications compiled with older versions of the library. These are the delta-compatible changes.
See “Delta-Compatible Changes Examples” for examples.
The following changes to a class library are not backwards-compatible with applications compiled with older versions of the library. If any of the following restrictions apply to your code, you must recompile. These are the delta-incompatible changes.
You cannot change the declaration of a member in any way (for example, changing a type from short to long). This includes the types of data members, function declarations, inline function bodies, typedef declarations, and enumeration declarations. The full set of restrictions is listed below.
You may not change the values of enumeration constants. However, you may add values to an existing enumeration provided you do not modify the values of any of the existing enumeration constants. See “Changing the Values of Enumeration Constants” for an example.
You may not add or remove parameters from a member function, and you cannot change the return type of a function. You may add a default value to an existing parameter that did not already have one. See “Adding or Removing Member Function Parameters” for an example.
You may not change the default parameters of a member. See “Changing Member Function Default Parameters” for an example.
You may add an overloaded function to a class only if the function you are adding cannot possibly be called by existing code. See “Adding Overloaded Functions to a Class” for an example.
You may not override an inline function defined in a base class with a new version of the function in a derived class. See “Base Class/Derived Class” for an example.
You may not override a global object in a base class when any derived classes reference that object. You also may not override functions with default arguments. See “Base Class/Global Object” for an example.
You may not change the class inheritance from single to multiple inheritance when adding a base class. See “Changing From Single to Multiple Inheritance” for an example.
You may not move members to an enclosing class. Delta/C++ allows you to promote a member to a base class, or override a member from a base class. However, you may not move a member to an enclosing class, or hide a member from an enclosing class. See “Moving Members to an Enclosing Class” for an example.
There are various restrictions on virtual functions, virtual base classes, and static members:
Virtual base classes may not be made non-virtual (and vice versa).
Virtual functions may not be made non-virtual (and vice versa).
Non-virtual functions may not be overridden by a virtual function.
Members may be promoted only to a non-virtual base class.
Static members may not be made non-static (and vice versa).
Static members may not be overridden by non-static members (and vice versa).
You cannot make a class abstract by adding a pure virtual function.
You may not move inline member functions from one class to another.
If you define a class as a leftmost base class in the first release, it must continue to be the leftmost base class in all future releases.
See “Delta-Incompatible Changes Examples” for examples.
The Delta/C++ product comes with a tutorial located in the directory
/usr/demos/DeltaCC. To run this tutorial, you must follow these steps:
Enter the command cd /usr/demos/DeltaCC to move to the demos directory.
Enter the command make delta to run a demonstration of Delta/C++.
This run tries to recompile only those files that we know are in need of recompilation, and shows how the test program picks up these changes without necessarily having to recompile the whole world.
To port your cfront-compatible source code to the Delta/C++ environment, you must follow these steps:
Change your makefiles to invoke DCC with options it will recognize.
Decide which of your classes will be dynamic. (You can define classes as dynamic or not dynamic with the delta-preload.h file and pragmas. See “Setting Classes to Be Dynamic” for more information.)
Correct source code that is not accepted by DCC.
To change your makefile you must
Use DCC instead of CC (the cfront driver) when compiling.
Use DCC to link an executable or DSO; invoking the linker (ld) directly on C++ object files is not recommended.
Remove any options (such as -float) that are no longer supported. See the DCC(1) reference page for a list and description.
Use the +w or -fullwarn option to get warnings about questionable constructs.
Enable SmartBuild with the -smart option. You may want to place all precompiled header files into the same repository directory, so put -smart, ILDUMPS in your Makefile. (ILDUMPS is a directory of your choice.)
Invoke the prelinker before running the ar command if you are building an archive (a .a file) out of object files that use templates. See “Building Shared Libraries and Archives” for more information.
When you move from cfront to DCC, there are several areas that may cause you problems. These areas are cfront incompatibilities, DCC limitations, and added DCC warnings.
By default, DCC tries to accept everything that CC did, even if these are sometimes illegal (and cfront simply doesn't catch it). There are some things, however, that DCC does not allow, and instead issues an error. For details, see “Compatibility Restrictions”.
The functionality of the +p option with DCC is identical to that with CC. However, you may want to use the +pp option instead. The +pp option disables cfront compatibility as well as disabling anachronisms. The +pp option enforces a strict interpretation of the current C++ language specification. The errors and warnings generated, when addressed and corrected, will give you more correct and reliable software.
Due to implementation constraints, DCC disallows certain constructs. See “DCC Limitations” for details.
DCC has a few limitations when the _DELTA facility is enabled:
Since the size of a dynamic class is no longer known at compile time, calling sizeof for a dynamic class no longer yields a compile time constant. In the following example, sizeof(myclass) cannot be used as a constant to set the buffer size for char buf:
class myclass
{
public: void A();
int a;
};
void myclass::A() {}
char buf[ sizeof(myclass)]; // \Gets error.
|
Taking the size of a dynamic class in an expression that is evaluated at run time will compile and work.
The size of a dynamic class cannot exceed 32767 bytes.
Constructors, destructors, and assignment operators for dynamic classes are always out of line.
A union may not have a member that has a constructor. Since DCC always ensures that all dynamic classes have a constructor, objects of such classes may not be a member of a union. Suppose you have the following code:
class dynamic {
public:
foo();
};
union xyz {
int x;
int y;
dynamic member; // Error 375.
xyz();
};
|
cfront accepts this code, but DCC generates the following error:
error(375): invalid union member -- class “dynamic” has a disallowed member function
dynamic member; // Error 375.
^
|
You cannot pass a dynamic class to a “...” parameter. When the formal parameter for a function is “...”, the corresponding actual parameter in a function call cannot be a dynamic object. A class object is passed to a “...” parameter “by value” (that is, no copy constructor is ever run), but since we do not know the size of a dynamic object, it cannot be passed by value on the stack.
Suppose you have the following code::
struct dynamic {
#pragma dynamic_class
dynamic();
};
dynamic object;
void f(int,...);
void main(){
f(5,object);
}
|
Compiling this code with DCC generates the error below.
“bug.c”, line 11: error(671): dynamic class cannot be passed to a ... parameter f(5,object);
^
|
_builtin_alignof is not a compile time constant. When you apply _builtin_alignof to a dynamic class, the alignment of the class may subsequently be changed by the addition of new data members. It is illegal to use _builtin_alignof in contexts where a compile time constant is needed.
For example, you cannot have the declaration of an array as in the code sample below.
int x[__builtin_alignof(dynamic_class)];” |
This restriction is similar to the restriction imposed on “sizeof”.