Reputation: 10122
I read an answer here showing how to read an entire stream into a std::string with the following one (two) liner:
std::istreambuf_iterator<char> eos;
std::string s(std::istreambuf_iterator<char>(stream), eos);
For doing something similar to read a binary stream into a std::vector
, why can't I simply replace char
with uint8_t
and std::string
with std::vector
?
auto stream = std::ifstream(path, std::ios::in | std::ios::binary);
auto eos = std::istreambuf_iterator<uint8_t>();
auto buffer = std::vector<uint8_t>(std::istreambuf_iterator<uint8_t>(stream), eos);
The above produces a compiler error (VC2013):
1>d:\non-svn\c++\library\i\file\filereader.cpp(62): error C2440: '' : cannot convert from 'std::basic_ifstream>' to 'std::istreambuf_iterator>' 1>
with 1> [ 1> _Elem=uint8_t 1> ] 1>
No constructor could take the source type, or constructor overload resolution was ambiguous
Upvotes: 8
Views: 13970
Reputation: 131415
I'd say the problem is with what you're trying to do:
For doing something similar to read a binary stream into an
std::vector
Don't use iteration for reading bytes into a buffer, my friend!
Instead, do something like this:
std::ifstream file(path, std::ios::binary);
auto file_contents = [&file]() {
file.seekg(0, std::ios::end);
auto file_size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<byte_type> file_contents(file_size);
file.read(result.data(), file_size);
return file_contents;
}();
or actually, since you want to handle errors, this:
std::ifstream file(path, std::ios::binary);
auto file_contents = [&file]() {
try {
file.exceptions(std::ios::failbit | std::ios::badbit);
file.seekg(0, std::ios::end);
auto file_size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<byte_type> file_contents(file_size);
file.read(result.data(), file_size);
return file_contents;
} catch (std::ios_base::failure& ios_failure) {
if (errno == 0) { throw ios_failure; }
throw std::system_error(errno, std::generic_category(), "read from binary file");
}
}();
Upvotes: 0
Reputation: 302663
There's just a type mismatch. ifstream
is just a typedef:
typedef basic_ifstream<char> ifstream;
So if you want to use a different underlying type, you just have to tell it:
std::basic_ifstream<uint8_t> stream(path, std::ios::in | std::ios::binary);
auto eos = std::istreambuf_iterator<uint8_t>();
auto buffer = std::vector<uint8_t>(std::istreambuf_iterator<uint8_t>(stream), eos);
That works for me.
Or, since Dietmar says this might be a little sketchy, you could do something like:
auto stream = std::ifstream(...);
std::vector<uint8_t> data;
std::for_each(std::istreambuf_iterator<char>(stream),
std::istreambuf_iterator<char>(),
[&data](const char c){
data.push_back(c);
});
Upvotes: 17
Reputation: 254431
ifstream
is a stream of char
, not uint8_t
. You'll need either basic_ifstream<uint8_t>
or istreambuf_iterator<char>
for the types to match.
The former may not work without some amount of work, since the library is only required to support streams of char
and wchar_t
; so you probably want istreambuf_iterator<char>
.
Upvotes: 6