The Problem: When I try to stop the first loop, and move to the second by either hitting CTRL-D or giving a letter as input, then the program quits and skips the second loop. I realized that it relates to the cin mechanism, but I failed to cure it.
This is because when you try and read a letter or EOF (Ctrl-D) this sets the state of the stream into a bad state. Once this happens all operations on the stream fail (until you reset it). This can be done by calling clear()
std::cin.clear();
The question: How should I program this? What is the least painful manner to overcome the problem?
I would not use this technique.
I would use something like an empty line as a separator between loop. Thus the code looks like this:
while(std::getline(std::cin, line) && !line.empty())
{
// STUFF
}
while(std::getline(std::cin, line) && !line.empty())
{
// STUFF
}
Try this:
Note: in C++ streams work best when numbers are space separated. So it is easy just use space or tab to separate the numbers.
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
struct Average
{
Average(): result(0.0), count(0) {}
void operator()(double const& val) { result += val;++count;}
operator double() { return (count == 0) ? 0.0 : result/count;}
double result;
int count;
};
struct Sum
{
Sum(): result(0.0) {}
void operator()(double const& val) { result += val;}
operator double() { return result;}
double result;
};
int main()
{
std::string line;
// Read a line at a time.
// If the read fails or the line read is empty then stop looping.
while(std::getline(std::cin, line) && !line.empty())
{
// In C++ we use std::Vector to represent arrays.
std::vector<double> data;
std::stringstream lineStream(line);
// Copy a set of space separated integers from the line into data.
// I know you wanted doubles (I do this next time)
// I just wanted to show how easy it is to change between the types being
// read. So here I use integers and below I use doubles.
std::copy( std::istream_iterator<int>(lineStream),
std::istream_iterator<int>(),
std::back_inserter(data));
// Sum is a functor type.
// This means when you treat it like a function then it calls the method operator()
// We call sum(x) for each member of the vector data
Sum sum;
sum = std::for_each(data.begin(), data.end(), sum);
std::cout << "Sum: " << static_cast<double>(sum) << "\n";
}
// Read a line at a time.
// If the read fails or the line read is empty then stop looping.
while(std::getline(std::cin, line) && !line.empty())
{
// In C++ we use std::Vector to represent arrays.
std::vector<double> data;
std::stringstream lineStream(line);
// Same as above but we read doubles from the input not integers.
// Notice the sleigh difference from above.
std::copy( std::istream_iterator<double>(lineStream),
std::istream_iterator<double>(),
std::back_inserter(data));
// Average is a functor type.
// This means when you treat it like a function then it calls the method operator()
// We call average(x) for each member of the vector data
Average average;
average = std::for_each(data.begin(), data.end(), average);
std::cout << "Average: " << static_cast<double>(average) << "\n";
}
}
// Or we could templatize the code slightly:
template<typename T, typename F>
void doAction()
{
std::string line;
// Read a line at a time.
// If the read fails or the line read is empty then stop looping.
while(std::getline(std::cin, line) && !line.empty())
{
std::stringstream lineStream(line);
// F is a functor type.
// This means when you treat it like a function then it calls the method operator()
// We call action(x) for each object type 'T' that we find on the line.
// Notice how we do not actual need to store the data in an array first
// We can actually processes the data as we read it from the line
F action;
action = std::for_each( std::istream_iterator<T>(lineStream),
std::istream_iterator<T>(),
action);
std::cout << "Action Result: " << static_cast<double>(action) << "\n";
}
}
int main()
{
doAction<int, Sum>();
doAction<double, Average>();
}