C++ Questions

Pages: 12345... 13
Also note that you can't enter a space as part of a string when using >>. The extraction stops when a white-space (the space) char is found. If you need to treat a space as part of a string then you need to use getline().
I was afraid you would reiterate that. That is why I mentioned that I created that in the morning and before your previous post. And it looks like it did not work exactly how I expected it with null termination which I though must be at the end. Instead there is strict removal from the stream/buffer and \n detecting, all depending on which object is doing the work. But anyways, that is where I was coming from.

So basically a stream cannot be called a string or an array, although it can take in those types and others, and the streams don't necessarily require a null terminator at the end. It is best to call it a "sequence of characters" and following a cin>> it adds a \n newline at the end. Items are added on one end and removed from the other like a queue.

cout: is an object of ostream (iostream is derived class of i & ostream)
cin: is a global object of istream
<<: is the insertion operator
>>: is the extraction operator
endl: is a manipulator

I have tried putting in trailing spaces and tabs, and it is as you all have said, that the cin>> ignores white spaces. I also tried typing for ex: "bbb" on a cin>> and sure enough on the next getline() it extracts "bb" and extracts the \n at the end as well, which were all leftovers from the cin>>.

I have tried "bbb" on a looped cin>> and it acts as if the user hit 3x b's independent and prints them out.

I was aware that cin>> is not useful for multi-word strings and stops when encountering the first space and that it is best to use getline() in such circumstances.

Good arsenal of info and insight here, as always I am grateful.
Something to consider....

C++: Console User Input Done Right

http://lb-stuff.com/user-input

Yeah, this all looks complicated, but it is pro-active defensive programming. You should presume the user is an idiot who will make data entries that won't match the type you are expecting.

Especially if you don't prompt them what the type is they should enter. E.g. string, int, double, etc. You should provide a "way out" for incorrect data entered.

You could expand the linked examples so possible multiple input is extracted with getline() and tokenized into separate entries to be processed in turn or discarded as needed.
Thanks!
As another take on numeric input, consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <string>
#include <sstream>
#include <string_view>

template<typename T = int>
T getNum(std::string_view prm) {
	for (std::string input; std::cout << prm; ) {
		if (T response {}; std::getline(std::cin, input))
			if (std::istringstream is { input }; (is >> response) && !(is >> input))
				return response;

		std::cin.clear();
		std::cerr << "Not a number!\n";
	}
}

int main() {
	const auto no { getNum<double>("Enter a number: ") };

	std::cout << "Entered number is " << no << '\n';
}

For an example of very pro-active defensive user input programming using exceptions one needs to look at this:

https://github.com/Apress/beginning-cpp20/tree/main/Exercises/NoModules/Chapter%2016/Soln16_05

That really is using a tactical nuke to swat a gnat.
Clever seeplus, did you dream this up based on things you have seen in the past?

At first, it looked like a hodgepodge, but then I spent some time and analyzed it.
I have not seen "string_view" before, but I looked it up and it looks like it is a more efficient way of passing a string literal to a function that does not lead to re-allocation of the string.

Also, on your conditional check on the for loop, the "std::cout << prm" prints out and returns a reference to the stream (if all is good system wise, which it most likely will be). As a result it is always true and an infinite loop is created until we return from the function.

Besides the clever syntax, the next clever line "(is >> response) && !(is >> input)" means it is acceptable if an int or double can be streamed to the int/double "response" and NOT to string. If any part of the user input has a char/special character/string, then "(is >> input)" falls true and the if statement does not return the "response"...clever stuff indeed!

So, now on to the "CLEVER" part. I am sure for you guys that snippet is a walk in the park, but I had to take some time on it. When you are viewing code, do you prefer to see clever code or simplified code and is seen in the wild? I rewrote your code so that it is easier on my eyes and maybe the switch() falls through more efficiently/faster?


Thanks George, I saved the link and will have to look at it when I am ready to nuke. Did you memorize the info from both links and can you reproduce them without looking or is it something you have to go back from time to time in order to refresh?

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
template<typename T = int>
T getNum2(std::string_view prm)
{
	string input{};
	T response{};

	for(;;)
	{
		std::cout << prm;
		std::getline(std::cin, input);
		std::istringstream is{ input };

		bool isNumerical = (is >> response) && !(is >> input);
	
		switch (isNumerical)
		{
		case 1:
			return response;
			break;
		case 0:
			std::cin.clear();
			std::cerr << "Not a number!\n";
			break;
		}
	}
}


int main() 
{
	const auto no{ getNum2<double>("Enter a number: ") };
	std::cout << "Entered number is " << no << '\n';

	return 0;
}
seeplus' for loop is a bit too clever for my taste. It uses std::cout << prm as a condition and assumes it never fails. In the unlikely event that it does fail it would reach the end of the function without returning anything which would cause UB.

Besides the clever syntax, the next clever line "(is >> response) && !(is >> input)" means it is acceptable if an int or double can be streamed to the int/double "response" and NOT to string. If any part of the user input has a char/special character/string, then "(is >> input)" falls true and the if statement does not return the "response"...clever stuff indeed!

The !(is >> input) part just reads the next sequence of non-whitespace characters that is left over on the line after reading the response. If this succeeds (i.e. it finds any left-over non-whitespace characters) then it returns false.

Examples:

T=double
Input line: "2.5 x"
is >> response extracts "2.5"
!(is >> input) extracts "x" (and returns false)

T=int
Input line: "1.3"
is >> response extracts "1"
!(is >> input) extracts ".3" (and returns false)

T=int
Input line: "512 "
is >> response extracts "512"
!(is >> input) fails to extract anything (and returns true)

Note that getNum could be used to read any type that has a >> stream operator. The only thing that makes it specific to numbers is the error message.

When you are viewing code, do you prefer to see clever code or simplified code and is seen in the wild?

I prefer to see simple code that is easy to read.
(This doesn't mean you should be overly verbose.)

I think using clever tricks to try and use as little code as possible should generally be avoided, but what one person consider to be a "clever trick" (in a negative sense) another person might consider to just be the normal way to do things. Being concise has advantages too.

I rewrote your code so that it is easier on my eyes and maybe the switch() falls through more efficiently/faster?

Since isNumerical is a bool I think it makes more sense to use true and false instead of 1 and 0 in the case labels. I don't see why it would be faster to use a switch though. Just use an ordinary if statement.
Last edited on
It uses std::cout << prm as a condition and assumes it never fails. In the unlikely event that it does fail it would reach the end of the function without returning anything which would cause UB


I've never had std::cout fail, but if it could on your os then after L15:

 
    return (std::cerr << "cout failed!"), 0;


which will return a 0 if cout failed;

Note that getNum could be used to read any type that has a >> stream operator


The original code used a 'requires' to make sure the template type was numeric. I excluded that here for the sake of simplicity!
seeplus wrote:
I've never had std::cout fail, but if it could on your os then after L15:
 
return (std::cerr << "cout failed!"), 0;

Me neither, but I don't want to gamble when it comes to UB, and it's nice to get rid of the compiler warning.

Since the subject about "clever tricks" came up, do you mind if I ask why you wrote the return statement like that? It's doing two things so why not write it using two different statement (on two different lines)?

1
2
std::cerr << "cout failed!";
return 0;
Last edited on
Cause it's one line and not 2!

I don't see why it would be faster to use a switch though. Just use an ordinary if statement.


I read somewhere that the switch was faster than the if/else, back when I first learned switch statements. I just did a quick search & a few places say it is faster, one says if it is greater than >5 checks then switch is faster. I don't know, since then I just assumed they were faster. Similar to how
++num is faster than num++, when not used immediately.

https://www.google.com/search?client=firefox-b-1-d&q=c%2B%2B+are+switch+faster+than+if+else


The !(is >> input) part just reads the next sequence of non-whitespace characters that is left over on the line after reading the response. If this succeeds (i.e. it finds any left-over non-whitespace characters) then it returns false.


Not just the NEXT sequence, but ANY char/string, even at the beginning. Consider when you type "b2.1".
Interesting though when you type "2.b1" that the double consumes the "2." with the period as if it were 2.0 and it does not get consumed by the string, which consumes "b1".
Hmmm, maybe the switch is/was faster in C and many people carry that over and assume that the same applies to C++. Hence, people like me read it and believe it and just knew it as fact (or false fact).

Since isNumerical is a bool I think it makes more sense to use true and false instead of 1 and 0 in the case labels

I knew you can use true/false, but I liked the faster typing. It works though, but do you think it is WRONG? How if true=1 & false=0. Is true/false a macro and they simply get replaced with 0 & 1 during compile time?
Last edited on
PS. To be really pedantic, you'd return a std::optional/std::expected (C++23) so that the caller can differentiate between a return with a std::cout failure or an entered 0.
SubZeroWins wrote:
I read somewhere that the switch was faster than the if/else, back when I first learned switch statements. I just did a quick search & a few places say it is faster, one says if it is greater than >5 checks then switch is faster. I don't know, since then I just assumed they were faster.

That might be true when you have a long list of values that you're checking for because the compiler might be able to turn the switch into a "jump table" (if the values are consecutive) or do other optimizations.

In theory it is possible for the compiler to do the same optimizations for the equivalent chain of if-else-if statements, and compilers have become better at this recently, but I don't know if they're always able to do it.

In any case, for a single if (with or without an else) there really isn't any performance advantage to using a switch instead.

SubZeroWins wrote:
Not just the NEXT sequence, but ANY char/string, even at the beginning. Consider when you type "b2.1".

In that case the (is >> response) will fail so the !(is >> input) expression is never executed.

This is happening because the && operator returns false without evaluating the second operand expression if the first operand expression is false. This is called "short-circuiting".

SubZeroWins wrote:
I knew you can use true/false, but I liked the faster typing. It works though, but do you think it is WRONG?

It's not technically wrong but I think true and false is better for showing your intent. I don't think of bool as an integer type that can store 1 or 0. I think of it as a type that is used to represent Boolean values, that's why I write true and false. It's also nice to get the type right (even though it doesn't matter here). The literal 1 is an int while true is a bool.

 
std::cout << std::boolalpha << 1 << true; // prints "1true" 
Last edited on
Got it, thanks!
How do you guys go about adding timers to code in order to measure speed? Would be nice to check a if/else statement and switch with a million loops for each, just for the heck of it.
https://www.learncpp.com/cpp-tutorial/timing-your-code/

I rarely bother with timing code, so really don't need doing this.

Of course, there are lots of links for other sites showing ways to time code:

https://duckduckgo.com/?t=ffab&q=c%2B%2B+time+code&ia=web

Avoid using C timers when writing C++ code. Some people just can't get past "I learned doing it 'The C Way' and that is how I will always do it!"

Learning the C++ way also applies to generating pseudo-random numbers, the C Way has "issues."

https://web.archive.org/web/20180123103235/http://cpp.indi.frih.net/blog/2014/12/the-bell-has-tolled-for-rand/

/rant off
I knew you can use true/false, but I liked the faster typing.

Saving an occasional 3 or 4 typed characters is faster? Those saved pico-seconds likely won't be worth it when reviewing code days, weeks and months later.

Same goes for that (IMO) abomination using namespace std;.

I never use it, and now after a very brief transition period typing std:: and whatever C++ feature I want is actually faster.

https://stackoverflow.com/questions/1452721/whats-the-problem-with-using-namespace-std

Thanks, super cool that I can check timing now!
Have you guys used the timing in the 1st link yet and as I copied into my code? Is this something you know by memory or do you need to reference it?

I know the below also takes the while loop with the timing, but both the if and switch do the same and should balance out. Numbers are a little different with each run and sometimes are zero, maybe too small to represent.

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#include <iostream>
#include <chrono> 
using namespace std;

class Timer
{
private:
	// Type aliases to make accessing nested type easier
	using Clock = std::chrono::steady_clock;
	using Second = std::chrono::duration<double, std::ratio<1> >;

	std::chrono::time_point<Clock> m_beg{ Clock::now() };

public:
	void reset()
	{
		m_beg = Clock::now();
	}

	double elapsed() const
	{
		return std::chrono::duration_cast<Second>(Clock::now() - m_beg).count();
	}
};

Timer timer1;

//TEST if/else and switch timing
void TimeIfSwitch(const bool& ifOrSwitch, const bool& state, const int& maxCount, const string msg)
{
	unsigned int count{};

	if (ifOrSwitch == false)		//Test if/else timing
	{
		timer1.reset();
		while (count < maxCount)
		{
			if (state == true)
				++count;
			else
				++count;
		}
	}
	else                         //Test switch timing
	{
		timer1.reset();
		while (count < maxCount)
		{
			switch (state)
			{
			case true:
				++count;
				break;
			case false:
				++count;
				break;
			}
		}
	}

	//cout << fixed << timer1.elapsed() << msg << endl;
	cout << timer1.elapsed() << msg << endl;
	cout << "COUNT = " << count << endl;
	cout << "***************" << endl;
}

int main()
{
	bool stateT = true;
	bool stateF = false;
	bool testIf = false;			//test if/else
	bool testSwitch = true;			//test switch{}
	unsigned int maxCount{1};


	cout << "************Test loop 1x************" << endl;
	TimeIfSwitch(testIf, stateT, maxCount, " (if (true))");
	TimeIfSwitch(testSwitch, stateT, maxCount, " (switch (true))");

	TimeIfSwitch(testIf, stateF, maxCount, " (if (false))");
	TimeIfSwitch(testSwitch, stateF, maxCount, " (switch (false))");


	cout << endl << "************Test loop 1'000'000x************" << endl;
	maxCount = 1'000'000;
	TimeIfSwitch(testIf, stateT, maxCount, " (if (true))");
	TimeIfSwitch(testSwitch, stateT, maxCount, " (switch (true))");

	TimeIfSwitch(testIf, stateF, maxCount, " (if (false))");
	TimeIfSwitch(testSwitch, stateF, maxCount, " (switch (false))");


	cout << endl << "************Test loop 10'000'000x************" << endl;
	maxCount = 10'000'000;
	TimeIfSwitch(testIf, stateT, maxCount, " (if (true))");
	TimeIfSwitch(testSwitch, stateT, maxCount, " (switch (true))");

	TimeIfSwitch(testIf, stateF, maxCount, " (if (false))");
	TimeIfSwitch(testSwitch, stateF, maxCount, " (switch (false))");


	cout << endl << "************Test loop 1x from main()************" << endl;
	timer1.reset();
	if (stateT == true);
	else;
	cout << timer1.elapsed() << " (if (true))" << endl;
	cout << "COUNT = " << 1 << endl;
	cout << "***************" << endl;
	
	timer1.reset();
	switch (stateT)
	{
		case true:
			break;
		case false:
			break;
	}
	cout << timer1.elapsed() << " (switch (true))" << endl;
	cout << "COUNT = " << 1 << endl;
	cout << "***************" << endl;


	timer1.reset();
	if (stateF == true);
	else;
	cout << timer1.elapsed() << " (if (false))" << endl;
	cout << "COUNT = " << 1 << endl;
	cout << "***************" << endl;

	timer1.reset();
	switch (stateF)
	{
	case true:
		break;
	case false:
		break;
	}
	cout << timer1.elapsed() << " (switch (false))" << endl;
	cout << "COUNT = " << 1 << endl;
	cout << "***************" << endl;
	return 0;
}
/*
OUTPUT:
************Test loop 1x************
2e-07 (if (true))
COUNT = 1
***************
1e-07 (switch (true))
COUNT = 1
***************
1e-07 (if (false))
COUNT = 1
***************
1e-07 (switch (false))
COUNT = 1
***************

************Test loop 1'000'000x************
0.0010435 (if (true))
COUNT = 1000000
***************
0.0012574 (switch (true))
COUNT = 1000000
***************
0.0010443 (if (false))
COUNT = 1000000
***************
0.0018543 (switch (false))
COUNT = 1000000
***************

************Test loop 10'000'000x************
0.0104584 (if (true))
COUNT = 10000000
***************
0.0126492 (switch (true))
COUNT = 10000000
***************
0.009499 (if (false))
COUNT = 10000000
***************
0.0173167 (switch (false))
COUNT = 10000000
***************

************Test loop 1x from main()************
2e-07 (if (true))
COUNT = 1
***************
1e-07 (switch (true))
COUNT = 1
***************
2e-07 (if (false))
COUNT = 1
***************
2e-07 (switch (false))
COUNT = 1
***************
*/
Hi

There is also this RAII timing class:

https://www.youtube.com/watch?v=oEx5vGNFrLk

I did have an example of it here at cplusplus, but couldn't find it.
Pages: 12345... 13