I am working on a VST plugin using C++. The plugin will allow the user to enter a math expression, which will then be run 44100 times per second in order to generate sound. I am new to realtime stuff like this, as well as interpreting user entered expressions.
The issue is that I cannot find a method to evaluate a user defined function that can run that fast. My best attempt has been to convert the user entered expression into RPN when it's entered, and then having a function evaluate the RPN expression to generate the audio. I implemented the RPN evaluation function and hard-coded an RPN expression to test it. While it seems to evaluate correctly, it does not seem to be doing it fast enough.
Here is my evaluation function, in addition to a couple RPN expressions:
#include <string>
#include <stack>
#include <deque>
/*
* an RPN expression is stored as a deque of strings
*
* each string is either an operator, a number, or the single variable t
*
* the deque is read from front to back
*/
std::deque<std::string> simpleCase, complexCase;
//simple expression, just the variable t
simpleCase.push_back("t");
//more complex expression, t*(42&(t>>11))
complexCase.push_back("t");
complexCase.push_back("42");
complexCase.push_back("t");
complexCase.push_back("11");
complexCase.push_back(">>");
complexCase.push_back("&");
complexCase.push_back("*");
/*
* The evalRPN function takes an RPN deque, plugs in a supplied t,
* and evaluates it.
*
* The idea is that t increases continually, and that the integer overflow
* causes the output to oscillate between 0 and 255.
*
* t is a double, but I convert it to a uint32_t.
*
* Allowed operators: bitwise logic (&, |, ^), bitshifts (<<, >>),
* and math (+, -, *, /, %)
*
* Allowed vars: t
*
* Supplied numbers are converted from string to char arrays then to an int
*
* This also assumes the RPN is not ill-formatted.
*/
uint8_t evalRPN(std::deque<std::string> rpnExpr, double tVal)
{
std::stack<uint8_t> numberStack;
std::string token;
while(rpnExpr.size() > 0)
{
token = rpnExpr.front();
rpnExpr.pop_front();
if(token.find_first_not_of("0123456789") == std::string::npos)
{
//if token is a number
numberStack.push((uint8_t)atoi(token.c_str()));
}
else if (token == "t")
{
numberStack.push((uint8_t)tVal);
}
else
{
uint8_t last = numberStack.top();
numberStack.pop();
uint8_t first = numberStack.top();
numberStack.pop();
if(token == "^")
{
numberStack.push(first ^ last);
}
else if (token == "&")
{
numberStack.push(first & last);
}
else if (token == "|")
{
numberStack.push(first | last);
}
else if (token == "<<")
{
numberStack.push(first >> last);
}
else if (token == ">>")
{
numberStack.push(first >> last);
}
else if (token == "+")
{
numberStack.push(first + last);
}
else if (token == "-")
{
numberStack.push(first - last);
}
else if (token == "*")
{
numberStack.push(first * last);
}
else if (token == "/")
{
numberStack.push(first / last);
}
else if (token == "%")
{
numberStack.push(first % last);
}
}
}
//assume one left in numberStack
return(numberStack.top());
}
Are there any optimizations I can make in my RPN processing to make it potentially run fast enough? Alternatively is there another way of handling the RPN computation that's more efficient?
Additionally, is there another method that's C++ compatible for taking a user entered string representing a standard math expression, and then running that expression fast enough to be completed in under 1/44100th of a second?
This is a great question.
Compiling your expression into RPN is a good start, and in fact it kinda looks to me like your code should probably be able to execute more than 88K expressions per second, unless they are quite long.
BUT, you can certainly do a lot better without too much trouble.
I would make an interface like this:
class Expression
{
public:
virtual uint32_t eval(uint32_t tVal) = 0;
};
You will then compile your expression into an implementation of this interface.
You can have an implementation for constants:
class ConstExpression : public Expression
{
private:
uint32_t m_constVal;
public:
// ...
uint32_t eval(uint32_t tVal)
{
return m_constVal;
}
};
...an implementation for a reference to t
class RefExpression : public Expression
{
public:
// ...
uint32_t eval(uint32_t tVal)
{
return m_tVal;
}
};
... and implementations for the binary operators
class AddExpression : public Expression
{
private:
auto_ptr<Expression> m_left;
auto_ptr<Expression> m_right;
public:
// ...
uint32_t eval(uint32_t tVal)
{
return m_left->eval(tVal) + m_right->eval(tVal);
}
};
... maybe you want to do some template magic to avoid having to hand code so many operator classes.
Anyway, after compiling your expression into an Expression, you can evaluate it simply like theExpression->eval(t), and all the code executes in a reasonably efficient manner with no parsing, string compares, stack manipulation, etc.
How about realtime generating code, compile it and then use as binary library function? I'm sure it will work much more faster.
You don't have to. If you are building a vst, you have to use the vst libraries. The host daw will call the update functions automatically.
Thanks for all your advice! I have since implemented a solution I am immensely happy with. While none of the answers here directly informed me of this approach, they did inspire me.
I decided to parse the user-input infix expressions into polish notation (prefix), and then into binary trees. I implemented a TreeNode struct specifically for this, which is essentially a doubly linked list, but with 2 children instead of 1. Each TreeNode can either be set as a variable, a fixed number, or an operator. If it's set as an operator, it's opFunc function pointer member is set to a predefined function that performs that operator.
Each node has an evaluate(tVal) member function. If it's a fixed number, this just returns that number. If it's a variable, it just returns tVal. If it's an operator, it returns opFunc(first->evaluate(tVal), last->evaluate(tVal)), which recursively calculates the two nodes beneath it, then performs the it's operator on them.
I also implemented an ExpressionTree class which manages the tree and parses user input into a tree. It has public members for destroying the tree recursively, building the tree from a user input string, and evaluating the tree (it just needs to evaluate the root node).
Because this uses function pointers in a simple recursive tree, it's astoundingly fast to evaluate an ExpressionTree, even when the formulas get very large. Essentially, I have moved more or less as much processing as possible away from when the function is being evaluated and to when the formula is entered.
Below is the code I wrote for the expression trees. Note that only very limited operators exist, however it is easy to add new binary operators by including them in the precedence map, adding a function for them, and including a switch in setOp. There is also only one variable allowed, which is all my application will use.
There is also no error checking implemented at the moment, so invalid operators, spaces, or mismatched parentheses will result in undefined behavior ATM.
Also note that the expressions are processed as uint32_t and turned into a uint8_t at the end, as that's what my application calls for. When I'm done with this project, I may generalize this as a library and publish it.
TreeExpressions.hpp:
#include <cstdint>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <algorithm>
struct TreeNode {
private:
bool isOp = false;
bool isVar = false;
uint32_t value = 0;
uint32_t(*opFunc)(uint32_t, uint32_t) = NULL;
static inline uint32_t xorOp(uint32_t a, uint32_t b) { return(a ^ b); };
static inline uint32_t andOp(uint32_t a, uint32_t b) { return(a & b); };
static inline uint32_t orOp(uint32_t a, uint32_t b) { return(a | b); };
static inline uint32_t lshiftOp(uint32_t a, uint32_t b) { return(a << b); };
static inline uint32_t rshiftOp(uint32_t a, uint32_t b) { return(a >> b); };
static inline uint32_t addOp(uint32_t a, uint32_t b) { return(a + b); };
static inline uint32_t subOp(uint32_t a, uint32_t b) { return(a - b); };
static inline uint32_t multOp(uint32_t a, uint32_t b) { return(a * b); };
static inline uint32_t divOp(uint32_t a, uint32_t b) { return(a / b); };
static inline uint32_t modOp(uint32_t a, uint32_t b) { return(a % b); };
public:
TreeNode *first = NULL;
TreeNode *last = NULL;
TreeNode *parent = NULL;
uint32_t evaluate(uint32_t tVal);
void setOp(std::string op);
void setVal(uint32_t val);
void setVar();
};
class ExpressionTree
{
private:
std::map<std::string, int> precedence;
TreeNode *treeRoot;
TreeNode *insertNode(TreeNode *leaf);
void destroyTree(TreeNode *leaf);
public:
ExpressionTree();
~ExpressionTree();
void destroyTree();
bool build(std::string formulaStr);
uint8_t evaluate(uint32_t tVal);
};
TreeExpressions.cpp:
#include "TreeExpressions.h"
void TreeNode::setOp(std::string op)
{
isVar = false;
isOp = true;
if (op == "^")
{
opFunc = &xorOp;
}
else if (op == "&")
{
opFunc = &andOp;
}
else if (op == "|")
{
opFunc = &orOp;
}
else if (op == "<<")
{
opFunc = &lshiftOp;
}
else if (op == ">>")
{
opFunc = &rshiftOp;
}
else if (op == "+")
{
opFunc = &addOp;
}
else if (op == "-")
{
opFunc = &subOp;
}
else if (op == "*")
{
opFunc = &multOp;
}
else if (op == "/")
{
opFunc = &divOp;
}
else if (op == "%")
{
opFunc = &modOp;
}
}
void TreeNode::setVal(uint32_t val)
{
isVar = false;
isOp = false;
value = val;
}
void TreeNode::setVar()
{
isVar = true;
isOp = false;
}
uint32_t TreeNode::evaluate(uint32_t tVal)
{
if (isOp)
{
//if it's an op
return( opFunc( first->evaluate(tVal), last->evaluate(tVal) ) );
}
else if (isVar)
{
//if it's a var
return(tVal);
}
else
{
//if it's a number
return(value);
}
}
ExpressionTree::ExpressionTree()
{
treeRoot = NULL;
// http://en.cppreference.com/w/cpp/language/operator_precedence
precedence["*"] = 5;
precedence["/"] = 5;
precedence["%"] = 5;
precedence["+"] = 6;
precedence["-"] = 6;
precedence["<<"] = 7;
precedence[">>"] = 7;
precedence["&"] = 10;
precedence["^"] = 11;
precedence["|"] = 12;
}
ExpressionTree::~ExpressionTree()
{
destroyTree();
}
void ExpressionTree::destroyTree(TreeNode *leaf)
{
if (leaf != NULL)
{
destroyTree(leaf->first);
destroyTree(leaf->last);
delete leaf;
}
}
TreeNode *ExpressionTree::insertNode(TreeNode *leaf)
{
if (leaf->first == NULL)
{
leaf->first = new TreeNode;
leaf->first->parent = leaf;
return(leaf->first);
}
else
{
leaf->last = new TreeNode;
leaf->last->parent = leaf;
return(leaf->last);
}
}
void ExpressionTree::destroyTree()
{
destroyTree(treeRoot);
}
bool ExpressionTree::build(std::string formulaStr)
{
std::string::iterator stringIterator;
std::vector<std::string>::iterator stringVectorIterator;
std::vector<std::string> formulaTokens;
std::vector<std::string> pnTokens;
std::stack<std::string> stringStack;
std::string currentNumString = "";
std::string currentTokenString = "";
std::stack<TreeNode> nodeStack;
TreeNode *currentNode;
std::string currToken;
bool treeIsDone;
//tokenization
for (stringIterator = formulaStr.begin(); stringIterator != formulaStr.end(); stringIterator++)
{
std::string currCharString(1, *stringIterator);
currentTokenString.push_back(*stringIterator);
if ((precedence.find(currentTokenString) != precedence.end()) || (currentTokenString == "(") || (currentTokenString == ")"))
{
//if the current token string is found in the precedence list (or is a parentheses)
if (currentNumString != "")
{
formulaTokens.push_back(currentNumString);
currentNumString = "";
}
formulaTokens.push_back(currentTokenString);
currentTokenString = "";
}
else if (std::all_of(currentTokenString.begin(), currentTokenString.end(), ::isdigit))
{
//if the current token string is all digits
currentNumString.append(currentTokenString);
currentTokenString = "";
}
else if (currentTokenString == "t")
{
//if the current token string is the t variable
formulaTokens.push_back(currentTokenString);
currentTokenString = "";
}
}
//convert to polish notation
std::reverse(formulaTokens.begin(), formulaTokens.end());
stringStack.push(")");
for (stringVectorIterator = formulaTokens.begin(); stringVectorIterator != formulaTokens.end(); stringVectorIterator++)
{
currToken = *stringVectorIterator;
if ((precedence.find(currToken) == precedence.end()) && (currToken != "(") && (currToken != ")"))
{
pnTokens.push_back(currToken);
}
else if (currToken == ")")
{
stringStack.push(currToken);
}
else if (precedence.find(currToken) != precedence.end())
{
if (stringStack.size() > 0)
{
if (stringStack.top() != ")")
{
while (precedence[stringStack.top()] <= precedence[currToken])
{
if (stringStack.top() != ")")
{
pnTokens.push_back(stringStack.top());
stringStack.pop();
}
if (stringStack.size() <= 0)
{
break;
}
if (stringStack.top() == ")")
{
break;
}
}
}
}
stringStack.push(currToken);
}
else if (currToken == "(")
{
if (stringStack.size() > 0)
{
while (stringStack.top() != ")")
{
pnTokens.push_back(stringStack.top());
stringStack.pop();
if (stringStack.size() <= 0)
{
break;
}
}
stringStack.pop();
}
}
}
while (stringStack.size() > 0)
{
if (stringStack.top() != ")")
{
pnTokens.push_back(stringStack.top());
}
stringStack.pop();
}
std::reverse(pnTokens.begin(), pnTokens.end());
//if it's gotten this far, the formula was valid
//destroy the current tree to make room
destroyTree();
//parse polish notation into tree
treeRoot = new TreeNode;
currentNode = treeRoot;
treeIsDone = false;
for (stringVectorIterator = pnTokens.begin(); stringVectorIterator != pnTokens.end(); stringVectorIterator++)
{
currToken = *stringVectorIterator;
if (precedence.find(currToken) != precedence.end())
{
//if the token is an operator
currentNode->setOp(currToken);
currentNode = insertNode(currentNode);
}
else
{
//if it's a number or a variable
if (currentNode->first != NULL)
{
//if the current node has it's first branch initialized
while (currentNode->last != NULL)
{
//while the last branch is initialized
currentNode = currentNode->parent;
}
currentNode = insertNode(currentNode);
}
if (std::all_of(currToken.begin(), currToken.end(), ::isdigit))
{
//if it's a number
currentNode->setVal((uint32_t)atoi(currToken.c_str()));
}
else
{
//if it's something else, a variable
currentNode->setVar();
}
if (currentNode != treeRoot)
{
currentNode = currentNode->parent;
}
//since we just moved up, we know at least the first branch is used
//so only check the last
while (currentNode->last != NULL)
{
//if the last node is not free
if (currentNode == treeRoot)
{
//if we're at the root, and it's totally populated
treeIsDone = true;
break;
}
currentNode = currentNode->parent;
}
if (!treeIsDone)
{
currentNode = insertNode(currentNode);
}
}
}
return(true);
}
uint8_t ExpressionTree::evaluate(uint32_t tVal)
{
return((uint8_t)treeRoot->evaluate(tVal));
}
Related
I am getting a runtime error for this test case using stack
"bxj##tw", "bxj###tw"
Line 171: Char 16: runtime error: reference binding to misaligned address 0xbebebebebebec0ba for type 'int', which requires 4 byte alignment (stl_deque.h)
0xbebebebebebec0ba: note: pointer points here
<memory cannot be printed>
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/stl_deque.h:180:16
class Solution {
public:
bool backspaceCompare(string s, string t) {
stack<int>st1;
stack<int>st2;
for(int i=0; i<s.size(); i++){
if(st1.empty() && s[i]!='#'){
st1.push(s[i]);
}
else{
if(!st1.empty() && s[i]=='#'){
st1.pop();
}
else if(s[i]=='#' && (st1.empty())){
continue;
}
else{
st1.push(s[i]);
}
}
}
for(int i=0; i < t.size(); i++){
if(st2.empty() && t[i]!='#'){
st2.push(t[i]);
}
else{
if(!st2.empty() && t[i]=='#'){
st2.pop();
}
else if(t[i]=='#' && st2.empty()){
continue;
}
else{
st2.push(t[i]);
}
}
}
if(st1.empty() && st2.empty()){
return "";
}
while(!st1.empty()){
if(st1.top()!= st2.top()){
return false;
}
else{
st1.pop();
st2.pop();
}
}
return true;
}
};
I wouldn't use a stack<char> since the natural representation is a string, and you're not using any functionality of the stack's ability to expand or shrink in the front (other than the end where you can just say return a == b). string has push_back and pop_back methods as well.
For small input (like the ones guaranteed for this challenge problem), I'd recommend constructing the two "editors" and comparing with ==:
class Solution {
public:
bool backspaceCompare(string s, string t) {
return backspace(s) == backspace(t);
}
private:
string backspace(const string &s) {
string editor = "";
string::const_iterator commandItr = s.cbegin();
while(commandItr != s.cend())
if(*commandItr == '#' && !editor.empty()) {
editor.pop_back();
++commandItr;
} else if(*commandItr != '#')
editor.push_back(*commandItr++);
else
++commandItr;
return editor;
}
};
However, they did challenge the coder to use O(1) memory. Here is an example of that:
class Solution {
public:
bool backspaceCompare(string s, string t) {
int left = s.size() - 1;
int right = t.size() - 1;
while(true) {
left = backspace(s, left);
right = backspace(t, right);
if (left == -1 && right == -1)
return true;
if (left == -1 && right != -1 || right == -1 && left != -1)
return false;
if(s[left--] != t[right--])
return false;
}
}
private:
// Returns first index from back that indexes to a non-deleted character
int backspace(string const &s, int startingIndex) {
if(startingIndex == -1)
return -1;
if(s[startingIndex] != '#')
return startingIndex;
unsigned backspaceCount = 0;
while(true) {
while(startingIndex != -1 && s[startingIndex] == '#') {
++backspaceCount;
--startingIndex;
}
while (startingIndex != -1 && backspaceCount && s[startingIndex] != '#') {
--startingIndex;
--backspaceCount;
}
if (startingIndex == -1)
return -1;
else if(s[startingIndex] != '#' && !backspaceCount)
return startingIndex;
}
}
};
I am just kind of starting to code and this was in a book that I am using to learn C++. It should be working straight from the book and I can't figure out how to fix it.
I think the problem might be because it lacks the constant operator but if you add it in doesn't that prevent you from modifying the value?
The book is by Drozdek titles Data Structures and Algorithms in C++ if you need that. Thanks for the help!
#include <iostream>
#include <cctype>
#include <cstdlib>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;
class Variable {
public:
char id;
int exp;
Variable() { // required by <vector>;
}
Variable(char c, int i) {
id = c; exp = i;
}
bool operator== (const Variable& v) const {
return id == v.id && exp == v.exp;
}
bool operator< (const Variable& v) const { // used by sort();
return id < v.id;
}
};
class Term {
public:
Term() {
coeff = 0;
}
int coeff;
vector<Variable> vars;
bool operator== (const Term&) const;
bool operator!= (const Term& term) const { // required by <list>
return !(*this == term);
}
bool operator< (const Term&) const;
bool operator> (const Term& term) const { // required by <list>
return *this != term && (*this < term);
}
int min(int n, int m) const {
return (n < m) ? n : m;
}
};
class Polynomial {
public:
Polynomial() {
}
Polynomial operator+ (Polynomial&);
void error(char *s) {
cerr << s << endl; exit(1);
}
private:
list<Term> terms;
friend istream& operator>> (istream& in, Polynomial& polyn) {
char ch, sign, coeffUsed, id;
int exp;
Term term;
in >> ch;
while (true) {
coeffUsed = 0;
if (!isalnum(ch) && ch != ';' && ch != '-' && ch != '+')
polyn.error("Wrong character entered2");
sign = 1;
while (ch == '-' || ch == '+') { // first get sign(s) of Term
if (ch == '-')
sign *= -1;
ch = in.get();
if (isspace(ch))
in >> ch;
}
if (isdigit(ch)) { // and then its coefficient;
in.putback(ch);
in >> term.coeff;
ch = in.get();
term.coeff *= sign;
coeffUsed = 1;
}
else term.coeff = sign;
int i;
for (int i = 0; isalnum(ch); i++) { // process this term:
id = ch; // get a variable name
ch = in.get();
if (isdigit(ch)) { // and an exponent (if any);
in.putback(ch);
in >> exp >> ch;
}
else exp = 1;
term.vars.push_back(Variable(id,exp));
}
polyn.terms.push_back(term); // and include it in the linked list;
term.vars.resize(0);
if (isspace(ch))
in >> ch;
if (ch == ';') // finish if a semicolon is entered;
if (coeffUsed || i > 0)
break;
else polyn.error("Term is missing"); // e.g., 2x - ; or just ';'
else if (ch != '-' && ch != '+') // e.g., 2x 4y;
polyn.error("wrong character entered");
}
for (list<Term>::iterator it = polyn.terms.begin(); it != polyn.terms.end(); it++)
if (it->vars.size() > 1)
sort(it->vars.begin(),it->vars.end());
return in;
}
friend ostream& operator<< (ostream& out, const Polynomial& polyn) {
int afterFirstTerm = 0, i;
for (list<Term>::const_iterator pol = polyn.terms.begin(); pol != polyn.terms.end(); pol++) {
out.put(' ');
if (pol->coeff < 0) // put '-' before polynomial
out.put('-'); // and between terms (if needed);
else if (afterFirstTerm) // don't put '+' in front of
out.put('+'); // polynomial;
afterFirstTerm++;
if (abs(pol->coeff) != 1) // print a coefficient
out << ' ' << abs(pol->coeff);// if it is not 1 nor -1, or
else if (pol->vars.size() == 0) // the term has only a coefficient
out << " 1";
else out.put(' ');
for (i = 1; i <= pol->vars.size(); i++) {
out << pol->vars[i-1].id; // print a variable name
if (pol->vars[i-1].exp != 1) // and an exponent, only
out << pol->vars[i-1].exp; // if it is not 1;
}
}
out << endl;
return out;
}
};
// two terms are equal if all varibles are the same and
// corresponding variables are raised to the same powers;
// the first cell of the node containing a term is excluded
// from comparison, since it stores coefficient of the term;
bool Term::operator== (const Term& term) const {
int i;
for (i = 0; i < min(vars.size(),term.vars.size()) &&
vars[i] == term.vars[i]; i++);
return i == vars.size() && vars.size() == term.vars.size();
}
bool Term::operator< (const Term& term2) const { // used by sort();
if (vars.size() == 0)
return false; // *this is just a coefficient;
else if (term2.vars.size() == 0)
return true; // term2 is just a coefficient;
for (int i = 0; i < min(vars.size(),term2.vars.size()); i++)
if (vars[i].id < term2.vars[i].id)
return true; // *this precedes term2;
else if (term2.vars[i].id < vars[i].id)
return false; // term2 precedes *this;
else if (vars[i].exp < term2.vars[i].exp)
return true; // *this precedes term2;
else if (term2.vars[i].exp < vars[i].exp)
return false; // term2 precedes *this;
return ((int)vars.size() - (int)term2.vars.size() < 0) ? true : false;
}
Polynomial Polynomial::operator+ (Polynomial& polyn2) {
Polynomial result;
list<Term>::iterator p1, p2;
bool erased;
for (p1 = terms.begin(); p1 != terms.end(); p1++) // create a new polyn
result.terms.push_back(*p1); // from copies of *this
for (p1 = polyn2.terms.begin(); p1 != polyn2.terms.end(); p1++) // and
result.terms.push_back(*p1); // polyn2;
for (p1 = result.terms.begin(); p1 != result.terms.end(); ) {
for (p2 = p1, p2++, erased = false; p2 != result.terms.end(); p2++)
if (*p1 == *p2) { // if two terms are equal (except
p1->coeff += p2->coeff; // for the coefficient), add the
result.terms.erase(p2); // two coefficients and erase
if (p1->coeff == 0) // a redundant term; if the
result.terms.erase(p1);// coefficient in retained term
erased = true; // is zero, erase the term as well;
break;
}
if (erased) // restart processing from the beginning
p1 = result.terms.begin(); // if any node was erased;
else p1++;
}
result.terms.sort();
return result;
}
int main() {
Polynomial polyn1, polyn2;
cout << "Enter two polynomials, each ended with a semicolon:\n";
cin >> polyn1 >> polyn2;
cout << "The result is:\n" << polyn1 + polyn2;
return 0;
}
On line 52, the error function should take a const char*, rather than a char*:
void error(char* s) { // WRONG
void error(const char *s) { // RIGHT
cerr << s << endl; exit(1);
}
This is because strings like "Hello" are arrays of const char, because you can't modify literals. If you make this change, the code should work. The compiler won't convert pointers to const types to regular pointers, because that would break the constness.
Also, on line 82 ad 83, the textbook writes:
int i; // Error: i never initialized
for (int i = 0; isalnum(ch); i++) { // process this term:
It looks like it was trying to use i both inside and outside the for loop, but the author accidentally declared i a second time at the start of the loop. We can fix it by doing this:
int i = 0; // This is probably what was intended
for(; isalnum(ch); i++) { // process this term:
Why can't I modify string literals?
Imagine if you could do
5 = 10; // This is complete and utter nonsense.
This doesn't make any sense! You can't assign 5 to 10. In the same way, this is also nonsense:
"hello" = "blarg"; // This is also nonsense
"hello" is always "hello", and never anything else. If the compiler allowed you to write
"hello"[0] = 'H'; // This is also nonsense
This would be modifying "hello", which could just... break your program. It's wrong; it's evil. In fact, the string literal "hello" might even be placed in a section of memory that's flagged as const by the Operating System itself.
Why does the compiler give you the error (or warning)
If you have char*, that is a pointer to a char. If you have const char*, that is a pointer to a const char. If you could go from const char* to char*, this would allow you to modify const memory, which could break the program:
// If you could do this, you could modify "hello"
// Modifying "hello" is nonsense, so this should be nonsense too:
char* s = "hello";
s[0] = 'H'; // How you'd modify "hello"
As a result, string literals can only be assigned to const char*:
// Because s contains const chars, elements of s can't be modified so this is fine
const char* s = "hello"; // This is OK
Why did the textbook contain the error?
The language used to let people do really unsafe stuff, like modifying string literals. This is extremely bad practice and it breaks optimizations the compiler uses to make programs smaller and faster.
The textbook is probably written by someone who's used to writing old, unsafe code.
I showed my codes and i have an error reading character of string. Everything is okey in my debugger until the NULL.
*Warning C4715 `String::equas`: not all control paths return a value*
I can not use it because I am using a NULL pointer for the parameter.
How can I solve this problem?
Thanks a lot and have a nice day!
Header:
class String
{
public:
String();
bool empty();
String(char * other_str);
bool equals(const String other_str);
private:
char* m_str;
};
My Codes:
#include "String.hpp"
#include <string>
#include<iostream>
int my_len(const char* p) {
int c = 0;
while (*p != '\0')
{
c++;
*p++;
}
return c;
}
String::String()
:m_str(NULL)
{
}
String::String(char * other_str)
{
}
bool String::empty()
{
return true;
}
bool String::equals(const String other_str)
{
if (m_str == NULL && other_str.m_str == NULL) {
return true;
}
if (m_str == NULL && other_str.m_str != NULL) {
return false;
}
if (m_str != NULL && other_str.m_str == NULL) {
return false;
}
if (m_str != NULL && other_str.m_str != NULL) {
int mystrlen = my_len(m_str);
int myrhslen = my_len(other_str.m_str);
if (mystrlen != myrhslen)
{
return false;
}
else
{
for (int i = 0; i < mystrlen; i++)
{
if (m_str[i] != other_str.m_str[i])
{
return false;
}
else {
return true;
}
}
}
}
}
i will add this codes:
int mylen(const char* p) {
int c = 0;
while (*p != '\0')
{
c++;
*p++;
}
return c;
}
void my_cpy(char dest, const char* src) {
int i = 0;
while (src[i] != '\0') {
dest[i] = src[i];
i++;
}
dest[i] = '\0';
}
char mytrdup(const char *s) {
char* d = (char*)malloc((my_len(s) + 1) * sizeof(char));
if (d == NULL) {
return NULL;
}
else{
my_cpy(d, s);
}
return d;
}
String empty_string2(NULL);
This will invokde the constructor version :
String::String(char* other_str) {}
which does nothing, leaving the m_str pointer dangling/uninitialized. You should change this constructor somehow, either by copying the string and setting the m_str pointer accordingly, or by setting m_str to the same address as the parameter. Either case it depends on what you want to achieve.
Besides, many other problems exist in your code. I notice already this one in you implemented function my_len. you should change *p++ into p++. I wonder how this passed the compilation btw, since the parameter is a const char*.
Finally, the compiler's warning is correct and very clear, although it is not the source of the problem you are facing for the time being.
EDIT: to make a duplicate copy of the string, you can write you constructor like this:
String::String(const char* other_str)
{ m_str = (other_str ? strdup(other_str) : other_str); }
And besides, preferably use null_ptr instead of NULL in your code. Since C++11, this is the standard for null pointers.
I add TEST_TRUE and then that work correctly.
My assignment is to make a binary expression tree to convert postfix expressions to infix expressions in C++. I originally coded all my work in my Xcode IDE and would periodically ssh to linprog4 in terminal to make sure it works there because it has to before I turn it in. I don't understand the errors that are popping up when I compile it using g++ -o proj4.x -std=c++11 proj4_driver.cpp BET.cpp
This assignment was due 2 weeks ago and unfortunately when I was turning in the tar file with all my files, I forgot to include the .tar extension. I don't think that would affect my files but now they aren't compiling and I don't understand the errors I am getting. Could someone skim through my code and see if I am missing anything? I've looked over my code and it doesn't look like I accidentally typed something randomly somewhere.
Here is a screenshot of the errors I am getting
BET.h
#ifndef BET_H
#define BET_H
using namespace std;
class BET {
private:
struct BinaryNode {
string data;
BinaryNode * parent;
BinaryNode * childLeft;
BinaryNode * childRight;
bool visited; // to tell if node has been visited to or not
// Construct a blank copy version BinaryNode when an
// object of BET is created
BinaryNode( const char & d = char{}, BinaryNode * p = NULL,
BinaryNode * l = NULL,
BinaryNode * r = NULL, bool v = false )
: data { d },
parent { p },
childLeft { l },
childRight { r },
visited { v } {}
// Construct a blank move version of BinaryNode when an
// an object of BET is created
BinaryNode( char && d, BinaryNode * p = NULL, BinaryNode * l = NULL,
BinaryNode * r = NULL, bool v = false )
: data { std::move(d) },
parent { p },
childLeft { l },
childRight { r },
visited { v } {}
}; // end of BinaryNode struct
public:
// constructors and destructor
BET();
BET( const string postfix );
BET( const BET & rhs );
~BET();
// help copy constructor
bool buildFromPostfix( const string postfix );
// copy assignment operator
const BET & operator=( const BET & rhs );
void printInfixExpression();
void printPostfixExpression();
size_t size();
size_t leaf_nodes();
bool empty();
bool isOperand(BinaryNode * n);
private:
BinaryNode *root;
size_t leaves, nodes;
bool useP;
void printInfixExpression( BinaryNode * n );
void makeEmpty( BinaryNode* & t );
BinaryNode * clone( BinaryNode * t ) const;
void printPostfixExpression( BinaryNode * n );
size_t size( BinaryNode * t );
size_t leaf_nodes( BinaryNode * t );
}; // end of BET class
#endif
BET.cpp
#include <iostream>
#include <stack>
#include <sstream>
#include <string>
#include "BET.h"
//using namespace std;
// default zero-param constructor
BET::BET() {
root = new BinaryNode;
leaves = 0;
nodes = 0;
}
// one-param constructor, where parameter "postfix" is a
// string containing a postfix expression. The tree should
// be built based on the postfix expression. Tokens in the
// postfix expression are separated by space
BET::BET(const string postfix) {
root = new BinaryNode;
buildFromPostfix(postfix);
}
// copy constructor
BET::BET(const BET & rhs) {
leaves = rhs.leaves;
nodes = rhs.nodes;
root = rhs.root;
}
// destructor
BET::~BET() {
makeEmpty(root);
leaves = nodes = 0;
}
bool BET::buildFromPostfix(const string postfix) {
// Create stack to hold variables
stack<BinaryNode *> s;
stack<BinaryNode> bet;
char token;
string temp;
int index = 1;
bool doubleDigit = false;
int opCount = 0, digitCount = 0;
//stringstream hexToInt;
// iterator through postfix
for (int i = 0; i < postfix.size(); ++i) {
// grab token at iterations index
token = postfix[i];
if ( (token > '0' && token < '9') || (token > 62 && token < 80)) {
// check to see if token is an operand
// create a dynamic object of BinaryNode
BinaryNode *operand = new BinaryNode;
// check to see if next index of postfix is digit
// if its not, then we know its a double digit
// this while loop should only continue as long as the
// next index is between 0 and 9
temp = postfix[i];
while (postfix[i + index] >= '0' && postfix[i + index] <= '9') {
temp += postfix[i + index];
index++;
doubleDigit = true;
}
if (doubleDigit == true) {
i += index;
doubleDigit = false;
index = 1;
operand->data = temp;
} else {
operand->data = postfix[i];
}
s.push(operand);
digitCount++;
} else if (token == '+' || token == '-' || token == '*' || token == '/'){
// check to see if token is operator
BinaryNode *operand = new BinaryNode;
operand->data = postfix[i];
operand->childLeft = s.top();
s.top()->parent = operand;
s.pop();
operand->childRight = s.top();
s.top()->parent = operand;
s.pop();
s.push(operand);
opCount++;
} else {
// if neither, must be space or other character
if (token == ' ') {
} else
return false;
}
}
if (digitCount <= opCount) {
return false;
}
root = s.top();
nodes = size();
//leaves = leaf_nodes();
// THINGS TO DO:
// Make error cases with if statements to return false at some point
return true;
}
// assignment operator
const BET & BET::operator=(const BET & rhs) {
root = clone(rhs.root);
return *this;
}
// public version of printInfixExpression()
// calls the private version of the printInfixExpression fuction
// to print out the infix expression
void BET::printInfixExpression() {
printInfixExpression(root);
}
// public version of printPostfixExpression()
// calls the private version of the printPostfixExpression function
// to print out the postfix expression
void BET::printPostfixExpression() {
printPostfixExpression(root);
}
// public version of size()
// calls the private version of the size function to return
// the number of nodes in the tree
size_t BET::size() {
return size(root);
}
// public version of leaf_nodes()
// calls the private version of leaf_nodes function to return
// the number of leaf nodes in the tree
size_t BET::leaf_nodes() {
return leaf_nodes(root);
}
// public version of empty()
// return true if the tree is empty. return false otherwise
bool BET::empty() {
if (nodes == 0) {
return true;
} else
return false;
}
// checks whether node is operand or not
bool BET::isOperand(BinaryNode * n) {
if (n->data != "+" && n->data != "-" && n->data != "*" && n->data != "/") {
return true;
} else
return false;
}
// private version of printInfixExpression
// print to the standard output the corresponding infix expression
void BET::printInfixExpression(BinaryNode * n) {
//BinaryNode * temp = NULL;
if (n != NULL ) {
printInfixExpression(n->childRight);
cout << n->data << " ";
printInfixExpression(n->childLeft);
}
/*
if (n != NULL && useP == true) {
printInfixExpression(n->childRight);
if (isOperand(n->parent) && n->parent != NULL && !n->childLeft) {
cout << "(";
}
cout << n->data << " ";
if (isOperand(n->parent) && n->parent != NULL && !n->childRight) {
cout << ")";
}
printInfixExpression(n->childLeft);
}
*/
}
// private method makeEmpty()
// delete all nodes in the subtree pointed to by n.
// Called by functions such as the destructor
void BET::makeEmpty(BinaryNode * & n) {
if (n != NULL) {
makeEmpty(n->childLeft);
makeEmpty(n->childRight);
delete n;
}
}
// private method clone()
// clone all nodes in the subtree pointed by n. Called by
// functions such as the assignment operator=
BET::BinaryNode * BET::clone(BinaryNode * n) const {
if (n != NULL) {
root->childRight = clone(n->childRight);
root->childLeft = clone(n->childLeft);
root->data = n->data;
}
return root;
}
// private method printPostfixExpression()
// print to the standard output the corresponding postfix expression
void BET::printPostfixExpression(BinaryNode * n) {
if (n != NULL) {
printPostfixExpression(n->childRight);
printPostfixExpression(n->childLeft);
cout << n->data << " ";
}
}
// private version of size()
// return the number of nodes in the subtree pointed by n
size_t BET::size(BinaryNode * n) {
if (n != NULL) {
size(n->childLeft);
size(n->childRight);
nodes++;
}
return nodes;
}
// return the number of leaf nodes in the subtree pointed by n
size_t BET::leaf_nodes(BinaryNode * n) {
if (n != NULL) {
leaf_nodes(n->childLeft);
leaf_nodes(n->childRight);
if (n->childLeft == NULL && n->childRight == NULL) {
leaves += 1;
}
}
return leaves;
}
Driver.cpp
#include "BET.cpp"
//using namespace std;
int main() {
string postfix;
// get a postfix expression
cout << "Enter the first postfix expression: ";
getline(cin, postfix);
// create a binary expression tree
BET bet1(postfix);
if (!bet1.empty()) {
cout << "Infix expression: ";
bet1.printInfixExpression();
cout << "\n";
cout << "Postfix expression: ";
bet1.printPostfixExpression();
cout << "\nNumber of nodes: ";
cout << bet1.size() << endl;
cout << "Number of leaf nodes: ";
cout << bet1.leaf_nodes() << endl;
// test copy constructor
BET bet2(bet1);
cout << "Testing copy constructor: ";
//bet2.printInfixExpression();
// test assignment operator
BET bet3;
bet3 = bet1;
cout << "Testing assignment operator: ";
//bet3.printInfixExpression();
}
cout << "Enter a postfix expression (or \"quit\" to quit): ";
while (getline(cin, postfix)) {
if (postfix == "quit") {
break;
}
if (bet1.buildFromPostfix(postfix)) {
cout << "Infix expression: ";
bet1.printInfixExpression();
cout << "Postfix expression: ";
bet1.printPostfixExpression();
cout << "Number of nodes: ";
cout << bet1.size() << endl;
cout << "Number of leaf nodes: ";
cout << bet1.leaf_nodes() << endl;
}
cout << "Enter a postfix expression (or \"quit\" to quit): ";
}
return 0;
}
Driver.cpp #include "BET.cpp" should be #include "BET.h"
Or (and this is just for completeness, not recommended), leave the include of the .cpp, but then only try to compile the one .cpp (as in g++ Driver.cpp) - since driver will include BET and so all your code is there and will build.
I am trying to call a function for my stack class. If I have all of the functions within the main file the project works, however, when called from the class it says the the Error: identifier "function name" is undefined. I think it is a simple syntax error, but i can't find it.
main.cpp
#include<iostream>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "stack.h"
#define MAX 10
#define EMPTY -1
struct stack
{
char data[MAX];
int top;
};
int mystack::isempty(struct stack *s)
{
return (s->top == EMPTY) ? 1 : 0;
}
void mystack::emptystack(struct stack* s)
{
s->top=EMPTY;
}
void mystack::push(struct stack* s,int item)
{
if(s->top == (MAX-1))
{
printf("\nSTACK FULL");
}
else
{
++s->top;
s->data[s->top]=item;
}
}
char mystack::pop(struct stack* s)
{
char ret=(char)EMPTY;
if(!isempty(s))
{
ret= s->data[s->top];
--s->top;
}
return ret;
}
void mystack::display(struct stack s)
{
while(s.top != EMPTY)
{
printf("\n%d",s.data[s.top]);
s.top--;
}
}
int isoperator(char e)
{
if(e == '+' || e == '-' || e == '*' || e == '/' || e == '%' || e == '^')
return 1;
else
return 0;
}
int priority(char e)
{
int pri = 0;
if(e =='%' || e == '^')
pri = 3;
else
{
if (e == '*' || e == '/' || e =='%')
pri = 2;
else
{
if(e == '+' || e == '-')
pri = 1;
}
}
return pri;
}
void infix2postfix(char* infix, char * postfix, int insertspace)
{
char *i,*p;
struct stack X;
char n1;
emptystack(&X); // any time a class like this is called it says Error: identifier "emptystack"
// is undefined
i = &infix[0];
p = &postfix[0];
while(*i)
{
while(*i == ' ' || *i == '\t')
{
i++;
}
if( isdigit(*i) || isalpha(*i) )
{
while( isdigit(*i) || isalpha(*i))
{
*p = *i;
p++;
i++;
}
if(insertspace)
{
*p = ' ';
p++;
}
}
if( *i == '(' )
{
push(&X,*i);
i++;
}
if( *i == ')')
{
n1 = pop(&X);
while( n1 != '(' )
{
*p = n1;
p++;
if(insertspace)
{
*p = ' ';
p++;
}
n1 = pop(&X);
}
i++;
}
if( isoperator(*i) )
{
if(mystack::isempty(&X))
push(&X,*i);
else
{
n1 = pop(&X);
while(priority(n1) >= priority(*i))
{
*p = n1;
p++;
if(insertspace)
{
*p = ' ';
p++;
}
n1 = pop(&X);
}
push(&X,n1);
push(&X,*i);
}
i++;
}
}
while(!isempty(&X))
{
n1 = pop(&X);
*p = n1;
p++;
if(insertspace)
{
*p = ' ';
p++;
}
}
*p = '\0';
}
int main()
{
char in[50],post[50];
strcpy(&post[0],"");
printf("Enter Infix Expression : ");
fflush(stdin);
gets(in);
infix2postfix(&in[0],&post[0],1);
printf("Postfix Expression is : %s\n",&post[0]);
return 0;
}
stack.h
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
using namespace std;
class mystack
{
public:
int isempty(struct stack *s);
void emptystack(struct stack* s);
void push(struct stack* s,int item);
char pop(struct stack* s);
void display(struct stack s);
};
I am using visual studio if that helps.
EDIT: added comment for clarity.
Thanks,
Ryan
At a cursory glance, this function:
void emptystack(struct stack* s)
{
s->top=EMPTY;
}
Is missing the scope operator (::), so you probably intended to write it as:
void mystack::emptystack(struct stack* s)
{
s->top=EMPTY;
}
I'm not sure if that's your problem though, since "I'm trying to call a function" is a bit vague. You might want to narrow down precisely where the error is occurring, then edit your question with additional information.
Edit: In looking at your implementation a bit more, I'm not sure why you created the mystack class at all. It looks like you just want to define a bunch of functions that operate on your stack struct, which doesn't require a class definition. If you want to do it this way for some unusual reason, then you'll have to instantiate a mystack object before you can call its member functions. Something of the nature:
mystack * myStackObj = new mystack();
myStackObj->emptystack(&X);
Though I'm not sure why you would want to do this. The other alternative is to roll your stack struct into the class instead, either by making the whole struct a member of the class or by simply adding data and top to the class. Then if you instantiated a mystack object it would have the data of the stack and could call methods on its own data. I'd also suggest looking at a tutorial/documentation/book related to C++ classes and their usage. Here's one, but there are undoubtedly plenty of others.