Chapter 6. Code Examples

This chapter contains C++ coding examples. It has the following major sections:

cfront Compatibility Examples

This section contains examples of cfront-compatible code that is illegal in the Silicon Graphics C++ environment. (For a complete description of the cfront compatibility issues, see “cfront Compatibility”.) The examples covered are listed below:

  • Terminating comment lines with a backslash

  • Explicitly declaring member functions

  • Using the same identifier for multiple arguments

  • Deleting a pointer to a const

  • Passing a pointer to volatile data

  • Disambiguating between a char* and a long

  • Rejecting redundant type specifiers

  • Converting a pointer to a class to an accessible base class

  • Assigning a 0 to a pointer

Terminating Comment Lines With a Backslash

If you use a C++-style (//) comment line terminated with a backslash, CC and DCC will (correctly) continue the comment line into the next source line. OCC (which uses the UNIX® standard cpp) incorrectly terminates the comment at the end of the line. This may cause hard-to-find bugs.

In Example 6-1, CC and DCC considers the two lines following the // comment to be part of the comment, while cfront does not.

Example 6-1. Terminating Comment Lines With a Backslash


#include <stdio.h>

int macro() { return 0; } 

// Continued comment...............\
#define macro() \
  (printf( “FAIL: executed a comment!\n” ), 1 )

int main()
{
  return macro();
}

cfront (incorrectly) calls the macro and DCC calls the function. You must delete the backslash at the end of the comment line.

Explicitly Declaring Member Functions

You must have an explicit declaration of a member function in a class if there is an explicit definition of it outside the class. cfront allows this, but CC and DCC displays the following error message:

error(3414): defining an implicitly declared member function is not allowed alpha::alpha( const alpha &other )
                ^

For example, the code in Example 6-2 will not compile.

Example 6-2. Explicitly Declaring Member Functions


class alpha
{
public:
  virtual void func(); // Needed to get implicit constructor
  long member; // No constructor, so not initialized
};

alpha::alpha( const alpha &other )
{
  member = other.member + 1000;
}

You must explicitly declare the missing member function and provide the default constructor.

Deleting a Pointer to a const

You may not delete a pointer to a const. (If the pointer is to a type other than the class itself, cfront also issues an error message.) If you do so, the following error message is displayed:

error(3352): a pointer to const may not be deleted
   delete ptr; // Error 352.
          ^

For example, the code in Example 6-3 will not compile.

Example 6-3. Deleting a Pointer to a const


class Foo
{
public:
  Foo();
  Foo( int );
  ~Foo();
private:
  int        stuff;
  const Foo* ptr;
};
Foo::~Foo()
{
  delete ptr; // Error 352.
}

You must cast the pointer to a non-const type.

Passing a Pointer to Volatile Data

You may not pass a pointer to volatile data to a function that is expecting a pointer to non-volatile data. If you do so, the following error message is displayed:

error(3252): argument of type “volatile mytype *” is
   incompatible with parameter of type “mytype *”
   f1( vol ); // Error.
       ^

For example, the code in Example 6-4 will not compile.

Example 6-4. Passing a Pointer to Volatile Data


typedef struct
{
  long i;
  long j;
} mytype;

void f1( mytype* );
void f2( int* );

volatile mytype *vol;
volatile int *pvi; 

int main()
{
  f1( vol ); // Error.
  f2( pvi ); // Error.
  return 0;
}

One solution is to cast the actual expression to the type expected, though that may result in incorrect code, as the data is no longer treated as volatile. A better solution is to rewrite the function to accept volatile data.

Disambiguating Between a char* and a long

When calling an overloaded function with char* and long variables and passing an integral constant 0 smaller than a long, you must explicitly cast the argument to either a char* or a long. If you do not, the following error message is displayed:

error(3390): more than one instance of constructor
   “UndoEvent::UndoEvent” matches the argument list:
   function func( char *)”
   function func( long)”
   func( (Bool)False );

For example, the code in Example 6-5 will not compile.

Example 6-5. Disambiguating Between a char* and a long


  void func( char * ){}
  void func( long ){}

typedef unsigned char Bool;

int main()
{
  func( (Bool) 0 ); // Error 3390.
  return 0;
}

You must use a cast to disambiguate which constructor is to be called.

Rejecting Redundant Type Specifiers

You may not use redundant type specifiers. If you do so, the following error message is displayed:

error(3177): invalid combination of type specifiers
    unsigned unsigned int x; // Error
             ^

For example, the code in Example 6-6 will not compile.

Example 6-6. Rejecting Redundant Type Specifiers


typedef const int Int;
. . .
const Int x;            // Error

You must delete the redundant type specifier.


Note: A long long data type is supported.


Implicitly Converting a Pointer to a Pointer to a Different Class

You cannot implicitly cast a pointer to a class to be a pointer to a different class, except when the pointer becomes a pointer to a base-class of the original class. Even when both classes inherit from a same base class, a pointer to one of these classes cannot be implicitly cast to be a pointer to the other class. If you try to do so, the following error message is displayed:

error(135): operand types are incompatible (“D1 *” and “D2 *”)
   bp = i ? dp1 : dp2; // Error.
                 ^

For example, the code in Example 6-7 will not compile.

Example 6-7. Converting a Pointer to a Class to an Accessible Base Class


struct base { };

struct D1 : public base { };
struct D2 : public base { };

int i =            906;
base* bp = (base*)   0;
D1* dp1 = (D1*)      1;
D2* dp2 = (D2*)      2;

void main(void)
{
  bp = i ? dp1 : dp2; // Error.
  bp = i ? (base*)dp1 : (base*)dp2; // Work-around.
}

You must cast both the second and third expressions to the type of the common base class.

Assigning a Comma Expression Ending in 0 to a Pointer

You cannot assign a comma expression with a rightmost expression of 0 to a pointer. If you do, the following error message is displayed:

error(611): a value of type “int” cannot be assigned to
   an entity of type “int *”
   p = ( 0, 0 ); // Error.
     ^

For example, the code in Example 6-8 will not compile.

Example 6-8. Assigning a 0 to a Pointer


void main()
{
  int* p;
  p = ( 0, 0 ); // Error.
}

You must cast the entire comma expression to the type of the pointer being assigned. You can do this by setting the macro WORKAROUND.

Delta-Compatible Changes Examples

This section contains examples that demonstrate the unique capabilities of the DCC. (For a complete description of the cfront compatibility issues, see “Delta-Compatible Changes”.) The examples covered are listed below.

  • Adding members to a class

  • Adding new base classes

  • Promoting members

  • Overriding functions

  • Reordering members

These examples are for dynamic classes only. Non-dynamic classes (which is the default) do not have this facility; all changes are delta-incompatible.

Adding Members to a Class

DCC allows you to add both member functions and variables to a class without forcing the recompilation of any code that uses that class. This is true for public, protected, and private members. For example, you have a class Alpha defined as shown in the following code segment:

class Alpha
{
  long a;
  long A();
  virtual long VA();
};

You are allowed to extend the members of Alpha without recompilation. A typical extension is shown in Example 6-9.

Example 6-9. Adding Members to a Class


class Alpha
{
  long a;
  long a1; 
  long A();
  virtual long VA();
  virtual long VA1(); 
};

Reordering Members

DCC allows you to reorder the members of a class. You can reorder the member variables to make more efficient use of space or to group members by their protection level. This reordering has no effect on the interface being provided by the class. For example, consider the code sample shown below:

class Alpha 
{
  long a;
  char c;
  long a1; 
  short s;
  long A();
};

You can reorder the members as shown in Example 6-10.

Example 6-10. Reordering Members


class Alpha 
{
  long a;
  long a1; 
  char c; 
  short s;
  long A();
};

Adding New Base Classes

DCC allows you to add a new base class to a class that already exists. For example, consider the code sample shown below:

class Alpha
{
  long a;
  long a1; 
  long A();
  virtual long VA();
  virtual long VA1(); 
};

You are allowed to extend the classes without recompilation. A typical extension is shown in Example 6-11.

Example 6-11. Adding New Base Classes


class Gamma 
{ 
  long g; 
  long G(); 
  virtual long VG(); 
} 

class Alpha : public Gamma 
{
  long a;
  long a1; 
  long A();
  virtual long VA();
  virtual long VA1(); 
};

Class Alpha now supports the additional functionality described in class Gamma and still supports Alpha's original interface.


Note: You cannot change from single to multiple inheritance. See “Changing From Single to Multiple Inheritance” for more information.


Promoting Members

DCC allows you to move functionality from a derived class to a non-virtual base class, so long as its type remains identical and is not an inline function. For example, consider the code sample shown below:

class Gamma 
{ 
  long g; 
  long G(); 
  virtual long VG(); 
} 

class Alpha : public Gamma 
{
  long a;
  long a1; 
  long A();
  virtual long VA();
  virtual long VA1(); 
};

If Alpha is derived from Gamma, you are free to move some of the members from Alpha into Gamma. The new version of Alpha still provides a compatible interface. You won't have to worry about how the functionality of Alpha is provided, only that it is provided. For example, consider the code sample shown below:

Example 6-12. Promoting Members


class Gamma 
{ 
  long g; 
  long a1; 
  long G(); 
  virtual long VG(); 
  virtual long VA1(); 
} 

class Alpha : public Gamma 
{
  long a;
  long A();
  virtual long VA();
};

a1 and VA1() have been promoted from Alpha to Gamma. You can release the new versions of Alpha and Gamma without requiring the recompilation of any code that uses either class.

Overriding Functions

DCC allows you to override a function or variable independent of whether it is a member or virtual member of a class. For example, consider the code sample shown below:

class Gamma 
{ 
  long g; 
  long a1; 
  long G(); 
  virtual long VG(); 
  virtual long VA1(); 
} 

class Alpha : public Gamma 
{
  long a;
  long A();
  virtual long VA();
};

You can change the overrides as shown in Example 6-13.

Example 6-13. Overriding Functions


class Gamma 
{ 
  long g; 
  long a1; 
  long G(); 
  virtual long VG(); 
  virtual long VA1(); 
} 

class Alpha : public Gamma 
{
  long a;
  long a1; 
  long A();
  virtual long VA();
  long G(); 
  long VG(); 
};

A user of Alpha should be unconcerned whether Alpha overrides the member function G() or the virtual member function VG() (originally declared in Gamma). The function that is called when invoking G() on an instance of Alpha changes, but the code still works.

Delta-Incompatible Changes Examples

This section contains examples of class modifications that are not supported by DCC. (For a complete description of the unsupported incompatible changes, see “Delta-Incompatible Changes”.) The examples covered are listed below:

  • Changing member declarations

  • Adding overloaded functions to a class

  • Overriding functions

  • Changing from single to multiple inheritance

  • Moving members to an enclosing class

Changing Member Declarations

You may not change the declaration of a member in any way (for example, changing a type from short to long). This includes types of data members, function declarations, inline function bodies, typedef declarations, and enumeration declarations.

Changing the Values of Enumeration Constants

You may not change the values of enumeration constants. For example, consider the code sample shown below:

enum color {
  red,
  blue
};

An incompatible change is shown in Example 6-14.

Example 6-14. Changing the Values of Enumeration Constants (1)


enum color {
  red,
  yellow, 
  blue 
};

Another incompatible change is shown in Example 6-15.

Example 6-15. Changing the Values of Enumeration Constants (2)


enum color {
  red = 4,
  blue = 1 
};

You may add values to an existing enumeration, provided you do not modify the values of any of the existing enumeration constants.

Adding or Removing Member Function Parameters

You may not add or remove parameters from a member function, and you may not change the return type of a function. For example, consider the code sample shown below:

class Alpha {
  void f(int x);
};

An incompatible change is shown in Example 6-16.

Example 6-16. Adding or Removing Member Function Parameters


class Alpha {
  void f(int x, long y);
};

You may add a default value to an existing parameter that did not already have one (such as int x = 906).

Changing Member Function Default Parameters

You may not change default parameters to member functions. For example, consider the code sample shown below:

class Alpha {
  void f(int x = 906);
};

An incompatible change is shown in Example 6-17.

Example 6-17. Changing Member Function Default Parameters


class Alpha {
  void f(int x = 1);
};

Adding Overloaded Functions to a Class

You may add an overloaded function to a class only if the function you are adding cannot possibly be called by existing code. For example, consider the code sample shown below:

class Alpha
{
  void F(int);
}

An incompatible change is shown in Example 6-18.

Example 6-18. Adding Overloaded Functions to a Class


class Alpha
{
  void F(int);
  void F(float); 
}

Adding the function F(float) is an incompatible change, since an existing call of F(1.0), which previously invoked F(int), would still do so. This is because DCC determines the function's signature at compile time. Only the location of the function's class is left unresolved until link time.

Overriding Functions

There are two unacceptable incompatibilities when you want to override functions:

  • overriding a function defined in a base class with a new version in a derived class

  • overriding a global object in a base class when any derived classes reference that object.

Base Class/Derived Class

You may not override an inline function defined in a base class with a new version of the function in a derived class. For example, consider the code sample shown below:

class Base 
{
  int test()
  {
    printf( “Base::test” );
  }
};

class Derived : public Base
{
};

Derived Object;

main()
{
  object.test();
}

object.test() invokes Base::test(), which is expanded inline.

Assuming these are dynamic classes, an incompatible change is shown in Example 6-19.

Example 6-19. Overriding Functions: Base Class/Derived Class


class Base 
{
  int test()
  {
    printf( “Base::test” );
  }
};

class Derived : public Base
{
  int test() 
  { 
    printf( “Derived::test” ); 
  } 
};

Derived Object;

main()
{
  object.test();
}

Unless main() is recompiled, it will continue to call Base::test().

Base Class/Global Object

A global object may not be overridden in a base class, when any derived classes reference that object. For example, consider the code sample shown below:

int global;

class Alpha
{
};

class Gamma : public Alpha 
{ 
public: 
  func(); 
}; 

int gamma::func()
{
  return global;
}

An incompatible change is shown in Example 6-20.

Example 6-20. Overriding Functions: Base Class/Global Object


int global;

class Alpha
{
  int global; 
};

int gamma::func()
{
  return global;
} 

Changing From Single to Multiple Inheritance

You may not add a base class if the derived class already has exactly one base class. For example, consider the code sample shown below:

class Gamma 
{ 
  long g; 
  long G(); 
  virtual long VG(); 
} 

class Alpha : public Gamma 
{
  long a;
  long A();
  virtual long VA1(); 
};

An incompatible change is shown in Example 6-21.

Example 6-21. Changing From Single to Multiple Inheritance


class Gamma 
{ 
  long g; 
  long G(); 
  virtual long VG(); 
} 

class Beta 
{ 
  long b; 
  long B(); 
  virtual long VB(); 
} 

class Alpha : public Gamma, Beta 
{
  long a;
  long A();
  virtual long VA1(); 
};

You may add a base class if the derived class has zero, two, or more base classes (in other words, it is already participating in multiple inheritance).

Moving Members to an Enclosing Class

You may not move a member to an enclosing class, or hide a member from an enclosing class. For example, consider the code sample shown below:

struct outer {
  static int s1;
  outer();
  struct inner {
    static int s2;
    inner();
  };
};

An incompatible change is shown in Example 6-22.

Example 6-22. Moving Members to an Enclosing Class


struct outer {
  static int s1;
  static int s2; // moved
  outer();
  struct inner {
    inner();
    static int s1; //new member
  };
};

s2 was moved to an enclosing class, and the new s1 in inner hides the s1 from outer. Both of these changes are delta- incompatible, and you must recompile your code.