If using boost is an option for you, since version 1.55 boost::container::vector has had support for explicitly default-initializing elements when resizing using the syntax:
using namespace boost::container;
vector<T> vector(37283, default_init);
at creation or
using namespace boost::container;
vector.resize(37283, default_init);
after creation. This results in the nice syntax:
using T = unsigned; // but can be any trivially copyable type
FILE* fp = fopen( outfile.c_str(), "r" );
boost::container::vector<T> x(big_n, boost::container::default_init);
fread( x.data(), sizeof(T), big_n, fp );
fclose( fp );
In my tests performance is identical to using std::vector
with a default-initializing allocator.
EDIT: Unrelated aside, I'd use an RAII wrapper for FILE*
:
struct FILE_deleter {
void operator () (FILE* f) const {
if (f) fclose(f);
}
};
using FILE_ptr = std::unique_ptr<FILE, FILE_deleter>;
using T = unsigned; // but can be any trivially copyable type
FILE_ptr fp{fopen( outfile.c_str(), "r" )};
boost::container::vector<T> x(big_n, boost::container::default_init);
fread( x.data(), sizeof(T), big_n, fp.get() );
I'm a bit OCD about RAII.
EDIT 2: Another option, if you absolutely MUST produce a std::vector<T>
, and not a boost::container::vector<T>
or std::vector<T, default_allocator<T>>
, is to fill your std::vector<T>
from a custom iterator pair. Here's one way to make an fread
iterator:
template <typename T>
class fread_iterator :
public boost::iterator_facade<fread_iterator<T>, T,
std::input_iterator_tag, T> {
friend boost::iterator_core_access;
bool equal(const fread_iterator& other) const {
return (file_ && feof(file_)) || n_ <= other.n_;
}
T dereference() const {
// is_trivially_copyable is sufficient, but libstdc++
// (for whatever reason) doesn't have that trait.
static_assert(std::is_pod<T>::value,
"Jabberwocky is killing user.");
T result;
fread(&result, sizeof(result), 1, file_);
return result;
}
void increment() { --n_; }
FILE* file_;
std::size_t n_;
public:
fread_iterator() : file_(nullptr), n_(0) {}
fread_iterator(FILE* file, std::size_t n) : file_(file), n_(n) {}
};
(I've used boost::iterator_facade to reduce the iterator boilerplate.) The idea here is that the compiler can elide the move construction of dereference
's return value so that fread
will read directly into the vector
's memory buffer. It will likely be less efficient due to calling fread
once per item vs. just once for the allocator modification methods, but nothing too terrible since (a) the file data is still only copied once from the stdio buffer into the vector, and (b) the whole point of buffering IO is so that granularity has less impact. You would fill the vector using its assign(iterator, iterator)
member:
using T = unsigned; // but can be any trivially copyable type
FILE_ptr fp{fopen( outfile.c_str(), "r" )};
std::vector<T> x;
x.reserve(big_n);
x.assign(fread_iterator<T>{fp.get(), big_n}, fread_iterator<T>{});
Throwing it all together and testing side-by-side, this iterator method is about 10% slower than using the custom allocator method or boost::container::vector
. The allocator and boost method have virtually identical performance.