0

Below is my code to perform a lexical analysis. I have a couple of requests.

  • I am getting a LNK2019 error. LNK2019 unresolved external symbol "public: __thiscall Stack::Stack(void)" (??0?$Stack@UToken@LexicalAnalysis@@@@QAE@XZ) referenced in function "public: __thiscall LexicalAnalysis::LexicalAnalysis(void)" (??0LexicalAnalysis@@QAE@XZ). I think it has something to do with the tokens variable and how it is being constructed inside the constructor. Am not sure.

  • Please review my code to see if I am managing memory correctly, and
    to see if my initial approach to the problem is correct.

-

// Stack.h
#pragma once

#include <iostream>
#include <string>

using namespace std;

template <typename T>
class Stack
{
public:
    // Constructors
    Stack();
    Stack(const Stack<T>& rhs);
    Stack(Stack<T>&& rhs);

    // Destructor
    ~Stack();

    // Helper Functions
    inline int getSize() const
    {
        return size;
    }

    // Is the stack empty, no memory
    inline bool isEmpty()
    {
        return (size == 0) ? true : false;
    }

    // Add an element to the stack
    inline void push(const T& value) 
    {
        if (size == capacity) 
        {
            capacity = capacity ? capacity * 2 : 1;

            T* dest = new T[capacity];

            // Copy the contents of the old array into the new array
            copy(data, data + size, dest);

            delete[] data;

            data = dest;
        }

        // Stack size is increased by 1
        // Value is pushed
        data[size++] = value;
    }

    // Add an element to the stack (rvalue ref) IS THIS RIGHT??
    inline void push(const T&& value)
    {
        if (size == capacity)
        {
            capacity = capacity ? capacity * 2 : 1;

            T* dest = new T[capacity];

            // Copy the contents of the old array into the new array
            copy(data, data + size, dest);

            delete[] data;

            data = dest;
        }

        // Stack size is increased by 1
        // Value is pushed
        data[size++] = value;
    }

    // Remove an element from the stack
    inline const T& pop()
    {
        if (size > 0)
        {
            T value;

            size--;

            value = data[size];

            T* dest = new T[size];

            // Copy the contents of the old array into the new array
            copy(data, data + (size), dest);

            delete[] data;

            data = dest;

            return value;
        }

        return NULL;
    }

    // Search stack starting from TOP for x, return the first x found

    T& operator[](unsigned int i)
    {
        return data[i];
    }

    T operator[](unsigned int i) const
    {
        return data[i];
    }

private:
    T* data;

    size_t size, capacity;
};

// Stack.cpp
#include "Stack.h"

// Constructor
template <typename T>
Stack<T>::Stack()
{
    data = nullptr;

    size = 0;
    capacity = 0;
}

// Copy Constructor
template <typename T>
Stack<T>::Stack(const Stack<T>& rhs)
{
    this->size = rhs.size;
    this->capacity = rhs.capacity;

    this->data = new T[size];

    for (int i = 0; i < size; i++)
    {
        data[i] = rhs.data[i];
    }
}

// Move Constructor
template <typename T>
Stack<T>::Stack(Stack<T>&& rhs)
{
    this->size = rhs.size;
    this->capacity = rhs.capacity;
    this->data = rhs.data;

    rhs.data = nullptr;
}

// Destructor
template <typename T>
Stack<T>::~Stack()
{
    delete[] data;
}

// LexicalAnalysis.h
#pragma once

#include <iostream>
#include <string>

#include "Stack.h"

#define NUM_KEYWORDS 3
#define NUM_REL_OP 6
#define NUM_OTHER_OP 2

using namespace std;

class LexicalAnalysis
{
public:
    // Constructor
    LexicalAnalysis();

    // Destructor
    ~LexicalAnalysis();

    // Methods
    void createTokenStack(const string& inputString);

    inline bool isAlpha(const char& ch) const
    {
        int asciiVal = ch;

        if (((asciiVal >= 65) && (asciiVal <= 90)) ||
            ((asciiVal >= 97) && (asciiVal <= 122)))
            return true;

        return false;
    }

    inline bool isWord(const string& str) const
    {
        if (str.length() > 1)
            return true;

        return false;
    }

    inline bool isDigit(const char& ch) const
    {
        int asciiVal = ch;

        if ((asciiVal >= 48) && (asciiVal <= 57))
            return true;

        return false;
    }

    inline bool isRelationalOperator(const char& ch) const
    {
        if (ch == '=' || ch == '<' || ch == '>')
            return true;

        return false;
    }

    inline bool isOtherOperator(const char& ch) const
    {
        if (ch == ',' || ch == '*')
            return true;

        return false;
    }

    inline void printTokenStack()
    {
        for (int i = 0; i < tokens->getSize(); i++)
        {
            cout << tokens[i][0].instance << endl;
        }
    }

private:
    enum TokenType {
        IDENTIFIER,
        KEYWORD,
        NUMBER,
        REL_OP,     // such as ==  <  >  =!  =>  =<
        OTHER_OP,   // such as , * 

        UNDEF,      // undefined
        EOT         // end of token
    };

    struct Token {
        TokenType tokenType;
        string instance;
    };

    Stack<Token>* tokens;

    // Methods
    void splitString(const string& inputString, Stack<string>& result);
};

// LexicalAnalysis.cpp
#include "LexicalAnalysis.h"

LexicalAnalysis::LexicalAnalysis()
{
    tokens = new Stack<Token>();
}


LexicalAnalysis::~LexicalAnalysis()
{
}

void LexicalAnalysis::splitString(const string& inputString, Stack<string>& result)
{
    const char delim[2] = { ',', ' ' };
    char* dup = strdup(inputString.c_str());
    char* token = strtok(dup, delim);

    while (token != NULL)
    {
            result.push(string(token));
            token = strtok(NULL, delim);
        }

        // ARE THESE DELETES NECESSARY?
        delete dup;
        delete token;
    }
}

// main.cpp
#include <iostream>
#include <string>

#include "LexicalAnalysis.h"

using namespace std;

int main(int argv, char* argc)
{
    LexicalAnalysis lex = LexicalAnalysis();

    lex.createTokenStack("GET id, fname, lname FROM employee WHERE id > 5");

    system("PAUSE");
}
Andre Rogers
  • 47
  • 3
  • 10
  • At a quick glance: 1. You missed implementing `operator=` 2. Template function definitions must be visible in the translation unit where the template is instantiated (in other words they go in the header or need to be `#include`d in the header) – Dark Falcon Jul 30 '15 at 21:05
  • @Dark Falcon Should the overloaded operator= be a copy or move constructor or both? Am still getting a handle on const correctness, lvalues, rvalues and rvalue reference (&&). – Andre Rogers Jul 30 '15 at 21:10
  • `operator=` is the equivalent of destructing `this` and copying from the passed in parameter. But don't forget to handle the case of `x = x` (self-assignment) and of any exceptions or allocation failures during the copy, so perhaps a better description is a copy, swap with this (atomic), and destroy previous value. – Dark Falcon Jul 30 '15 at 21:12

1 Answers1

1

The problem is that you defined your Stack methods in a cpp file. All template code should go in header files.

Template definitions must be available to the compiler at the point that the definition is being used. So put the template code in stack.h file, not in stack.cpp and the link error will go away. In fact all the stack.cpp code should be in stack.h, you can get rid of stack.cpp entirely.

On the second question, your pop method is really strange. Why allocate memory on a pop? Just decrement size, no need to allocate more memory.

inline const T& pop()
{
    if (size == 0)
        throw std::runtime_error("stack underflow");
    return data[--size];
}
john
  • 85,011
  • 4
  • 57
  • 81
  • if I just decrement the size, would that memory actually be free'd, and would the remainder of the un-popped values remain or persist? – Andre Rogers Jul 30 '15 at 21:13
  • Nothing would be freed, until the stack itself was destroyed. That's not normally a problem. – john Jul 30 '15 at 21:14