Appendix B. Cfront Compatability

The SGI C++ compiler has a Cfront-compatibility mode (enabled by the -cfront option), which duplicates a number of features and bugs of Cfront, an older C++ compiler that accepted output from a C preprocessor and gave input to a C compiler. Complete compatibility is not guaranteed or intended; the mode is there to allow programmers who have used Cfront features to continue to compile their existing code. By default, the compiler does not support Cfront compatibility. See “Extensions Accepted in Cfront-Compatibility Mode”, and “Cfront Compatibility Restrictions”, for details.

Extensions Accepted in Cfront-Compatibility Mode

The information in this section is derived from the C++ Front End Internal Documentation, Version 2.45, copyright 1992-2000, by the Edison Design Group. Used by permission of the authors.

The following extensions are accepted in Cfront-compatibility mode (by using the -cfront option):

  • Type qualifiers on the this parameter may be dropped in contexts such as the following example:

    struct A {
        void f() const;
    void (A::*fp)() = &A::f;

    This is actually a safe operation. A pointer to a const function may be put into a pointer to non-const, because a call using the pointer is permitted to modify the object and the function pointed to actually does not modify the object. The opposite assignment would not be safe.

  • Conversion operators specifying conversion to void are allowed.

  • A nonstandard friend declaration may introduce a new type. A friend declaration that omits the elaborated type specifier is allowed in default mode, but in Cfront-compatibility mode, the declaration is also allowed to introduce a new type name, as follows:

    struct A {
        friend B;

  • A reference to a pointer type may be initialized from a pointer value without use of a temporary even when the reference pointer type has additional type qualifiers above those present in the pointer value. For example,

    int *p;
    const int *&r = p;  // No temporary used

  • A reference may be initialized with a null.

  • Because Cfront does not check the accessibility of types, access errors for types are issued as warnings instead of errors.

  • When matching arguments of an overloaded function, a const variable with a value of zero is not considered to be a null pointer constant.

  • An alternate form of declaring pointer-to-member-function variables is supported. Consider the following code sample:

    struct A {
        void f(int);
        static void f(int);
        typedef void A::T3(int);  // Non-std typedef declaration
        typedef void T2(int);     // Std typedef declaration
    typedef void A::T(int);  // Non-std typedef declaration
    T* pmf = &A::f;          // Non-std ptr-to-member declaration
    A::T2* pf = A::sf;       // Std ptr to static mem declaration
    A::T3* pmf2 = &A::f;     // Non-std ptr-to-member declaration

    In this example, T is construed to name a routine type for a nonstatic member function of class A that takes an int argument and returns void; the use of such types is restricted to nonstandard pointer-to-member declarations. The declarations of T and pmf in combination are equivalent to a single standard pointer-to-member declaration, such as in the following example:

    void (A::* pmf)(int) = &A::f;

    A nonstandard pointer-to-member declaration that appears outside a class declaration, such as the declaration of T, is normally invalid and would cause an error to be issued. However, for declarations that appear within a class declaration, such as A::T3, this feature changes the meaning of a valid declaration. Version 2.1 of Cfront accepts declarations, such as T, even when A is an incomplete type; so this case is also excepted.

  • Protected member access checking is not done when the address of a protected member is taken. For example:

    class B { protected: int i; };
    class D : public B { void mf(); };
    void D::mf() {
      int B::* pmi1 = &B::i;  // Error;OK in cfront-compatibility mode
      int D::* pmi2 = &D::i;  // OK

    Note: Protected member access checking for other operations (in other words, everything except taking a pointer-to-member address) is done in the normal manner.

  • The destructor of a derived class may implicitly call the private destructor of a base class. In default mode this is an error, but in Cfront-compatibility mode it is reduced to a warning. For example:

    class A {
    class B : public A {
    B::~B(){}    // Error except in Cfront-compatibility mode

  • When disambiguation requires deciding whether something is a parameter declaration or an argument expression, the pattern type-name-or-keyword (identifier...) is treated as an argument. For example:

    class A { A(); };
    double d;
    A x(int(d));

    By default int(d) is interpreted as a parameter declaration (with redundant parentheses), and x is a function; but in Cfront-compatibility mode int(d) is an argument and x is a variable.

    The statement A(x2); is also misinterpreted by Cfront. It should be interpreted as the declaration of an object named x2 , but in Cfront-compatibility mode is interpreted as a function style cast of x2 to the type A.

    Similarly, the statement

    int xyz(int());

    declares a function named xyz, that takes a parameter of type “function taking no arguments and returning an int .” In Cfront-compatibility mode, this is interpreted as a declaration of an object that is initialized with the value int() (which evaluates to zero).

  • A named bit-field may have a size of zero. The declaration is treated as though no name had been declared.

  • Plain bit fields (in other words, bit fields declared with type int) are always unsigned.

  • The name given in an elaborated type specifier is permitted to be a typedef name that is the synonym for a class name, for example:

    typedef class A T;
    class T *pa;      // Not an error in cfront-compatibility mode

  • No warning is issued on duplicate size and sign specifiers.

    short short int i; // No warning given in cfront-compatibility mode

  • Virtual function table pointer update code is not generated in destructors for base classes of classes without virtual functions, even if the base class virtual functions might be overridden in a further-derived class. For example:

    struct A {
        virtual void f() {}
        A() {}
        ~A() {}
    struct B : public A {
        B() {}
        ~B() {f();}  // Should call A::f according to ARM 12.7
    struct C : public B {
        void f() {}
    } c;

    In Cfront-compatibility mode, B::~B calls C::f.

  • An extra comma is allowed after the last argument in an argument list, as in the following example:

    f(1, 2, );

  • A constant pointer-to-member function may be cast to a pointer to function. A warning is issued.

    struct A {int f();};
    main () {
        int (*p)();
        p = (int (*)())A::f;  // Okay, with warning

  • Arguments of class types that allow bitwise copy construction but also have destructors are passed by value (in other words, like C structures), and the destructor is not called on the new copy. In normal mode, the class object is copied into a temporary, the address of the temporary is passed as the argument, and the destructor is called on the temporary after the call returns.

    Note: Because the argument is passed differently (by value instead of by address), code like this compiled in Cfront-compatibility mode is not calling-sequence compatible with the same code compiled in normal mode. In practice, this is not much of a problem, since classes that allow bitwise copying usually do not have destructors.

  • A union member may be declared to have the type of a class for which the user has defined an assignment operator (as long as the class has no constructor or destructor). A warning is issued.

  • When an unnamed class appears in a typedef declaration, the typedef name may appear as the class name in an elaborated type specifier. For example:

    typedef struct { int i, j; } S;
    struct S x; // No error in cfront-compatibility mode

Cfront Compatibility Restrictions

Even when you specify the -cfront option, the N32, 64, and O32 C++ compilers are not completely backwards-compatible with Cfront. The N32, 64, and O32 compilers reject the following source constructs that Cfront ignores:

  • Assignment to this in constructors and destructors is not allowed (O32 generates a warning).

  • If a C++ comment line (//) is terminated with a backslash, the MIPSpro compilers (correctly) continue the comment line into the next source line. Cfront uses the standard UNIX cpp and terminates the comment at the end of the line.

  • You must have an explicit declaration of a constructor or destructor in the class if there is an explicit definition of it outside the class.

  • You may not pass a pointer to volatile data to a function that is expecting a pointer to non-volatile data.

  • The MIPSpro compilers do not disambiguate between overloaded functions with a char* and long parameter, respectively, when called with an expression that is a zero cast to a char type.

  • You may not use redundant type specifiers.

  • When in a conditional expression, the MIPSpro compilers do not convert a pointer to a class to an accessible base class of that class.

  • You may not assign a comma-expression ending in a literal constant expression “0” to a pointer; the “0” is treated as an int.

  • The MIPSpro compilers mangle member functions declared as extern “C” differently from Cfront. The CC command does not strip the type signature when you are building the mangled name. If you try to do so, the following warning is issued:

    Mangling of classes within an extern “C” block does not 
    match cfront name mangling.

    You may not be able to link code containing a call to such a function with code containing the definition of the function that was compiled with Cfront.