StringStream >> char not working?

We recently covered function pointers and I had an epiphany - that an array of them can be used as an operator stack. This program takes in a string containing a basic math program (ex. 5.3 + 3.0 - 2.1 * 10.053 / 2.5 (ignoring order of operations)) and puts the numbers and operators into separate vectors. I am trying to use a stringstream (another new thing we just covered) to get them into their proper vectors, but it seems to be ignoring the special characters?

ss >> val works fine... ss >> op does nothing. I have also tried (op = ss.get()) which works fine by itself, but stops working when adding "ss >> val || ".

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void addToStack(vector<double(*)(double, double)> *s, vector<double> *os, string str) {
	string t = "";
	stringstream ss(str);
	double val = 0.0;
	char op = ' ';
	ss << str;
	
	//                  \/\/\/\/
	while (ss >> val || ss >> op) { // <-- This is the problem
		cout << val << " : " << op << endl; //<-- debugging
		cout << ss.str() << endl;           //<-- debugging
		cin.get();                          //<-- debugging
		if (val > 0)
			os->push_back(val);
		else {
			switch (op) {
				case '+':
					s->push_back(add);
					break;
				case '-':
					s->push_back(subtract);
					break;
				case '*':
					s->push_back(multiply);
					break;
				case '/':
					s->push_back(divide);
					break;
			}
		}
		
		val = 0.0;
		op = ' ';
	}
}
Last edited on
Okay... well... immediately after posting this, I sat back and looked at my code in VS and immediately figured it out......... I must have spend two hours staring at the screen last night. I have a vague idea of why this happened, ss >> val executes trying to store the special character, but fails. When ss >> op then executes it is set to the ascii value of the following number. The answer I came across was this:
1
2
3
4
string t = "";
while (ss >> t) { 
		if (isdigit(t[0])) val = double(atof(t.c_str()));
		else op = t[0];


This doesn't seem like the most efficient way to do it though...
ss >> val works fine... ss >> op does nothing.

No it is doing something, but that something is probably not what you want. When you're trying to receive that single character you will retrieve the whitespace character that is preceding the operator. Then the next time you try to extract the value the extraction will fail because the stream contains the operator, not a number and the extraction operator knows you can't insert a non-digit character into a numeric variable.

You will need to skip the whitespace, if present, to get to your actual operator. Try something like:

ss >> val >> skipws >> op >> noskipws
Don't forget to reset the stream to the default mode of operation using the noskipws because these flags are "sticky" meaning that they stay in effect until you change them.

By the way there is really no need for the || in your if() statement because you can just combine the extraction operations to achieve the same results.

This doesn't seem like the most efficient way to do it though...

You're correct it isn't the most efficient way, nor is it a recommend way for a couple of reasons. First since atof() can silently fail using this C function is not the best way to convert a string to a number, use one of the stoX() series of functions instead. Second you don't need the horrible C style cast since the return value of atof() is of a type double. If you must cast you should be using the safer C++ style casts instead of the C style casts.

http://en.cppreference.com/w/cpp/string/byte/atof
http://en.cppreference.com/w/cpp/language/explicit_cast


Something like this perhaps:
1
2
3
4
5
6
7
char next ;
while( ss >> next ) { // extract the next char

    ss.unget() ; // put it back
    if( std::isdigit(next) ) { ss >> val ; /* ... */ }
    else { ss >> op ; /* ... */ }
}


This check is broken: if (val > 0), what if the user entered, say 3.4 + 0.0 - -7.8

Pass these vectors by reference:
1
2
// void addToStack(vector<double(*)(double, double)> *s, vector<double> *os, string str) {
void addToStack( vector<double(*)(double, double)>& s, vector<double>& os, string str) {

and, ideally give them names that are semantically richer than s and os : say, operations and values
Topic archived. No new replies allowed.