C++ Qt: bitwise operations - c++

I'm working on a little project for college, and I need to model transmission over network, and to impment and visualize different sorts of error correction algorithms. My improvized packet consists of one quint8: I need to convert it into a bit array, like QBitArray, append a check bit to it, trasfer it over UDP, check the success of transmission with the check bit, and then construct quint8 out of it.
Once again, it's not a practical but educational task, so don't suggest me to use real algoriths like CRC...
So my question is: how do I convert any data type (in this case quint8) into QBitArray? I mean any data in computer is a bit array, but how do I access it is the question.
Thanks, Dmitri.

Lets see if we can get it correct
template < class T >
static QBitArray toQBit ( const T &obj ) {
int const bitsInByte= 8;
int const bytsInObject= sizeof(T);
const quint8 *data = static_cast<const quint8*>(&obj) ;
QBitArray result(bytsInObject*bitsInByte);
for ( int byte=0; byte<bytsInObject ; ++byte ) {
for ( int bit=0; bit<bitsInByte; ++bit ) {
result.setBit ( byte*bitsInByte + bit, data[byte] & (1<<bit) ) ;
}
}
return result;
}
void Foo () {
Bar b ;
QBitArray qb = toQBit ( b ) ;
}

qint8 is actually signed char. So you can treat your objs as a char array.
template < class T >
QBitArray toQBit ( T &obj ) {
int len = sizeof(obj) * 8 ;
qint8 *data = (qint8*)(&obj) ;
QBitArray result ;
for ( int i=0; i< sizeof(data); ++i ) {
for ( int j=0; j<8; ++j ) {
result.setBit ( i*8 + j, data[i] & (1<<j) ) ;
}
}
return result;
}
void Foo () {
Bar b ;
QBitArray qb = toQBit ( b ) ;
}

I don't see any point in declaring template function and then casting its argument to uint8.
Solution for types that can be promoted to unsigned long
#include <bitset>
template <typename T>
QBitArray toQBit(T val) {
std::bitset<sizeof(T) * 8> bs(val);
QBitArray result(bs.size());
for (int ii = 0; ii < bs.size(); ++ii) {
result.setBit(ii, bs.test(ii));
}
return result;
}
There is no way to generically convert any data type to bit array. Especially if your data type contains pointers you probably want to transfer the pointee not the pointer. So any complex type should be treated separately. And be aware about different endiannes (little-endian and big-endian) in different architectures. I think that std::bitset is safe according to this problem, but for example casting a pointer to struct to a char array and storing its bits may not be safe.

Related

The more productive passage by the elements std::vector<struct>

I have struct, which consist of a 30 elements different type.
struct my_struct {
int name1;
int name2;
int name3;
long long name4;
string name5;
//...
double name32;
double name33;
};
Also, i have vector, where save data(very much data) which have type of a struct std::vector<my_struct> my_vector;
Besides, i have so function:
char* name_function(char*){
//...some code
return char*;//processed value with type char*
}
What me need: take every element from vector, then take every value(name1,name2,..., name33) from struct, and then call function to every value, and after it write processing every value in new struct.
How i can see solution:
for(int x=0;x<my_vector.size();x++){
my_struct struct1 = my_vector[x];
char name1 = (char)struct1.name1;
struct1.name1 = int(my_function(&name1));
char name2= (char)struct1.name2;
struct1.name2 = double(my_function(&name2); //invalid type conversion
//...
my_vector[x]=struct1;
}
But, it takes a lot of memory, because i have to create many copies of the elements, maybe there is some more optimal solution for this task?
if you need to encrypt your data, you would need to write your function properly:
void rawencrypt( char *data, size_t size ) // encrypts data in place
{
for( size_t i = 0; i < size; ++i )
data[i] ^= 255; // lame xor encryption for example
}
template<typename T>
void encrypt( T &data ) { rawencrypt( static_cast<char *>( &data ), sizeof( T ) ); }
but for std::string it must be specialized:
template<>
void encrypt( std::string &data ) { rawencrypt( data.data(), data.length() ); }
and you must specialize encrypt() for any non POD data that you intend to encrypt. Encrypting your struct in place would be trivial now:
for(my_struct& s : my_vector){
encrypt( s.name1 );
encrypt( s.name2 );
//...
}
or you can even specialiize encrypt() for my_struct:
template<>
void encrypt( my_struct &data )
{
encrypt( data.name1 );
encrypt( data.name2 );
...
}
and loop would become even simpler:
for(my_struct& s : my_vector)
encrypt( s );
or if do not have C++11:
for(size_t x=0;x<my_vector.size();x++)
encrypt( my_vector[x] );
There is no need to copy the elements. Use references instead, and do proper int/string/int conversion:
char buf[28];
for(int x=0;x<my_vector.size();x++){
my_struct& s = my_vector[x];
itoa(s.name1, buf, 10);
buf = myfunction(buf);
s.name1 = atoi(buf);
//...
s.name5 = myfunction(s.name5.c_str());
//...
}
If you have C++11 or later:
char buf[28];
for(my_struct& s : my_vector){
itoa(s.name1, buf, 10);
//...
}
You can write directly
my_vector[x].name1 = int(my_function((char)my_vector[x].name1));
but these conversions are highly dubious.

How do you use the void pointer in C?

Here are a couple of function declarations that I'm having trouble understanding how to complete. I've scanned the web to see what a void pointer is, and I understand that it must be cast to something to be useful (because it just points to some memory block), but I don't see how that helps in completing these declarations.
/* type of comparison function that specifies a unique order.
Returns 1 if pointer1 should come before,
0 if equal to, or -1 if after pointer2. */
typedef int (*compare_function) (const void* pointer1, const void* pointer2);
/* get the data object */
void* get_obj(const void* item_pointer);
There are more functions like this, but I think if I understand how to do these two I should be in good shape. For example, for the second function, how do we cast the item_pointer to anything appropriate that should be returned?
void * usually means that you are only interested in the address of the data regardless of its type, some of the reasons:
the internal representation of the data this void * pointing to is hidden, you are not supposed to access the data directly, information hiding, your function 2 is properly an example of this case.
the type is known by some function in the call chain, like with qsort and most functions that pass arguments to other functions.
the type is not required because the data the pointer is pointing to will be handled as different type, like with memcpy which may handle the data as bytes, unsigned char *.
Sorting in C with quicksort uses void pointers so that we can sort any data in arrays. The sort function must return -1, +1, or 0 if the parameter b is before, after or the same as parameter a
#include <stdio.h>
#include <stdlib.h>
int sort_order( const void *, const void *);
int main(void)
{
int i;
char alfa[6] = { ’C’, ’E’, ’A’, ’D’, ’F’, ’B’ };
qsort( (char*)alfa, 6, sizeof(char), sort_order);
for (i=0 ; i<5 ; i++) // now in order?
printf("\nchar %d = %c",i, alfa[i]);
printf("\n");
system("PAUSE");
return 0;
}
int sort_order( const void* a, const void* b)
{
if ( *((char*)a) < *((char*)b) ) return -1 ;
else if ( *((char*)a) > *((char*)b) ) return 1 ;
else return 0 ;
}
Then you can sort your own datatypes:
typedef struct { float left; float right;} ears;
typedef struct{ char name[13]; int weight; ears eararea;} monkey;
monkey* Index[4];
for(i=0;i<4;i++)
Index[i]= (monkey* )malloc(sizeof(monkey));
qsort((void* ) Index, 4, sizeof(monkey* ), sort_order);
// Sorted by weight
int sort_order( const void* a, const void* b) {
if((**((monkey** )a)).weight < (**((monkey** )b)).weight) return -1 ;
else if ((**((monkey** )a)).weight > (**((monkey** )b)).weight ) return 1 ;
else return 0 ;
}
Complete program
#include <stdio.h>
#include <stdlib.h>
typedef struct {
float left;
float right;
} ears;
typedef struct {
char name[13];
int weight;
ears eararea;
} monkey;
int sort_monkeys( const void *, const void *);
int main(void)
{ monkey* monkeys[4];
int i;
for(i=0; i<4; i++) {
monkeys[i]= (monkey* )malloc(sizeof(monkey));
monkeys[i]->weight=i*10;
if (i==2)
monkeys[i]->weight=1;
}
for (i=0 ; i<4; i++)
printf("\nchar %d = %i",i, monkeys[i]->weight);
qsort((void* ) monkeys, 4, sizeof(monkey* ), sort_monkeys);
for (i=0 ; i<4; i++) // now in order?
printf("\nmonkey %d = %i",i, monkeys[i]->weight);
return 0;
}
// Sorted by weight
int sort_monkeys( const void* a, const void* b) {
if((**((monkey** )a)).weight < (**((monkey** )b)).weight) return -1 ;
else if ((**((monkey** )a)).weight > (**((monkey** )b)).weight ) return 1 ;
else return 0 ;
}
Any pointer type may be assigned to a void*, this is useful in cases where a function does not need to know the type, or the type information is conveyed by other means. This allows you to write just one function to deal with any pointer type rather than a separate function for each data type.
While you cannot dereference a void* you can cast it to any type and dereference it - the semantics of that - i.e. whether it is meaningful, depends on the code and is not enforced byte compiler.
Frequently a generic function is not interested in the content of some block of data, just its address and often its size.
As a simple example:
void memcopy( void* to, void* from, int length )
{
char* source = (char*)from ;
char* dest = (char*)to ;
int i ;
for( i = 0; i < lengt; i++ )
{
dest[i] = source[i] ;
}
}
int main()
{
typedef struct
{
int x ;
int y ;
} tItem
tItem AllItems[256] = {0} ;
tItem AllItemsCopy[256] ;
memcopy( AllItemsCopy, AllItems, sizeof(AllItems) ) ;
}
See that memcopy() does not need to know what a tItem is in order to copy an array of them, it only needs to know the addresses and the size on the array in bytes. It casts the void* pointer arguments to reinterpret the data as a char array to perform a byte-by-byte copy. To do that it does not need to know the internal semantics of tItem or any other data object passed to it.

Pointer type casting altering unintended memory

#define ARRAY_SIZE 20
float DataSource[ARRAY_SIZE];
void Read(unsigned char const *Source, unsigned char *Destination, unsigned long DataSize)
{
for ( unsigned long i = 0; i < DataSize; i++)
{
*(Destination + i*DataSize) = *(Source + i*DataSize);
}
}
void fun()
{
int Index;
float Dest;
for ( Index = 0; Index < ARRAY_SIZE; Index++ )
{
Read((unsigned char *)&DataSource[Index], (unsigned char *)&Dest, sizeof(DataSource[Index]));
}
}
I'm having an issue with the above code where upon calling Read(), my Index variable gets overwritten and I am certain the ugly pointer casting is the culprit, but I'm having trouble understanding exactly what is happening here.
The unsigned char pointer types are mandatory because the above code is intended to simulate some driver level software and maintain the same prototype.
Can someone help me to understand the issue here? All the above code is changeable except for the prototype of Read().
The error is here:
for ( unsigned long i = 0; i < DataSize; i++)
{
// vvvvvvvvvv vvvvvvvvvv
*(Destination + i*DataSize) = *(Source + i*DataSize);
}
i * DataSize is always greater than i => "out of bound" access.
Replace with:
for ( unsigned long i = 0; i < DataSize; i++)
{
*(Destination + i) = *(Source + i);
}
You pass in a single float's address to Read (&Dest) and then proceed to write many valuese to consecutive memory locations. Since you're writing random memory at that point it's not unlikely that it could have overwritten index (and other stuff) because stacks usually grow downwards.
This is wrong:
*(Destination + i*DataSize) = *(Source + i*DataSize);
You want to copy DataSize adjacent bytes, not bytes DataSize apart (total span DataSize*DataSize)
Just say
Destination[i] = Source[i];
An amusing (to me) C++ way.
template<typename Data>
struct MemBlockRefHelper {
typedef Data value_type;
Data* data;
size_t size;
MemBlockRefHelper( Data* d, size_t s):data(d), size(s) {}
template<typename Target, typename Other=typename Target::value_type>
Target& Assign( MemBlockRefHelper<Other> const& other ) {
Assert(size == other.size);
for (size_t i = 0; i < size; ++i) {
if (i < other.size) {
data[i] = other.data[i];
} else {
data[i] = 0;
}
}
Target* self = static_cast<Target*>(this);
return *self;
}
};
struct MemBlockRef;
struct MemBlockCRef:MemBlockRefHelper<const unsigned char> {
MemBlockCRef( const unsigned char* d, size_t s ):MemBlockRefHelper<const unsigned char>( d, s ) {}
MemBlockCRef( const MemBlockRef& other );
};
struct MemBlockRef:MemBlockRefHelper<unsigned char> {
MemBlockRef( unsigned char* d, size_t s ):MemBlockRefHelper<unsigned char>( d, s ) {}
MemBlockRef& operator=( MemBlockRef const& other ) {
return Assign< MemBlockRef >( other );
}
MemBlockRef& operator=( MemBlockCRef const& other ) {
return Assign< MemBlockRef, const unsigned char >( other );
}
};
inline MemBlockCRef::MemBlockCRef( const MemBlockRef& other ): MemBlockRefHelper<const unsigned char>( other.data, other.size ) {}
void Read( unsigned char const* Source, unsigned char* Dest, unsigned long DataSize ) {
MemBlockCRef src( Source, DataSize );
MemBlockRef dest( Dest, DataSize );
dest = src;
}
massively over engineered, but the idea is to wrap up the idea of a block of POD memory of a certain size, and provide reference semantics to its contents (initialization is creating a new reference to the same data, assignment does a copy over the referred to data).
Once you have such classes, the code for Read becomes a 3 liner. Well, you can do it in one:
MemBlockRef( Dest, DataSize ) = MemBlockCRef( Source, DataSize );
but that is needless.
Well, so it this entire framework.
But I was amused by writing it.
Let's take a closer look at your Read(): i changes from 0 to DataSize-1; each time you access memory by an offset of i*DataSize... that is, by an offset from 0 to DataSize*(DataSize-1). Looks wrong, as DataSize**2-DataSize makes no sense.
Unlike other answers, I don't want to guess what you wanted. Just showing a kind of "dimensional analysis" that can help spotting the wrongest part of code without reading the author's mind.
You are treating the scalar variable Dest declared inside fun() as an array inside Read(). It seems that both Dest and your Index variable are placed adjacent on the stack which explains that Index gets overwritten exactly when the loop inside Read() is executed for i==1.
So the solution is: declare Dest as an array, too:
float Dest[ARRAY_SIZE];

How to convert a string into an arbitrary length integer

I'm working on a project to implement multi-precision arithmetic in C++. I've sort of fallen at the first hurdle. I'm trying to convert a c-string into the binary representation of an integer that will probably contain more bits than an int can hold (this may be an arbitrary number of bits in theory). I essentially want to create an array of longs that will hold the binary representation of the number contained in the string, with index 0 being the least significant "limb" of the number. I am assuming that the number is in base ten.
I have already looked into using code from GMP but it is needlessly complex for my needs and has huge amounts of platform dependent code.
Any help would be great! If you require more details let me know.
Like #SteveJessop said
class Number {
public:
Number();
void FromString( const char * );
void operator *= ( int );
void operator += ( int );
void operator = ( int );
}
Number::FromString( const char * string )
{
*this = 0;
while( *string != '\0' ) {
*this *= 10;
*this += *string - '0';
string++;
}
}
The first thing you want to do is have a working test engine. This is a brain-dead, easy to understand, arbitrary precision arithmetic engine.
The purpose of this engine is a few fold. First, it makes converting strings into arbitrary precision integers really easy. Second, it is a means to test your later, improved engines. Even if it is really slow, you'll be more convinced it is correct (and having two independent implementations means corner case errors in one might be caught in another, even if you aren't more confident in either).
Assumes short is at least 16 bits and char is at least 8 (use the actual int_8 style types if your compiler supports them)
short Add(unsigned char left, unsigned char right, unsigned char extra=0) { return unsigned short(left)+unsigned short(right)+unsigned short(extra); }
unsigned short Multiply(unsigned char left, unsigned char right) { return unsigned short(left)*unsigned short(right); }
std::pair<unsigned char,unsigned char> CarryCalc(unsigned short input) {
std::pair<unsigned char,unsigned char> retval;
retval.first = input & (1<<8-1);
retval.second = input>>8;
return retval;
}
struct BigNum {
std::vector<char> base256;
BigNum& operator+=( BigNum const& right ) {
if (right.base256.size() > base256.size())
base256.resize(right.base256.size());
auto lhs = base256.begin();
auto rhs = right.base256.begin();
char carry = 0;
for(; rhs != right.base256.end(); ++rhs, ++lhs) {
auto result = CarryCalc( Add( *lhs, *rhs, carry ) );
*lhs = result.first;
carry = result.second;
}
while( carry && lhs != base256.end() ) {
auto result = CarryCalc( Add( *lhs, 0, carry ) );
*lhs = result.first;
carry = result.second;
}
if (carry)
base256.push_back(carry);
return *this;
}
BigNum& scaleByChar( unsigned char right ) {
char carry = 0;
for(auto lhs = base256.begin(); lhs != base256.end(); ++lhs) {
unsigned short product = Multiply( *lhs, right );
product += carry;
auto result = CarryCalc( product );
*lhs = result.first;
carry = result.second;
}
if (carry)
base256.push_back(carry);
return *this;
}
BigNum& shiftRightBy8BitsTimes( unsigned int x ) {
if (x > base256.size()) {
base256.clear();
return *this;
}
base256.erase( base256.begin(), base256.begin()+x) )
return *this;
}
// very slow, O(x * n) -- should be O(n) at worst
BigNum& shiftLeftBy8BitsTimes( unsigned int x ) {
while( x != 0 ) {
base256.insert( base256.begin(), 0 );
--x;
}
return *this;
}
// very slow, like O(n^3) or worse (should be O(n^2) at worst, fix shiftLeft)
BigNum& operator*=( BigNum const& right ) {
unsigned int digit = 0;
BigNum retval;
while (digit < right.base256.size()) {
BigNum tmp = *this;
tmp.shiftLeftBy8BitsTimes( digit );
tmp.scaleByChar( right.base256[digit] );
retval += tmp;
++digit;
}
*this = retval;
return *this;
}
};
which is a quick and dirty arbitrary precision integer type (not even compiled yet) with horrible performance. Test something like the above, convince yourself it is solid, then build up from there.
Much of your code could take the actual BigNum class in question as a template argument, so you can do the same algorithm with two different implementations, and compare the results for testing purposes.
Oh, and another piece of advice -- write a template class that "improves" a bare-bones arbitrary precision library via CRTP. The goal is to only have to write *=, +=, unary -, and maybe /= and some shift_helper and compare_helper functions, and have the rest of your methods automatically written for you by the template. By putting the boilerplate in one spot it makes it easier to maintain more than one version of your BigNum class: and having more than one version is very important for testing purposes.

efficent way to save objects into binary files

I've a class that consists basically of a matrix of vectors: vector< MyFeatVector<T> > m_vCells, where the outer vector represents the matrix. Each element in this matrix is then a vector (I extended the stl vector class and named it MyFeatVector<T>).
I'm trying to code an efficient method to store objects of this class in binary files.
Up to now, I require three nested loops:
foutput.write( reinterpret_cast<char*>( &(this->at(dy,dx,dz)) ), sizeof(T) );
where this->at(dy,dx,dz) retrieves the dz element of the vector at position [dy,dx].
Is there any possibility to store the m_vCells private member without using loops? I tried something like: foutput.write(reinterpret_cast<char*>(&(this->m_vCells[0])), (this->m_vCells.size())*sizeof(CFeatureVector<T>)); which seems not to work correctly. We can assume that all the vectors in this matrix have the same size, although a more general solution is also welcomed :-)
Furthermore, following my nested-loop implementation, storing objects of this class in binary files seem to require more physical space than storing the same objects in plain-text files. Which is a bit weird.
I was trying to follow the suggestion under http://forum.allaboutcircuits.com/showthread.php?t=16465 but couldn't arrive into a proper solution.
Thanks!
Below a simplified example of my serialization and 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 most efficient method is to store the objects into an array (or contiguous space), then blast the buffer to the file. An advantage is that the disk platters don't have waste time ramping up and also the writing can be performed contiguously instead of in random locations.
If this is your performance bottleneck, you may want to consider using multiple threads, one extra thread to handle the output. Dump the objects into a buffer, set a flag, then the writing thread will handle the output, releaving your main task to perform more important tasks.
Edit 1: Serializing Example
The following code has not been compiled and is for 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 the buffer
unsigned char * buffer = new unsigned char [buffer_size];
unsigned char * buf_ptr = buffer;
// Write / store the object into the buffer.
dog.store_to_buffer(buf_ptr);
// Write the buffer to the file / stream.
data_file.write((char *) buffer, buffer_size);
data_file.close();
delete [] buffer;
return 0;
}
Edit 2: A class with a 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); // with room for the quantity variable.
}
void store_to_buffer(unsigned char *& buf_ptr) const
{
// Treat the vector<string> as a variable length field.
// Store the quantity of strings into the buffer,
// followed by the content.
size_t string_quantity = m_string_container.size();
*((size_t *) buf_ptr) = string_quantity;
buf_ptr += sizeof(size_t);
for (size_t i = 0; i < string_quantity; ++i)
{
// Each string is a fixed length field.
// Pad with '\0' first, then copy the 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)
{
// The actual coding is left as an exercise for the reader.
// Psuedo code:
// Clear / empty the string container.
// load the quantity variable.
// increment the buffer variable by the size of the quantity variable.
// for each new string (up to the quantity just read)
// load a temporary string from the buffer via buffer pointer.
// push the temporary string into the vector
// increment the buffer pointer by the MAX_STRING_SIZE.
// end-for
}
std::vector<std::string> m_string_container;
};
I'd suggest you to read C++ FAQ on Serialization and you can choose what best fits for your
When you're working with structures and classes, you've to take care of two things
Pointers inside the class
Padding bytes
Both of these could make some notorious results in your output. IMO, the object must implement to serialize and de-serialize the object. The object can know well about the structures, pointers data etc. So it can decide which format can be implemented efficiently.
You will have to iterate anyway or has to wrap it somewhere. Once you finished implementing the serialization and de-serialization function (either you can write using operators or functions). Especially when you're working with stream objects, overloading << and >> operators would be easy to pass the object.
Regarding your question about using underlying pointers of vector, it might work if it's a single vector. But it's not a good idea in the other way.
Update according to the question update.
There are few things you should mind before overriding STL members. They're not really a good candidate for inheritance because it doesn't have any virtual destructors. If you're using basic data types and POD like structures it wont make much issues. But if you use it truly object oriented way, you may face some unpleasant behavior.
Regarding your code
Why you're typecasting it to char*?
The way you serialize the object is your choice. IMO what you did is a basic file write operation in the name of serialization.
Serialization is down to the object. i.e the parameter 'T' in your template class. If you're using POD, or basic types no need of special synchronization. Otherwise you've to carefully choose the way to write the object.
Choosing text format or binary format is your choice. Text format has always has a cost at the same time it's easy to manipulate it rather than binary format.
For example the following code is for simple read and write operation( in text format).
fstream fr("test.txt", ios_base::out | ios_base::binary );
for( int i =0;i <_countof(arr);i++)
fr << arr[i] << ' ';
fr.close();
fstream fw("test.txt", ios_base::in| ios_base::binary);
int j = 0;
while( fw.eof() || j < _countof(arrout))
{
fw >> arrout[j++];
}
It seems to me, that the most direct root to generate a binary file containing a vector is to memory map the file and place it in the mapped region. As pointed out by sarat, you need to worry about how pointers are used within the class. But, boost-interprocess library has a tutorial on how to do this using their shared memory regions which include memory mapped files.
First off, have you looked at Boost.multi_array? Always good to take something ready-made rather than reinventing the wheel.
That said, I'm not sure if this is helpful, but here's how I would implement the basic data structure, and it'd be fairly easy to serialize:
#include <array>
template <typename T, size_t DIM1, size_t DIM2, size_t DIM3>
class ThreeDArray
{
typedef std::array<T, DIM1 * DIM2 * DIM3> array_t;
array_t m_data;
public:
inline size_t size() const { return data.size(); }
inline size_t byte_size() const { return sizeof(T) * data.size(); }
inline T & operator()(size_t i, size_t j, size_t k)
{
return m_data[i + j * DIM1 + k * DIM1 * DIM2];
}
inline const T & operator()(size_t i, size_t j, size_t k) const
{
return m_data[i + j * DIM1 + k * DIM1 * DIM2];
}
inline const T * data() const { return m_data.data(); }
};
You can serialize the data buffer directly:
ThreeDArray<int, 4, 6 11> arr;
/* ... */
std::ofstream outfile("file.bin");
outfile.write(reinterpret_cast<char*>(arr.data()), arr.byte_size());