Reputation: 777
Hey guys here's a interesting challenge for you all. I'm given a text file and I'm supposed to process the information line by line. The processing part is trivial so long as I can obtain the individual lines. However here's the challenge:
Currently right now my best solution is this: Is there a C++ iterator that can iterate over a file line by line? but I'm hoping for a better one that does not involve creating my own iterator class or implementing a proxy for std::string.
P.S. this is for a school assignment and the challenge here was to solve the problem using a combination of std functionalities and algorithms but I have no clue how to go about solving it
Upvotes: 0
Views: 3179
Reputation: 275385
This is just a toy.
It consists of a Generator based way to slurp up files (which I find easy to write), together with a half-working adapter that turns Generators into Iterators.
A Generator in this context is a functor that takes a lambda, and passes that lambda the thing iterated over and returns true, or returns false. It makes a really short loop:
while( gen( [&]( std::string line ) {
// code using line goes here
} ) ); // <- notice that this is an empty while loop that does stuff
but you aren't allowed to use that. (This design is inspired by python generators, which I honestly find much more natural to work with than C++ iterators for many kinds of problem, which includes reading from a file).
The adpator takes this generator and makes it an iterator, so you can pass it to for_each
.
The point of this is that your question itself is inane, so rather than do something obvious (goto, or directly use for_each
on an istream iterator), I'm proposing messing around with something obtuse and different. :)
The Line generator is nice and short:
struct LineGenerator {
LineGenerator(std::ostream& os_):os(&os_) {}
std::ostream* os;
template<typename F>
bool operator()(F&& func) {
std::string s;
bool bRet = std::getline( *os, s );
if (!bRet) return false;
std::forward<F>(func)(s);
return true;
}
};
The adaptor, on the other hand, is pretty messy: (and not tested)
template<typename Generator>
struct generator_iterator {
typedef generator_iterator<Generator> my_type;
typedef typename std::decay< decltype( std::declval<Generator>()() ) >::type value_type;
Generator g;
bool bDone;
value_type val;
generator_iterator( Generator g_ ):g(g_), bDone(false) {
next();
}
generator_iterator(): bDone(true) {}
private:
void next() {
if (bDone) return;
bDone = g(val);
}
public:
generator_iterator& operator++() {
next();
return *this;
}
value_type operator*() {
return val;
}
const value_type operator*() const {
return val;
}
value_type* operator->() {
return &val;
}
value_type const* operator->() const {
return &val;
}
bool operator==( my_type const& o ) {
if (bDone != o.bDone) return false;
if (!bDone && !o.bDone) return true;
return false;
}
};
template<typename Generator>
generator_iterator<Generator> make_gen_it( Generator&& g ) {
return generator_iterator<Generator>( std::forward<Generator>(g) );
}
template<typename Generator>
generator_iterator<Generator> make_end_gen_it() {
return generator_iterator<Generator>();
}
I hope this amuses!
Upvotes: 0
Reputation: 11181
ifstream input("somefile")
if (!input) { /* Handle error */ }
//MyDataType needs to implement an operator>>
std::vector<MyDataType> res;
std::istream_iterator<MyDataType> first(input);
std::istream_iterator<MyDataType> last;
std::copy(first,last, std::back_inserter(res));
//etc..
Your input operator can be something like this:
std::istream& operator>>(std::istream &in,MyDataType & out)
{
std::string str;
std::getline(in,str);
//Do something with str without using loops
return in;
}
There are a lot of loops here (you dont' want to use goto
, don't you?), but they are all hidden behind std::copy
and std::getline
Upvotes: 6