-2

I'm trying to import a lightweight maths parsing library. It is only available as a .cpp file. (this is the library)

When I import it using #include mathparser.cpp, I get loads of LNK2005 errors, saying it is defining all the class methods again.

I'm not actually defining them in the main file though, why might these errors be occuring, and what should I do to fix them?

Arin Bryan
  • 29
  • 1
  • 3
  • 2
    __Do not__ include cpp files. Write a header file. – tkausl Dec 13 '22 at 15:18
  • 1
    Use a header file and correct header guards for the function / class declarations. – πάντα ῥεῖ Dec 13 '22 at 15:19
  • Only #include header files. If the library is just a single Cpp file, separately compile it as a static library file (.o), and link it at compile time using -l option. Or compile it as a shared library file (.so, or .dll as Windows call it), and tell your application executable the whereabouts of your .so file by specifying the .so file’s path in environment variable LD_LIBRARY_PATH. And since the library is just a cpp file, you declare in your application code “everything already declared in the library cpp” as extern. – fjs Dec 13 '22 at 15:20
  • Cut and paste the lines between 30 and 58 into a header file and include it from the cpp file and any other source file that needs to use this library. – drescherjm Dec 13 '22 at 15:22
  • 1
    What you have there is not a library. It is a C++ program. It says so in the first line of the description. – n. m. could be an AI Dec 13 '22 at 15:30

1 Answers1

1

You should never #include a cpp file. You should include header files, and ensure they have header guards (or use #pragma once). In this case the mathparser.cpp should be split up such that the parser class is in its own header and cpp file, then just main is in the cpp file.

mathparser.h

#pragma once
 
#include < iostream >
#include < cstdlib >
#include < cctype >
#include < cstring >
#include < math.h > 
 
#define PI 3.14159265358979323846 
 
using namespace std;
 
enum types { DELIMITER = 1, VARIABLE, NUMBER, FUNCTION };
const int NUMVARS = 26;
class parser {
    char *exp_ptr; // points to the expression
    char token[256]; // holds current token
    char tok_type; // holds token's type
    double vars[NUMVARS]; // holds variable's values
    void eval_exp1(double &result);
    void eval_exp2(double &result);
    void eval_exp3(double &result);
    void eval_exp4(double &result);
    void eval_exp5(double &result);
    void eval_exp6(double &result);
    void get_token();
public:
    parser();
    double eval_exp(char *exp);
    char errormsg[64];
};

mathparser.cpp

#include "stdafx.h"
#include "mathparser.h"

// Parser constructor.
parser::parser()
{
    int i;
    exp_ptr = NULL;
    for (i = 0; i < NUMVARS; i++)
        vars[i] = 0.0;
    errormsg[0] = '\0';
}
// Parser entry point.
double parser::eval_exp(char *exp)
{
    errormsg[0] = '\0';
    double result;
    exp_ptr = exp;
    get_token();
    if (!*token) 
    {
        strcpy(errormsg, "No Expression Present"); // no expression present
        return (double)0;
    }
    eval_exp1(result);
    if (*token) // last token must be null
        strcpy(errormsg, "Syntax Error");
    return result;
}
// Process an assignment.
void parser::eval_exp1(double &result)
{
    int slot;
    char temp_token[80];
    if (tok_type == VARIABLE) 
    {
        // save old token
        char *t_ptr = exp_ptr;
        strcpy(temp_token, token);
        // compute the index of the variable
        slot = *token - 'A';
        get_token();
        if (*token != '=') 
        {
            exp_ptr = t_ptr; // return current token
            strcpy(token, temp_token); // restore old token
            tok_type = VARIABLE;
        }
        else {
            get_token(); // get next part of exp
            eval_exp2(result);
            vars[slot] = result;
            return;
        }
    }
    eval_exp2(result);
}
// Add or subtract two terms.
void parser::eval_exp2(double &result)
{
    register char op;
    double 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.
void parser::eval_exp3(double &result)
{
    register char op;
    double temp;
    eval_exp4(result);
    while ((op = *token) == '*' || op == '/') 
    {
        get_token();
        eval_exp4(temp);
        switch (op) 
        {
        case '*':
            result = result * temp;
            break;
        case '/':
            result = result / temp;
            break;
        }
    }
}
// Process an exponent.
void parser::eval_exp4(double &result)
{
    double temp;
    eval_exp5(result);
    while (*token == '^')
    {
        get_token();
        eval_exp5(temp);
        result = pow(result, temp);
    }
}
// Evaluate a unary + or -.
void parser::eval_exp5(double &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 function, a parenthesized expression, a value or a variable
void parser::eval_exp6(double &result)
{
    bool isfunc = (tok_type == FUNCTION);
    char temp_token[80];
    if (isfunc)
    {
        strcpy(temp_token, token);
        get_token();
    } 
    if ((*token == '(')) 
    {
        get_token();
        eval_exp2(result);
        if (*token != ')')
            strcpy(errormsg, "Unbalanced Parentheses");
        if (isfunc)
        {
            if (!strcmp(temp_token, "SIN"))
                result = sin(PI / 180 * result);
            else if (!strcmp(temp_token, "COS"))
                result = cos(PI / 180 * result);
            else if (!strcmp(temp_token, "TAN"))
                result = tan(PI / 180 * result);
            else if (!strcmp(temp_token, "ASIN"))
                result = 180 / PI*asin(result);
            else if (!strcmp(temp_token, "ACOS"))
                result = 180 / PI*acos(result);
            else if (!strcmp(temp_token, "ATAN"))
                result = 180 / PI*atan(result);
            else if (!strcmp(temp_token, "SINH"))
                result = sinh(result);
            else if (!strcmp(temp_token, "COSH"))
                result = cosh(result);
            else if (!strcmp(temp_token, "TANH"))
                result = tanh(result);
            else if (!strcmp(temp_token, "ASINH"))
                result = asinh(result);
            else if (!strcmp(temp_token, "ACOSH"))
                result = acosh(result);
            else if (!strcmp(temp_token, "ATANH"))
                result = atanh(result);
            else if (!strcmp(temp_token, "LN"))
                result = log(result);
            else if (!strcmp(temp_token, "LOG"))
                result = log10(result);
            else if (!strcmp(temp_token, "EXP"))
                result = exp(result);
            else if (!strcmp(temp_token, "SQRT"))
                result = sqrt(result);
            else if (!strcmp(temp_token, "SQR"))
                result = result*result;
            else if (!strcmp(temp_token, "ROUND"))
                result = round(result);
            else if (!strcmp(temp_token, "INT"))
                result = floor(result);
            else
                strcpy(errormsg, "Unknown Function");
        }
        get_token();
    }
    else
        switch (tok_type)
        {
        case VARIABLE:
            result = vars[*token - 'A'];
            get_token();
            return;
        case NUMBER:
            result = atof(token);
            get_token();
            return;
        default:
            strcpy(errormsg, "Syntax Error");
        }
}
// Obtain the next token.
void parser::get_token()
{
    register char *temp;
    tok_type = 0;
    temp = token;
    *temp = '\0';
    if (!*exp_ptr)  // at end of expression
        return;
    while (isspace(*exp_ptr))  // skip over white space
        ++exp_ptr; 
    if (strchr("+-*/%^=()", *exp_ptr)) 
    {
        tok_type = DELIMITER;
        *temp++ = *exp_ptr++;  // advance to next char
    }
    else if (isalpha(*exp_ptr)) 
    {
        while (!strchr(" +-/*%^=()\t\r", *exp_ptr) && (*exp_ptr))
            *temp++ = toupper(*exp_ptr++);
        while (isspace(*exp_ptr))  // skip over white space
            ++exp_ptr;
        tok_type = (*exp_ptr == '(') ? FUNCTION : VARIABLE;
    }
    else if (isdigit(*exp_ptr) || *exp_ptr == '.')
    {
        while (!strchr(" +-/*%^=()\t\r", *exp_ptr) && (*exp_ptr))
            *temp++ = toupper(*exp_ptr++);
        tok_type = NUMBER;
    }
    *temp = '\0';
    if ((tok_type == VARIABLE) && (token[1]))
        strcpy(errormsg, "Only first letter of variables is considered");
}

main.cpp

#include "mathparser.h"

int main()
{
    char expstr[256];
    parser ob;
    cout << "Math expression parser. Enter a blank line to stop.\n\n";
    do
    {
        cout << "Enter expression: ";
        cin.getline(expstr, 255);
        double ans = ob.eval_exp(expstr);
        if (*ob.errormsg)
            cout << "Error: " << ob.errormsg << "\n\n";
        else
            cout << "Answer: " << ans << "\n\n";
    } while (*expstr);
    return 0;
}

Then in your code you can #include "mathparser.h" to instantiate the parser class for your purposes.

Note that this code itself is full of poor practices and therefore a bad reference to study for learning modern C++, but that is outside the scope of your current question.

Cory Kramer
  • 114,268
  • 16
  • 167
  • 218