_OBJECT-ORIENTED DEBUGGING_
by Simon Tooke


[LISTING ONE]

#ifndef STRING_H
#define STRING_H

#include <string.h>
#include <memory.h>
#include <malloc.h>

#ifdef NULL
# undef NULL
#endif
#define NULL 0

typedef enum { False, True } Boolean;

// a String is a simple implementation of a C++ string class
class String 
{
    char     *s;        // actual pointer to text
  public:
    String(void)           { s = strdup(""); }
    String(char *c)        { s = strdup(c); }
    String(char *c, int n) { s = new char[n+1]; memcpy(s,c,n); s[n]=0; }
    String(String &ss, char *c) { s = malloc(strlen(ss.s)+strlen(c)+1); 
                                                 strcpy(s,ss.s); strcat(s,c); }
    String(String &ss)     { s = strdup(ss.s); }
    ~String(void)          { delete s; }
    operator char *(void)  { return s; }
    String& operator +(char *c)  { String *a = new String(*this,c); return *a;}
    String& operator +=(char *c) { *this = *this + c; return *this; }
    String& operator =(char *c)  { delete s; s = strdup(c); return *this; }
    String& operator =(String& a){ delete s; s = strdup(a.s); return *this; }
    operator ==(char *c) const;
    operator ==(String *c) const;
#ifdef DEBUG
    void dump() const;
    Boolean verify() const;
#endif /*DEBUG*/
};

// a StringListElement is a single item in a StringList
class StringListElement 
{
    String s;                   // this String in the list
    StringListElement *next;    // pointer to next element in list
  public:
    StringListElement(void)     : next(NULL), s("")  {}
    StringListElement(char *c)  : next(NULL), s(c)   {}
    StringListElement(char *c, int n) : next(NULL), s(c,n) {}
    StringListElement(String &ss)     : next(NULL), s(ss)  {}
    ~StringListElement(void)    { if (next) delete next; next=NULL; }
    friend class StringList;
    friend class StringListIterator;
#ifdef DEBUG
    void dump() const;
    Boolean verify() const;
#endif /*DEBUG*/
};

// a StringList is a simple single-linked list of strings
class StringList 
{
    StringListElement *head;    // first String in list
    StringListElement *tail;    // last String in list
  public:
    StringList(void) : head(NULL), tail(NULL) {}
    StringList(String& ss) { head = tail = new StringListElement(ss); }
    StringList(char *ss)   { head = tail = new StringListElement(ss); }
    ~StringList(void)      { if (head) { delete head; head=NULL; } }
    String& find(char *s) const;
    void clear(void)       { delete head; head = tail = NULL; }
    StringList& operator +=(StringList& xx);
    StringList& operator +=(char *ss);
    StringList& operator +=(String& ss);
    StringList& operator =(StringList& xx);
    operator int();
    friend class StringListIterator;
#ifdef DEBUG
    void dump() const;
    Boolean verify() const;
#endif /*DEBUG*/
};

// a StringListIterator is a method of traversing a list of strings
class StringListIterator 
{
    const StringList *list;    // StringList to be traversed
    StringListElement *nxt;    // current String in StringList
  public:
    StringListIterator(void) : list(NULL), nxt(NULL) {}
    StringListIterator(const StringList *l) : list(l), nxt(l->head) {}
    String *next(void);
    void reset()        { nxt = (list != NULL) ? list->head: NULL; }
    int anymore() const { return (nxt != NULL); }
    StringListIterator& operator =(const StringList& ss);
#ifdef DEBUG
    void dump() const;
    Boolean verify() const;
#endif /*DEBUG*/
};
#endif // STRING_H


[LISTING TWO]

#include <stream.h>
#include "String.h"

int main (int, char *[]) 
{
    String a("Hello ");
    String *b = new String("world.");
    String c;

    c = a + *b + "\n";

    cout << "a + b = " << (char *)c;
    cout << "a = " << (char *)a << "\n";
    cout << "b = " << (char *)*b << "\n";

    StringList l(a);
    l += *b;

    l.dump();
}


[LISTING THREE]

#ifndef ASSERT_HDR
#define ASSERT_HDR

#ifdef DEBUG
extern void _assertRtn(char *, int);

# define assert(condition) \
    if (condition) ; else _assertRtn(__FILE__,__LINE__);

#else /*ifndef DEBUG*/

# define assert(condition)

#endif

#endif /*ASSERT_HDR*/


[LISTING FOUR]

#include <stream.h>

#ifdef DEBUG
void _assertRtn(char *file, int line) 
{
    cerr << "\nAssertion Failure in file '" << file 
                          << "' line " << line << "\n";
    line = 0; line /= line;    // force core dump
}    
#endif


[LISTING FIVE]

#include "String.h"
#include "Assert.h"

#ifdef DEBUG
# include <stream.h>
#endif

/*****  String class  ******/
// String comparison operator
String::operator ==(char *c) const 
{
    assert(this->verify());

    // compare String to char array
    return strcmp(s,c) == 0;
}

// String comparison operator
String::operator ==(String *c) const 
{
    assert(this->verify());
    // compare String to String
    return strcmp(s,(char *)c) == 0;
}
#ifdef DEBUG
void String::dump(void) const 
{
    assert(this->verify());
    cerr << "String(\"" << s << "\")";
}
Boolean String::verify(void) const 
{
    // Strings must always point to something.
    if (s == NULL) return False;    
    return True;
}
#endif

/****** StringList class (and StringListElement)  ******/
StringList& StringList::operator +=(String& ss) 
{
    assert(this->verify());
    if (tail) 
    {
        tail->next = new StringListElement(ss);
        tail = tail->next;
    }
    else
        head = tail = new StringListElement(ss);
    return *this;
}
StringList& StringList::operator +=(char *ss) 
{
    assert(this->verify());
    if (tail) 
    {
        tail->next = new StringListElement(ss);
        tail = tail->next;
    }
    else
        head = tail = new StringListElement(ss);
    return *this;
}
StringList& StringList::operator +=(StringList& xx) 
{
    assert(this->verify());
    // add new list to old list item by item
    for (StringListElement *le=xx.head; le; le=le->next)
        *this += le->s;
    return *this;
}
// StringList assignment operator (performs deep copy)
StringList& StringList::operator =(StringList& xx) 
{
    assert(this->verify());
    // get rid of old list
    clear();
    // add new list to (clear) old list item by item
    for (StringListElement *le=xx.head; le; le=le->next)
        *this += le->s;
    // return new copy of old list
    return *this;
}
// (int)(StringList) cast returns number of strings in list
StringList::operator int() 
{
    int count = 0;
    StringListIterator ll(this);

    assert(this->verify());
    while (ll.next() != NULL)
        count++;
    return count;
}
#ifdef DEBUG
//
//        dump() - display instance in format "StringList(...)"
//
void StringList::dump(void) const 
{
    // check consistancy
    assert(this->verify());
    // print header
    cerr << "StringList(";
    // use StringListElement::dump() to recursively display all members
    if (head != NULL) head->dump();
    // print trailer
    cerr << ")\n";
}
Boolean StringList::verify(void) const 
{
    // if there are elements in this list, ensure they are valid.
    // (note that head->verify() ensures the entire list is valid.)
    if ((head!=NULL) && !head->verify()) return False;

    // Both the head and tail must either be null or non-null.
    if ((head!=NULL) && (tail==NULL)) return False;
    if ((head==NULL) && (tail!=NULL)) return False;

    return True;
}
#endif /*DEBUG*/

#ifdef DEBUG
void StringListElement::dump(void) const 
{
    assert(this->verify());
    s.dump();
    if (next != NULL) 
    {
        cerr << ',';
        next->dump();
    }
}
Boolean StringListElement::verify(void) const 
{
    // An element of a list of Strings must point to a valid String.
    if ((char *)(s) == NULL) return False;
    if (!s.verify()) return False;
    // If there is another element within this list, it must be valid.
    if ((next!=NULL) && !next->verify()) return False;

    return True;
}
#endif

/****** StringListIterator class  ******/
// assignment operator
StringListIterator& StringListIterator::operator =(const StringList& ss) 
{
    assert(this->verify());
    list = &ss;
    nxt = ss.head;
    return *this;
}
// get next item in list of strings pointed to by iterator
String *StringListIterator::next(void) 
{
    assert(this->verify());
    if (list == NULL) return NULL;   // no StringList, so no next item
    if (nxt == NULL) return NULL;    // at end of list, so no next item
    String *aa = &(nxt->s);          // save pointer to String
    nxt = nxt->next;                 // point to next item in list
    return aa;                       // return pointer to String
}
#ifdef DEBUG
Boolean StringListIterator::verify(void) const 
{
    // if there is a list available, verify it.
    if (!list->verify()) return False;

    // if we haven't reached the end of the list, 
    // verify the next element
    if (nxt != NULL && !nxt->verify()) return False;

    // everything appears correct
    return True;
}
#endif /*DEBUG*/


[LISTING SIX]

CC = CC
CFLAGS = -DDEBUG

prog : main.o String.o lib.o
    $(CC) main.o String.o lib.o -o prog

String.o : String.C String.h
    $(CC) -c $(CFLAGS) String.C

main.o : main.C String.h
    $(CC) -c $(CFLAGS) main.C

lib.o : lib.C
    $(CC) -c $(CFLAGS) lib.C

clean :
    rm -f *.o a.out core

clobber : clean
    rm -f prog