Common M$/UNIX STL Workarounds

The greatest difference between the SGI compiler and linker provided with Irix 6.x and Microsoft's compiler and linker seems to exist in the linker. Where as Irix will see a template's *.C file and remember the implementation, Microsoft will not. See Microsoft's explaination under Section 3.2.2.

So, when working with VC++, the following inclusions and guarding are required (important points are in RED):

// Stack.H

#ifndef __INC__STACK__
#define __INC__STACK__

template <class T>
class Stack
{
public:
        Stack(int = 10); 
        ~Stack() { delete [] stackPtr; }
        int push(const T&); 
        int pop(T&);          // pop an element off the stack
        int isEmpty()const { return top == -1; } 
        int isFull() const { return top == size - 1; }

private:
        int size;             // Number of elements on Stack
        int top; 
        T* stackPtr; 

};

// WIN32 is defined by the Microsoft Compiler and Linker
// Rest assured UNIX does not define it.
#ifdef WIN32
  #include "Stack.C"
#endif

#endif

// End Stack.H

This creates one problem. As the template is instantiated, the *.C file will show up multiple times. So the *.C file will need to be guarded as follows:

// Stack.C

#ifdef WIN32
  #pragma once
#endif

//Constructor with the default size 10
template <class T>
Stack<T>::Stack(int s) {
        size = s > 0 && s < 1000 ? s : 10; 
        top = -1;                  // initialize stack
        stackPtr = new T[size]; 
}

//Push an element onto the Stack.
template <class T>
int Stack<T>::push(const T& item) {
        if (!isFull()) {
                stackPtr[++top] = item;
                return 1;         // push successful
        }
        return 0;                 // push unsuccessful
}

//Pop an element off the Stack.
template <class T>
int Stack<T>::pop(T& popValue) {
        if (!isEmpty()) {
                popValue = stackPtr[top--] ;
                return 1;        // pop successful
        }
        return 0;                // pop unsuccessful
}

// End Stack.C


This workaround satifies CMSC341's separate compilation requirement. Since the *.C file will only be included if WIN32 is defined, UNIX will disreguard the conditional during preprocessing, compilation, and linking. If interested in what the Irix's compiler defines, see here.


#pragma once is the C++ way of doing the following:

#ifndef __INC__STACK__
#define __INC__STACK__

// ...

#endif


The files that CMSC 341 provides you will also require reworking. So, you'll have to download vector.C, vector.H, string.C, string.H, etc. and guard them appropriately.


Also note, the vector.C file contains the following technique: mallocing for 0 elements. A condition exists where vector<Object>::resize will be called with currentSize == 0. This implies that the vector object was created using new with a size of 0. The following was taken from vector.H:

    explicit vector( int theSize = 0 ) : currentSize( theSize )
        { objects = new Object[ currentSize ]; }

UNIX does not complain, but the following code will cause Microsoft VC++ to throw an assert in the Debugger when using virtual destructors (delete[] oldArray when the previous array is 0 in size):

template <class Object>
void vector<Object>::resize( int newSize )
{
    Object* oldArray = objects;
    int numToCopy = newSize < currentSize ? newSize : currentSize;

    objects = new Object[ newSize ];

    currentSize = newSize;

    for( int k = 0; k < numToCopy; k++ )
        objects[ k ] = oldArray[ k ];

    delete [ ] oldArray;
}

The work around is as follows for a Windows machine:

template <class Object>
void vector<Object>::resize( int newSize )
{
    Object* oldArray = objects;
    int numToCopy = newSize < currentSize ? newSize : currentSize;
    objects = new Object[ newSize ];

    // old position of "currentSize = newSize;"

    for( int k = 0; k < numToCopy; k++ )
        objects[ k ] = oldArray[ k ];

    // conditional added
    if(currentSize) {
        delete [ ] oldArray;
    }

    // moved after the delete
    currentSize = newSize;
}

The reworked code will cause instrumentation to detect a memory leak on a Windows machine (shown below), but its the lesser of two evils.

 


Oddly enough, the SGI (and HP) STL - and the Microsoft STL- inline everything in their template code. UMBC has chose to use separate compilation. The following workaround should be performed when working with nested classes/structures. Note that the class being used is named Sample, with a nested structure named Nested:

/////////////////////////////////////////////
// Sample.H
//   Sample Header File
//
// Your Name Bozo
//   555-xx-1212
//   CMSC 341-x
//   Spring 2000
//   Project x
//   who@gl.umbc.edu
//
// Created: 3/2/00 7:37:09 PM
// Current: 3/2/00 7:37:09 PM
/////////////////////////////////////////////
#pragma once

class Sample {

// In case a forward declaration is needed
// A forward declaration would be needed if
//   a public function in Sample (public is
//   before private) returned a 
//   Nested object
struct Nested;

    public:

        // Constructor
        Sample( );
        // Copy
        Sample(const Sample& rhs);
        // Assignment
        Sample& operator=(const Sample& rhs);
        // Destructor
        ~Sample( );

    private:

        struct Nested {
            // structs are public by default

            // Constructor
            Nested( )
                #ifdef WIN32
                    { }
                #else
                    ;
                #endif

            // Copy
            Nested(const Nested& arg)
                #ifdef WIN32
                    { *this = arg; }
                #else
                    ;
                #endif

            // Assignment
            Nested& operator=(const Nested& arg)
                #ifdef WIN32
                    { *this = arg; return(*this); }
                #else
                    ;
                #endif

            // Destructor
            ~Nested( )
                #ifdef WIN32
                    { }
                #else
                    ;
                #endif

            private:
            // Some private stuff

        }; // End Nested

}; // End Sample

#ifdef WIN32
    #include "Sample.C"
#endif

// End Sample.H

/////////////////////////////////////////////
// Sample.C
//   Sample Implementation File
//
// Your Name Bozo
//   555-xx-1212
//   CMSC 341-x
//   Spring 2000
//   Project x
//   who@gl.umbc.edu
//
// Created: 3/2/00 7:37:08 PM
// Current: 3/2/00 7:37:08 PM
/////////////////////////////////////////////
#ifdef WIN32
    #pragma once
#endif

#include "Sample.H"

////////////////////////////////
// Constructor
////////////////////////////////
Sample::Sample( ) { }

////////////////////////////////
// Copy
////////////////////////////////
Sample::Sample(const Sample& rhs) { }

////////////////////////////////
// Assignment
////////////////////////////////
Sample& Sample::operator=(const Sample& rhs) {
    return(*this);
}

////////////////////////////////
// Destructor
////////////////////////////////
Sample::~Sample( ) { }

////////////////////////////////
////////////////////////////////
//  Nested Class is Guarded
//    Irix will use the
//    following code. Windows
//    will use inline code.
////////////////////////////////
////////////////////////////////

#ifndef WIN32

////////////////////////////////
// Constructor
////////////////////////////////
Sample::Nested::Nested( ) { }

////////////////////////////////
// Copy
////////////////////////////////
Sample::Nested::Nested(const Nested& arg) { }

////////////////////////////////
// Assignment
////////////////////////////////
Sample::Nested& Sample::Nested::operator=(const Nested& arg) {
    return(*this);
}

////////////////////////////////
// Destructor
////////////////////////////////
Sample::Nested::~Nested( ) { }

#endif

// End Sample.C