I prefer to build some infrastructure that will make the rest of the code trivial. The infrastructure may be a little extra work, but the long-term savings can be substantial. In this case, it takes the form of a special iterator that allows you to specify the "sentinel" that will end the input. It acts like a normal istream_iterator
, except that you specify the sentinel value when you construct the "end of range" iterator.
// sentinel_iterator.h
#pragma once
#if !defined(SENTINEL_ITERATOR_H_)
#define SENTINEL_ITERATOR_H_
#include <istream>
#include <iterator>
template <class T,
class charT=char,
class traits=std::char_traits<charT>,
class distance = ptrdiff_t>
class sentinel_iterator :
public std::iterator<std::input_iterator_tag,distance,void,void,void>
{
std::basic_istream<charT,traits> *is;
T value;
public:
typedef charT char_type;
typedef traits traits_type;
typedef std::basic_istream<charT,traits> istream_type;
sentinel_iterator(istream_type& s)
: is(&s)
{ s >> value; }
sentinel_iterator(T const &s) : is(0), value(s) { }
const T &operator*() const { return value; }
const T *operator->() const { return &value; }
sentinel_iterator &operator++() {
(*is)>>value;
return *this;
}
sentinel_iterator &operator++(int) {
sentinel_iterator tmp = *this;
(*is)>>value;
return (tmp);
}
bool operator==(sentinel_iterator<T,charT,traits,distance> const &x) {
return value == x.value;
}
bool operator!=(sentinel_iterator<T,charT,traits,distance> const &x) {
return !(value == x.value);
}
};
#endif
With that in place, reading the data becomes trivial:
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
#include "sentinel_iterator.h"
int main() {
// As per spec, read until a "." is entered:
std::vector<std::string> strings(
sentinel_iterator<std::string>(std::cin),
sentinel_iterator<std::string>("."));
// It's not restricted to strings either. Read numbers until -1 is entered:
std::vector<int> numbers(
sentinel_iterator<int>(std::cin),
sentinel_iterator<int>(-1));
// show the strings:
std::copy(strings.begin(), strings.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
// show the numbers:
std::copy(numbers.begin(), numbers.end(),
std::ostream_iterator<int>(std::cout, "\n"));
return 0;
}
Given an input of:
This is a string .
1 2 3 5 -1
It produces an output of:
This
is
a
string
1
2
3
5
It should work for essentially any type that defines a stream extractor and testing for equality (i.e., saying x==y
will compile and produce meaningful results).