c++ - efficent way to save objects into binary files -
i've class consists of matrix of vectors: vector< myfeatvector<t> > m_vcells
, outer vector represents matrix. each element in matrix vector
(i extended stl vector
class , named myfeatvector<t>
).
i'm trying code efficient method store objects of class in binary files. now, require 3 nested loops:
foutput.write( reinterpret_cast<char*>( &(this->at(dy,dx,dz)) ), sizeof(t) );
where this->at(dy,dx,dz)
retrieves dz
element of vector @ position [dy,dx]
.
is there possibility store m_vcells
private member without using loops? tried like: foutput.write(reinterpret_cast<char*>(&(this->m_vcells[0])), (this->m_vcells.size())*sizeof(cfeaturevector<t>));
seems not work correctly. can assume vectors in matrix have same size, although more general solution welcomed :-)
furthermore, following nested-loop implementation, storing objects of class in binary files seem require more physical space storing same objects in plain-text files. bit weird.
i trying follow suggestion under http://forum.allaboutcircuits.com/showthread.php?t=16465 couldn't arrive proper solution.
thanks!
below simplified example of serialization
, unserialization
methods.
template < typename t > bool myfeatmatrix<t>::writebinary( const string & ofile ){ ofstream foutput(ofile.c_str(), ios::out|ios::binary); foutput.write(reinterpret_cast<char*>(&this->m_nheight), sizeof(int)); foutput.write(reinterpret_cast<char*>(&this->m_nwidth), sizeof(int)); foutput.write(reinterpret_cast<char*>(&this->m_ndepth), sizeof(int)); //foutput.write(reinterpret_cast<char*>(&(this->m_vcells[0])), nsze*sizeof(cfeaturevector<t>)); for(register int dy=0; dy < this->m_nheight; dy++){ for(register int dx=0; dx < this->m_nwidth; dx++){ for(register int dz=0; dz < this->m_ndepth; dz++){ foutput.write( reinterpret_cast<char*>( &(this->at(dy,dx,dz)) ), sizeof(t) ); } } } foutput.close(); return true; }
template < typename t > bool myfeatmatrix<t>::readbinary( const string & ifile ){ ifstream finput(ifile.c_str(), ios::in|ios::binary); int nheight, nwidth, ndepth; finput.read(reinterpret_cast<char*>(&nheight), sizeof(int)); finput.read(reinterpret_cast<char*>(&nwidth), sizeof(int)); finput.read(reinterpret_cast<char*>(&ndepth), sizeof(int)); this->resize(nheight, nwidth, ndepth); for(register int dy=0; dy < this->m_nheight; dy++){ for(register int dx=0; dx < this->m_nwidth; dx++){ for(register int dz=0; dz < this->m_ndepth; dz++){ finput.read( reinterpret_cast<char*>( &(this->at(dy,dx,dz)) ), sizeof(t) ); } } } finput.close(); return true; }
a efficient method store objects array (or contiguous space), blast buffer file. advantage disk platters don't have waste time ramping , writing can performed contiguously instead of in random locations.
if performance bottleneck, may want consider using multiple threads, 1 thread handle output. dump objects buffer, set flag, writing thread handle output, releaving main task perform more important tasks.
edit 1: serializing example
following code has not been compiled , illustrative purposes only.
#include <fstream> #include <algorithm> using std::ofstream; using std::fill; class binary_stream_interface { virtual void load_from_buffer(const unsigned char *& buf_ptr) = 0; virtual size_t size_on_stream(void) const = 0; virtual void store_to_buffer(unsigned char *& buf_ptr) const = 0; }; struct pet : public binary_stream_interface, max_name_length(32) { std::string name; unsigned int age; const unsigned int max_name_length; void load_from_buffer(const unsigned char *& buf_ptr) { age = *((unsigned int *) buf_ptr); buf_ptr += sizeof(unsigned int); name = std::string((char *) buf_ptr); buf_ptr += max_name_length; return; } size_t size_on_stream(void) const { return sizeof(unsigned int) + max_name_length; } void store_to_buffer(unsigned char *& buf_ptr) const { *((unsigned int *) buf_ptr) = age; buf_ptr += sizeof(unsigned int); std::fill(buf_ptr, 0, max_name_length); strncpy((char *) buf_ptr, name.c_str(), max_name_length); buf_ptr += max_name_length; return; } }; int main(void) { pet dog; dog.name = "fido"; dog.age = 5; ofstream data_file("pet_data.bin", std::ios::binary); // determine size of buffer size_t buffer_size = dog.size_on_stream(); // allocate buffer unsigned char * buffer = new unsigned char [buffer_size]; unsigned char * buf_ptr = buffer; // write / store object buffer. dog.store_to_buffer(buf_ptr); // write buffer file / stream. data_file.write((char *) buffer, buffer_size); data_file.close(); delete [] buffer; return 0; }
edit 2: class vector of strings
class many_strings : public binary_stream_interface { enum {max_string_size = 32}; size_t size_on_stream(void) const { return m_string_container.size() * max_string_size // total size of strings. + sizeof(size_t); // room quantity variable. } void store_to_buffer(unsigned char *& buf_ptr) const { // treat vector<string> variable length field. // store quantity of strings buffer, // followed content. size_t string_quantity = m_string_container.size(); *((size_t *) buf_ptr) = string_quantity; buf_ptr += sizeof(size_t); (size_t = 0; < string_quantity; ++i) { // each string fixed length field. // pad '\0' first, copy data. std::fill((char *)buf_ptr, 0, max_string_size); strncpy(buf_ptr, m_string_container[i].c_str(), max_string_size); buf_ptr += max_string_size; } } void load_from_buffer(const unsigned char *& buf_ptr) { // actual coding left exercise reader. // psuedo code: // clear / empty string container. // load quantity variable. // increment buffer variable size of quantity variable. // each new string (up quantity read) // load temporary string buffer via buffer pointer. // push temporary string vector // increment buffer pointer max_string_size. // end-for } std::vector<std::string> m_string_container; };
Comments
Post a Comment