Project 7: Stack

A generic Stack data structure

Educational Objectives: After completing this assignment the student should have the following knowledge, ability, and skills:

Operational Objectives: Create the generic container class fsu::Stack<T,N> that satisfies the interface requirements given below, along with an appropriate test harness for the class.

Deliverables: Three files stack.t, fstack.cpp, and log.txt.

Assessment Rubric

=============================================
builds:   [2 pts each]
  fstack.x [student harness]               x
  fstack_char.x                            x
  fstack_int.x                             x
  fstack_String.x                          x
  constTest.x                              x
tests:    [8 pts each]
  fstack_char.x   b < s.com1     [0..8]:   x
  fstack_int.x    b < s.com2     [0..8]:   x
  fstack_String.x b < s.com3     [0..8]:   x
  constTest.x                    [0..8]:   x
log.txt                        [-50..8]:   x
code quality                   [-50..0]: ( x)

dated submission deduction [2 pts each]: ( x)
                                          --
total                           [0..50]:  xx
=============================================

Abstract Data Types

An abstract data type, abbreviated ADT, consists of three things:

  1. A set of elements of some type T
  2. Operations that may modify the set or return values associated with the set
  3. Rules of behavior, or axioms, that determine how the operations interact

The operations and axioms together should determine a unique characterization for the ADT, so that any two implementations should be essentially equivalent. (The word isomorphic is used to give precision to "essentially equivalent". We'll look at this in the next course.)

Stacks

The stack ADT is used in many applications and has roots that pre-date the invention of high-level languages. Conceptually, stack is set of data that can be expanded, contracted, and accessed using very specific operations. The stack ADT models the "LIFO", or last-in, first-out, rule. The actual names for the stack operations may vary somewhat from one description to another, but the behavior of the abstract stack operations is well known and unambiguously understood throughout computer science. Stacks are important in many aspects of computing, ranging from hardware design and language translators (compilers) to algorithm control structures.

Typical uses of ADT Stack are (1) runtime environment for modern programming languages (facilitating recursive function calls, among other things), (2) control of the depth first search and backtracking search algorithms, (3) hardware evaluation of postfix expressions, and (4) various compiler operations, such as converting expressions from infix to postfix.

Abstract Stack Interface

The stack abstraction has the following operations and behavior:

Stack Implementation Plan

We will implement the stack abstraction as a C++ class template

template < typename T , size_t N >
Stack;

with the following public methods:

// Stack < T , N > API
void     Push     (const T& t); // push t onto stack; error if full
T        Pop      ();           // pop stack and return removed element; error if stack is empty
T&       Top      ();           // return top element of stack; error if stack is empty
const T& Top      () const;     // const version
size_t   Size     () const;     // return number of elements in stack
size_t   Capacity () const;     // return storage capacity [maximum size] of stack
bool     Empty    () const;     // return 1/true if stack is empty, 0/false if not empty
void     Clear    ();           // make the stack empty
void     Display  (std::ostream& os, char ofc = '\0') const; // output stack contents through os
void     Dump     (std::ostream& os); // output all private data (for development use only)

There should be a full complement of self-management features:

Stack             ();             // default constructor
Stack             (T fill);       // puts "fill" in each slot of the underlying array (keeps size = 0)
Stack             (const Stack&); // copy constructor
~Stack            ();             // destructor
Stack& operator = (const Stack&); // assignment operator

The element and size data will be maintained in private variables:

const size_t capacity_;  // = N = size of array   - fixed during life of stack
T *          data_;      // dynamically allocated array of T objects    - where T objects are stored
size_t       size_;      // current size of stack - dynamic during life of stack

The class constructors will have responsibility for initializing variables and allocating the array data_. Note that capacity_ is a constant, so it must be initialized by the constructor in the initialization list and it cannot be changed during the life of a stack object; capacity_ should be given the value passed in as the second template argument N. The destructor will have the responsibility for de-allocating memory allocated to data_. Values stored in the data_ array and the size_ variable will be correctly maintained by the push and pop operations, using the "upper index" end of the data as the top of the stack. The data in the stack should always be the array elements in the range [0..size_), and the element data_[size_ - 1] is the top of the stack (assuming size_ > 0).

Please note that the dynamically allocated array data_ must be allocated by constructors and de-allocated by the destructor. Thus the underlying "footprint" of the stack object remains fixed as the size changes, even when the size is changed to zero. The only calls to operators new or delete should be in constrctors and destructors in this implementation.

This implementation will have the requirement on clients that the maximum size required for the stack is known in advance and determined by the second template argument - see requirements below.

Procedural Requirements

  1. Create and work within a separate subdirectory cop3330/proj7. Review the COP 3330 rules found in Introduction/Work Rules.

  2. After starting your log, copy the following files from the course directory [LIB] into your proj7 directory:

    proj7/constTest.cpp
    proj7/deliverables.sh
    area51/fstack_*.x
    scripts/submit.sh # not needed if you have set "submit.sh" as a command in your ".bin". 
    

    The naming of these files uses the convention that _s are compiled for Sun/Solaris and _i are compiled for Intel/Linux. Use one of the sample client executables to experiment to get an understanding of how your program should behave.

  3. Define and implement the class template fsu::Stack<T,N> in the file stack.t. Be sure to make log entries for your work sessions.

  4. Devise a test client for Stack<T,N> that exercises the Stack interface for at least one native type and one user-defined type T. Repair your code as necessary. Put this test client in the file fstack.cpp. Be sure to make log entries for your work sessions.

  5. Turn in stack.t, fstack.cpp, and log.txt using the submit.sh submit script.

    Warning: Submit scripts do not work on the program and linprog servers. Use shell.cs.fsu.edu to submit projects. If you do not receive the second confirmation with the contents of your project, there has been a malfunction.

Code Requirements and Specifications

  1. Stack should be a proper type, with full copy support. That is, it should have a public default constructor, destructor, copy constructor, and assignment operator. Be sure that you test the copy constructor and assignment operator.

  2. The Stack constructor should create a stack that is empty but has the capacity to hold N elements, where N is the second template parameter with type size_t. Note that this parameter should be given the default value of 100. This has the effect of making a declaration such as
     
    fsu::Stack<int> s;
     
    legal and create a stack with capacity 100.

  3. Use the implementation plan discussed above. No methods or variables should be added to the class, beyond those specified above and in the implementation plan.

  4. The Display(os, ofc) method is intended to output the contents through the std::ostream object os in bottom-to-top order. The second parameter ofc is a single output formatting character that has the default value '\0'. (The other three popular choices for ofc are ' ', '\t' and '\n'.) The implementation of Display must recognize two cases:

    1. ofc == '\0': send the contents to output with nothing between the elements
    2. ofc != '\0': send the contents to output with ofc preceeding each element of the stack

    Thus, for example, s.Display(std::cout) would send the contents of s to standard output.

  5. The output operator should be overloaded as follows:

    template < typename T , size_t N >
    std::ostream& operator << (std::ostream& os, const Stack<T,N>& s)
    {
      s.Display (os, '\0');
      return os;
    }
    

    The overload of operator <<() should be placed in your stack header file immediately following the class definition.

  6. The class Stack should be in the fsu namespace.

  7. The file stack.t should be protected against multiple reads using the #ifndef ... #define ... #endif mechanism.

  8. The test client program fstack.cpp should adequately test the functionality of stack, including the output operator. It is your responsibility to create this test program and to use it for actual testing of your stack data structure.

Hints