I would expect the following to leave the buf_iter
pointing to the character n
characters after the point at which it started. Instead it is left pointing at the last character read. Why is this? i.e. if I do in_stream.tellg() before and after the copy_n, they differ not by n
but by (n-1)
. Had I read n
characters with in_stream.read
, then the position would be advanced by n
.
std::istreambuf_iterator<char> buf_iter(in_stream);
std::copy_n(buf_iter, n, sym.begin());
I've looked at the implementation and it clearly does this on purpose, skipping the final increment.
Another post here mentions that incrementing the from iterator when it is hooked up to, say, cin
, will cause one too many reads since a read is done on operator++()
. That sounds like an issue with cin
- why isn't the read done on operator*()
?
Does the standard specify this anywhere? The docs I've seen don't mention what happens to the from iterator, and I've seen two different pages that give "possible correct implementations" that do each of the behaviours:
template< class InputIt, class Size, class OutputIt>
OutputIt copy_n(InputIt first, Size count, OutputIt result)
{
if (count > 0) {
*result++ = *first;
for (Size i = 1; i < count; ++i) {
*result++ = *++first;
}
}
return result;
}
while at cplusplus.com we have:
template<class InputIterator, class Size, class OutputIterator>
OutputIterator copy_n (InputIterator first, Size n, OutputIterator result)
{
while (n>0) {
*result = *first;
++result; ++first;
--n;
}
return result;
}
Both do n reads and result in the same contents in result. However, the first will only increment the "first" iterator n-1
times, and the second will increment it n
times.
What gives? How do I write portable code? I can use tellg
and then seekg
but then I might as well just do the loop by hand (ugh!).
Note that I'm not trying to read from the iterator after calling copy_n
, rather I want to read from the underlying stream after calling copy_n
, and the problem is that copy_n
is left pointing on byte short of where I had expected it to be. For now I'm going with the somewhat hideous but apparently portable:
auto pos = in_stream.tellg();
std::istreambuf_iterator<char> buf_iter(in_stream);
std::copy_n(buf_iter, cl, sym.begin());
in_stream.seekg(pos + cl);
uint64_t foo;
in_stream.read(reinterpret_cast<char *>(&foo), 8);
BTW, in case its not clear, I'm trying to avoid copying the data into a buffer and then again into the string sym
.
@DaveS: Moving out of my specific problem, here is a simple program that does not output what I would expect due to the fact that the input iterator isn't incremented the final time:
#include <algorithm>
#include <string>
#include <iostream>
#include <fstream>
int main(int argc, const char * argv[])
{
std::ifstream in("numbers.txt");
std::istreambuf_iterator<char> in_iter(in);
std::ostreambuf_iterator<char> out_iter(std::cout);
std::copy_n(in_iter, 3, out_iter);
std::cout << std::endl;
std::copy_n(in_iter, 3, out_iter);
std::cout << std::endl;
std::copy_n(in_iter, 3, out_iter);
std::cout << std::endl;
return 0;
}
The input file is just "0123456789\n"
I'm getting:
012
234
456
Because of the side-effect of istreambuf_iterator::operator++()
, this would give a different result if copy_n
was implemented to increment the input iterator n
times.
@aschepler: Need to capture the local parameter, but I'm going with it:
std::generate_n(sym.begin(), cl, [&in_stream](){ return in_stream.get(); });