0

Possible Duplicate:
Why should the implementation and the declaration of a template class be in the same header file?

I am reading the book "C++ The Complete Reference", and I adapted the parser example of Chapter 40, in three files. These are the files:

/* parser2.h */

enum types { DELIMITER = 1, VARIABLE, NUMBER};

const int NUMVARS = 26;

template <class PType> class parser
{
    char *exp_ptr;  // Points to the expression
    char token[80]; // Holds current token
    char tok_type;  // Holds token's type
    PType vars[NUMVARS]; // Holds variables' values

    void eval_exp1(PType &result);
    void eval_exp2(PType &result);
    void eval_exp3(PType &result);
    void eval_exp4(PType &result);
    void eval_exp5(PType &result);
    void eval_exp6(PType &result);
    void atom(PType &result);
    void get_token(), putback();
    void serror(int error);
    int isdelim(char c);
    PType find_var(char *s);
public:
    parser();
    PType eval_exp(char *exp);
};

/* parser2.cc */

#include "parser2.h"
#include <iostream>
#include <cstdlib>
#include <cctype>
#include <cstring>

using namespace std;

// Parser constructor.
template <class PType> parser<PType>::parser()
{
    int i;
    exp_ptr = NULL;
    for(i=0; i<NUMVARS; i++)
       vars[i] = (PType) 0;
}

// Parser entry point.
template <class PType> PType parser<PType>::eval_exp(char *exp)
{
    PType result;
    exp_ptr = exp;
    get_token();
    if(!*token)
    {
        serror(2); // No expression present
        return (PType) 0;
    }
    eval_exp1(result);
    if(*token)
        serror(0); // Last token must be null
    return result;
}

// Process an assignment.
template <class PType> void parser<PType>::eval_exp1(PType &result)
{
    int slot;
    char ttok_type;
    char temp_token[80];
    if(tok_type == VARIABLE)
    {
        // Save old token
        strcpy(temp_token, token);
        ttok_type = tok_type;

        // Compute the index of the variable
        slot = toupper(*token) - 'A';
        get_token();
        if(*token != '=')
        {
            putback(); // Return current token

            // Restore old token - not assignment
            strcpy(token, temp_token);
            tok_type = ttok_type;
        }
        else
        {
            get_token(); // Get next part of exp
            eval_exp2(result);
            vars[slot] = result;
            return;
        }
    }
    eval_exp2(result);
}

// Add or subtract two terms.
template <class PType> void parser<PType>::eval_exp2(PType &result)
{
    register char op;
    PType temp;
    eval_exp3(result);
    while((op = *token) == '+' || op == '-')
    {
        get_token();
        eval_exp3(temp);
        switch(op)
        {
            case '-':
               result = result - temp;
               break;

            case '+':
                result = result + temp;
                break;
            }
    }
}

// Multiply or divide two factors.
template <class PType> void parser<PType>::eval_exp3(PType &result)
{
    register char op;
    PType temp;
    eval_exp4(result);
    while((op = *token) == '*' || op == '/' || op == '%')
    {
        get_token();
        eval_exp4(temp);
        switch(op)
        {
            case '*':
                result = result * temp;
                break;

            case '/':
                result = result / temp;
                break;

            case '%':
                result = (int) result % (int) temp;
                break;
        }
    }
}

// Process an exponent.
template <class PType> void parser<PType>::eval_exp4(PType &result)
{
    PType temp, ex;
    register int t;
    eval_exp5(result);
    if(*token == '^')
    {
        get_token();
        eval_exp4(temp);
        ex = result;
        if(temp == 0.0)
        {
            result = 1.0;
            return;
        }
        for(t=(int)temp-1; t>0; --t)
            result = result * ex;
   }
}

// Evaluate a unary + or -.
template <class PType> void parser<PType>::eval_exp5(PType &result)
{
    register char op;
    op = 0;
    if((tok_type == DELIMITER) && (*token=='+' || *token == '-'))
    {
        op = *token;
        get_token();
    }
    eval_exp6(result);
    if(op=='-')
        result = -result;
}

// Process a parenthesized expression.
template <class PType> void parser<PType>::eval_exp6(PType &result)
{
    if((*token == '('))
    {
        get_token();
        eval_exp2(result);
        if(*token != ')')
            serror(1);
        get_token();
   }
   else
       atom(result);
}

// Get the value of a number or a variable.
template <class PType> void parser<PType>::atom(PType &result)
{
    switch(tok_type)
    {
        case VARIABLE:
            result = find_var(token);
            get_token();
            return;

        case NUMBER:
            result = (PType) atof(token);
            get_token();
            return;

        default:
            serror(0);
   }
}

// Return a token to the input stream.
template <class PType> void parser<PType>::putback()
{
    char *t;
    t = token;
    for(; *t; t++)
        exp_ptr--;
}

// Display a syntax error.
template <class PType> void parser<PType>::serror(int error)
{
    string e[] =
    {
        "Syntax Error",
        "Unbalanced Parentheses",
        "No expression Present"
    };
    cout << e[error] << endl;
}

// Obtains the next token.
template <class PType> void parser<PType>::get_token()
{
    register char *temp;
    tok_type = 0;
    temp = token;
    *temp = '\0';
    if(!*exp_ptr)
        return; // At the end of the expression

    while(isspace(*exp_ptr))
        ++exp_ptr; // Skip over white space

    if(strchr("+-*/%^=()", *exp_ptr))
    {
        tok_type = DELIMITER;

        // Advance to the next character
        *temp++ = *exp_ptr++;
    }
    else if(isalpha(*exp_ptr))
    {
        while(!isdelim(*exp_ptr))
            *temp++ = *exp_ptr++;

        tok_type = VARIABLE;
    }
    else if(isdigit(*exp_ptr))
    {
        while(!isdelim(*exp_ptr))
            *temp++ = *exp_ptr++;

        tok_type = NUMBER;
    }
    *temp = '\0';
}

// Return true if c is a delimiter.
template <class PType> int parser<PType>::isdelim(char c)
{
    if(strchr(" +-/*%^=()", c) || c==9 || c=='\r' || c==0)
        return 1;
    return 0;
 }

// Return the value of a variable.
template <class PType> PType parser<PType>::find_var(char *s)
{
    if(!isalpha(*s))
    {
        serror(1);
        return (PType) 0;
    }
    return vars[toupper(*token)-'A'];
}

/* main2.cc */

#include "parser2.h"
#include <iostream>

using namespace std;

int main(void)
{
    char expstr[80];

    // Demonstrate floating-point parser.
    parser<double> ob;
    cout << "Floating-point parser.  ";
    cout << "Enter a period to stop.\n";
    for(;;)
    {
        cout << "Enter expression: ";
        cin.getline(expstr, 79);
        if(*expstr == '.')
            break;
        cout << "Answer is: " << ob.eval_exp(expstr) << "\n\n";
    };

    // Demonstrate integer-based parser.
    parser<int> Iob;
    cout << "Integer parser.  ";
    cout << "Enter a period to stop.\n";
    for(;;)
    {
        cout << "Enter expression: ";
        cin.getline(expstr, 79);
        if(*expstr == '.')
        break;
        cout << "Answer is: " << Iob.eval_exp(expstr) << "\n\n";
    };

    return 0;
}

When I try to build the program, I find these linker errors (I am using Code::Blocks 10.05 in Windows with GCC 4.5.2.):

=== Parser2, Release ===
obj\Release\main2.o:main2.cc undefined reference to `parser<double>::parser()'
obj\Release\main2.o:main2.cc undefined reference to `parser<double>::eval_exp(char*)'
obj\Release\main2.o:main2.cc undefined reference to `parser<int>::parser()'
obj\Release\main2.o:main2.cc undefined reference to `parser<int>::eval_exp(char*)'
=== Build finished: 4 errors, 0 warnings ===

Why does this happen?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
J. C. M. H.
  • 159
  • 1
  • 9
  • You're new here, so I fixed it, but next time be more careful choosing tags. `c#` is not the same as `c++`. – Ben Voigt Sep 04 '11 at 04:55
  • My apologies, Ben Voigt. I put C# instead of C++. – J. C. M. H. Sep 04 '11 at 05:02
  • @Ben While that does seem to be the problem, the OP didn't know about it in particular. In the linked question, the OP already knew about it and was asking why it was a problem. Should this still be marked as a duplicate? – flight Sep 04 '11 at 05:03
  • 1
    @quasiverse The other question answers this one, this question can't really add anything, so I'd say yes, it should be marked as a duplicate. – Beta Sep 04 '11 at 05:12
  • I agree with closing this. "This question covers exactly the same ground as earlier questions on this topic." Seems to fit. – sbi Sep 04 '11 at 10:12

0 Answers0