What's wrong with this code ? (struct serialization to raw memoryblock)

A

Alfonso Morra

Hi,

I am at the end of my tether now - after spending several days trying to
figure how to do this. I have finally written a simple "proof of
concept" program to test serializing a structure containing pointers
into a "flattened" bit stream.

Here is my code (it dosen't work - compiles fine, pack appears to work,
but unpack retrieves jibberish and causes program to crash).

I would be grateful for any feedback that helps fix this. My intention
is to build on this example, and use the ideas here, to be able to
persist any data structure (I'll write different pack/unpack routines
for different data stuctures, just to keep things simple). Anyway,
here's the code:


#include "stdlib.h"
#include "stdio.h"
#include "string.h"


typedef struct {
int l ;
double d ;
char* s; /* Null terminated string */
} MyStruct ;


void * pack(size_t *size, MyStruct* m);
MyStruct *unpack(void* block);


int main(int argc, char* argv[]) {

MyStruct *in = (MyStruct*)malloc(sizeof(*in)), *out = NULL;
unsigned char *memblock = NULL ;
size_t size ;

in->l = 1000 ;
in->d = 3.142857;
in->s = strdup("Simple Text" );

memblock = (unsigned char*)pack(&size, in) ;
out = unpack(memblock) ;

printf("Int member has value : %d (expected : %d)", out->l, in->l ) ;
printf("Double member has value : %f (expected : %f)", out->d, in->d ) ;
printf("Int member has value : %s (expected : %s)", out->s, in->s ) ;

free(in->s) ;
free(in) ;
free(out->s) ;
free(out) ;

}




void * pack(size_t *size, MyStruct* m) {
unsigned char *buff = NULL ;
size_t len, length ;

length = strlen(m->s) ;
len = sizeof(int) + sizeof(double) + sizeof(size_t) +
(length+1)*sizeof(char) ;
buff = (unsigned char*)malloc(len) ;

/*copy int*/
memmove(buff, &(m->l), sizeof(int)) ;
/*copy double*/
memmove(buff + sizeof(int), &(m->d), sizeof(double)) ;
/*store length of string*/
memmove(buff + sizeof(int) + sizeof(int), &length, sizeof(size_t));
/*copy string*/
memmove(buff + sizeof(int) + sizeof(double), m->s,
(length+1)*sizeof(char)) ;

*size = len ;
return buff ;
}


MyStruct *unpack(void* block) {
int l, len ;
double d ;
char * s = NULL ;
MyStruct *p = NULL ;

/* get int*/
memmove(&l, block, sizeof(int)) ;
/* get double*/
memmove(&d, (unsigned char*)block + sizeof(int), sizeof(double)) ;
/* get string length*/
memmove(&len, (unsigned char*)block + sizeof(int) + sizeof(double),
sizeof(size_t)) ;
/* get string*/
s = (char*)malloc(len+1) ;
memmove(s,(unsigned char*)block + sizeof(int) + sizeof(double)+
sizeof(size_t),len) ;

p = (MyStruct*)malloc(sizeof(*p)) ;

p->l = l ;
p->d = d ;
p->s = s ;

/* free resource */
free(block) ;
block = NULL ;
return p ;
}
 
D

David Hilsee

Alfonso Morra said:
Hi,

I am at the end of my tether now - after spending several days trying to
figure how to do this. I have finally written a simple "proof of
concept" program to test serializing a structure containing pointers
into a "flattened" bit stream.

Here is my code (it dosen't work - compiles fine, pack appears to work,
but unpack retrieves jibberish and causes program to crash).

I would be grateful for any feedback that helps fix this. My intention
is to build on this example, and use the ideas here, to be able to
persist any data structure (I'll write different pack/unpack routines
for different data stuctures, just to keep things simple). Anyway,
here's the code:


#include "stdlib.h"
#include "stdio.h"
#include "string.h"


typedef struct {
int l ;
double d ;
char* s; /* Null terminated string */
} MyStruct ;


void * pack(size_t *size, MyStruct* m);
MyStruct *unpack(void* block);


int main(int argc, char* argv[]) {

MyStruct *in = (MyStruct*)malloc(sizeof(*in)), *out = NULL;
unsigned char *memblock = NULL ;
size_t size ;

in->l = 1000 ;
in->d = 3.142857;
in->s = strdup("Simple Text" );

memblock = (unsigned char*)pack(&size, in) ;
out = unpack(memblock) ;

printf("Int member has value : %d (expected : %d)", out->l, in->l ) ;
printf("Double member has value : %f (expected : %f)", out->d, in->d ) ;
printf("Int member has value : %s (expected : %s)", out->s, in->s ) ;

free(in->s) ;
free(in) ;
free(out->s) ;
free(out) ;

}




void * pack(size_t *size, MyStruct* m) {
unsigned char *buff = NULL ;
size_t len, length ;

length = strlen(m->s) ;
len = sizeof(int) + sizeof(double) + sizeof(size_t) +
(length+1)*sizeof(char) ;
buff = (unsigned char*)malloc(len) ;

/*copy int*/
memmove(buff, &(m->l), sizeof(int)) ;
/*copy double*/
memmove(buff + sizeof(int), &(m->d), sizeof(double)) ;
/*store length of string*/
memmove(buff + sizeof(int) + sizeof(int), &length, sizeof(size_t));

memmove(buff + sizeof(int) + sizeof(double), &length, sizeof(size_t));
/*copy string*/
memmove(buff + sizeof(int) + sizeof(double), m->s,
(length+1)*sizeof(char)) ;

memmove(buff + sizeof(int) + sizeof(double) + sizeof(size_t), m->s,
(length+1)*sizeof(char)) ;
*size = len ;
return buff ;
}


MyStruct *unpack(void* block) {
int l, len ;
double d ;
char * s = NULL ;
MyStruct *p = NULL ;

/* get int*/
memmove(&l, block, sizeof(int)) ;
/* get double*/
memmove(&d, (unsigned char*)block + sizeof(int), sizeof(double)) ;
/* get string length*/
memmove(&len, (unsigned char*)block + sizeof(int) + sizeof(double),
sizeof(size_t)) ;
/* get string*/
s = (char*)malloc(len+1) ;

memmove(s,(unsigned char*)block + sizeof(int) + sizeof(double)+
sizeof(size_t),len) ;

memmove(s,(unsigned char*)block + sizeof(int) + sizeof(double)+
sizeof(size_t),len+1) ;
p = (MyStruct*)malloc(sizeof(*p)) ;

p->l = l ;
p->d = d ;
p->s = s ;

/* free resource */
free(block) ;
block = NULL ;
return p ;
}

BTW, I don't see any reason to prefer memmove() to memcpy().
 
A

Alfonso Morra

David said:
Hi,

I am at the end of my tether now - after spending several days trying to
figure how to do this. I have finally written a simple "proof of
concept" program to test serializing a structure containing pointers
into a "flattened" bit stream.

Here is my code (it dosen't work - compiles fine, pack appears to work,
but unpack retrieves jibberish and causes program to crash).

I would be grateful for any feedback that helps fix this. My intention
is to build on this example, and use the ideas here, to be able to
persist any data structure (I'll write different pack/unpack routines
for different data stuctures, just to keep things simple). Anyway,
here's the code:


#include "stdlib.h"
#include "stdio.h"
#include "string.h"


typedef struct {
int l ;
double d ;
char* s; /* Null terminated string */
} MyStruct ;


void * pack(size_t *size, MyStruct* m);
MyStruct *unpack(void* block);


int main(int argc, char* argv[]) {

MyStruct *in = (MyStruct*)malloc(sizeof(*in)), *out = NULL;
unsigned char *memblock = NULL ;
size_t size ;

in->l = 1000 ;
in->d = 3.142857;
in->s = strdup("Simple Text" );

memblock = (unsigned char*)pack(&size, in) ;
out = unpack(memblock) ;

printf("Int member has value : %d (expected : %d)", out->l, in->l ) ;
printf("Double member has value : %f (expected : %f)", out->d, in->d ) ;
printf("Int member has value : %s (expected : %s)", out->s, in->s ) ;

free(in->s) ;
free(in) ;
free(out->s) ;
free(out) ;

}




void * pack(size_t *size, MyStruct* m) {
unsigned char *buff = NULL ;
size_t len, length ;

length = strlen(m->s) ;
len = sizeof(int) + sizeof(double) + sizeof(size_t) +
(length+1)*sizeof(char) ;
buff = (unsigned char*)malloc(len) ;

/*copy int*/
memmove(buff, &(m->l), sizeof(int)) ;
/*copy double*/
memmove(buff + sizeof(int), &(m->d), sizeof(double)) ;
/*store length of string*/
memmove(buff + sizeof(int) + sizeof(int), &length, sizeof(size_t));


memmove(buff + sizeof(int) + sizeof(double), &length, sizeof(size_t));

/*copy string*/
memmove(buff + sizeof(int) + sizeof(double), m->s,
(length+1)*sizeof(char)) ;


memmove(buff + sizeof(int) + sizeof(double) + sizeof(size_t), m->s,
(length+1)*sizeof(char)) ;

*size = len ;
return buff ;
}


MyStruct *unpack(void* block) {
int l, len ;
double d ;
char * s = NULL ;
MyStruct *p = NULL ;

/* get int*/
memmove(&l, block, sizeof(int)) ;
/* get double*/
memmove(&d, (unsigned char*)block + sizeof(int), sizeof(double)) ;
/* get string length*/
memmove(&len, (unsigned char*)block + sizeof(int) + sizeof(double),
sizeof(size_t)) ;
/* get string*/
s = (char*)malloc(len+1) ;

memmove(s,(unsigned char*)block + sizeof(int) + sizeof(double)+
sizeof(size_t),len) ;


memmove(s,(unsigned char*)block + sizeof(int) + sizeof(double)+
sizeof(size_t),len+1) ;

p = (MyStruct*)malloc(sizeof(*p)) ;

p->l = l ;
p->d = d ;
p->s = s ;

/* free resource */
free(block) ;
block = NULL ;
return p ;
}


BTW, I don't see any reason to prefer memmove() to memcpy().

Many, many thanks David - your help is much appreciated !
 
G

Greg

Alfonso said:
Hi,

I am at the end of my tether now - after spending several days trying to
figure how to do this. I have finally written a simple "proof of
concept" program to test serializing a structure containing pointers
into a "flattened" bit stream.

Here is my code (it dosen't work - compiles fine, pack appears to work,
but unpack retrieves jibberish and causes program to crash).

I would be grateful for any feedback that helps fix this. My intention
is to build on this example, and use the ideas here, to be able to
persist any data structure (I'll write different pack/unpack routines
for different data stuctures, just to keep things simple). Anyway,
here's the code:


#include "stdlib.h"
#include "stdio.h"
#include "string.h"


typedef struct {
int l ;
double d ;
char* s; /* Null terminated string */
} MyStruct ;


void * pack(size_t *size, MyStruct* m);
MyStruct *unpack(void* block);


int main(int argc, char* argv[]) {

MyStruct *in = (MyStruct*)malloc(sizeof(*in)), *out = NULL;
unsigned char *memblock = NULL ;
size_t size ;

in->l = 1000 ;
in->d = 3.142857;
in->s = strdup("Simple Text" );

memblock = (unsigned char*)pack(&size, in) ;
out = unpack(memblock) ;

printf("Int member has value : %d (expected : %d)", out->l, in->l ) ;
printf("Double member has value : %f (expected : %f)", out->d, in->d ) ;
printf("Int member has value : %s (expected : %s)", out->s, in->s ) ;

free(in->s) ;
free(in) ;
free(out->s) ;
free(out) ;

}




void * pack(size_t *size, MyStruct* m) {
unsigned char *buff = NULL ;
size_t len, length ;

length = strlen(m->s) ;
len = sizeof(int) + sizeof(double) + sizeof(size_t) +
(length+1)*sizeof(char) ;
buff = (unsigned char*)malloc(len) ;

/*copy int*/
memmove(buff, &(m->l), sizeof(int)) ;
/*copy double*/
memmove(buff + sizeof(int), &(m->d), sizeof(double)) ;
/*store length of string*/
memmove(buff + sizeof(int) + sizeof(int), &length, sizeof(size_t));
/*copy string*/
memmove(buff + sizeof(int) + sizeof(double), m->s,
(length+1)*sizeof(char)) ;

*size = len ;
return buff ;
}


MyStruct *unpack(void* block) {
int l, len ;
double d ;
char * s = NULL ;
MyStruct *p = NULL ;

/* get int*/
memmove(&l, block, sizeof(int)) ;
/* get double*/
memmove(&d, (unsigned char*)block + sizeof(int), sizeof(double)) ;
/* get string length*/
memmove(&len, (unsigned char*)block + sizeof(int) + sizeof(double),
sizeof(size_t)) ;
/* get string*/
s = (char*)malloc(len+1) ;
memmove(s,(unsigned char*)block + sizeof(int) + sizeof(double)+
sizeof(size_t),len) ;

p = (MyStruct*)malloc(sizeof(*p)) ;

p->l = l ;
p->d = d ;
p->s = s ;

/* free resource */
free(block) ;
block = NULL ;
return p ;
}

The members of a struct are not necessarily packed into the smallest
space that will hold them. Usually the compiler will place the
larger-sized elements at 4 or 8 or even 16 byte offsets and insert
bytes to "pad" the space between fields of a struct if necessary. Using
"sizeof" to calculate the field offsets is not reliable or likely even
to be correct. There is an "offset" macro that will report the actual
offset of a field in a struct by substracting the address of the struct
from the address of the field. But then the routine to unpack the data
must be compiled with the same alignment settings as the program that
packed the data, and that is not a safe assumption to make either.

Alternately, you just use a BER (ASN.1) library to encode the data. And
then there is always XML to encode the data in a very portable format.

Greg
 
A

Alfonso Morra

Greg said:
The members of a struct are not necessarily packed into the smallest
space that will hold them. Usually the compiler will place the
larger-sized elements at 4 or 8 or even 16 byte offsets and insert
bytes to "pad" the space between fields of a struct if necessary. Using
"sizeof" to calculate the field offsets is not reliable or likely even
to be correct. There is an "offset" macro that will report the actual
offset of a field in a struct by substracting the address of the struct
from the address of the field. But then the routine to unpack the data
must be compiled with the same alignment settings as the program that
packed the data, and that is not a safe assumption to make either.

Alternately, you just use a BER (ASN.1) library to encode the data. And
then there is always XML to encode the data in a very portable format.

Greg

Ok, now you've got me worried ... :-(

I can't use XML. Could you please shed some light on BER (never heard of
it) and offset. How would you modify the code (just a couple of lines to
show the concept) to use the more correct offset macro ?

thanks
 
B

Bob Hairgrove

Hi,

I am at the end of my tether now - after spending several days trying to
figure how to do this. I have finally written a simple "proof of
concept" program to test serializing a structure containing pointers
into a "flattened" bit stream.

Here is my code (it dosen't work - compiles fine, pack appears to work,
but unpack retrieves jibberish and causes program to crash).

I would be grateful for any feedback that helps fix this. My intention
is to build on this example, and use the ideas here, to be able to
persist any data structure (I'll write different pack/unpack routines
for different data stuctures, just to keep things simple). Anyway,
here's the code:


#include "stdlib.h"
#include "stdio.h"
#include "string.h"

First of all, you have posted this to comp.lang.c++, so you should get
used to doing things the C++ way:

#include <cstdlib>
#include <cstdio>
#include said:
typedef struct {
int l ;
double d ;
char* s; /* Null terminated string */
} MyStruct ;

struct MyStruct {
int l ; // terrible names of variables here...
double d ;
const char* s; /* Null terminated string */
};

I think I'll post this "as is" and try to do a rewrite in C++. Once
you see the difference, I think you won't go back to using malloc and
free ever again.
 
D

Default User

First of all, you have posted this to comp.lang.c++, so you should get
used to doing things the C++ way:


He's also been posting it to comp.lang.c. He's trying to come up with
some sort of hybrid solution that works in either language. Others have
pointed out the foolishness of that.




Brian
 
A

Alfonso Morra

Default said:
Bob Hairgrove wrote:





He's also been posting it to comp.lang.c. He's trying to come up with
some sort of hybrid solution that works in either language. Others have
pointed out the foolishness of that.




Brian

You sir, are an idiot.
 
B

Bob Hairgrove

I think I'll post this "as is" and try to do a rewrite in C++. Once
you see the difference, I think you won't go back to using malloc and
free ever again.

As promised. :) OK, below is my version. I tried not to change the
basic structure of your program. It works; however, I would never
write it this way because:

(a) It is not (yet) exception-safe. However, it is a whole lot safer
WRT memory management issues than using new and delete (or malloc and
free);

(b) It does way too little sanity-checking. However, using the STL
containers such as std::string and std::vector make a lot of this
unnecessary;

(c) It is highly inflexible. Your pack() and unpack() functions make
way too many assumptions about how MyStruct looks. Ideally, in C++,
you let an object serialize and deserialize itself because only the
object needs to know about its own structure. But you'll have to read
up more about C++ and OOP first to grasp this.

Look at how short the unpack() function has become. And we get along
fine without a single new or delete -- anywhere!

I haven't added any comments because most of the code now "speaks for
itself". However, I'd be glad to comment later on any parts you might
have questions about.

//~~~~~ test_pack.cpp ~~~~~~~~~~~~~~~~
// caveat: watch out for some line breaks

#include <vector>
#include <string>
#include <iostream>
#include <ostream>

struct MyStruct {
int l ;
double d ;
std::string s;
};

void pack(std::vector<char> &Memblock, MyStruct const &the_struct)
{
size_t block_len = sizeof(int)
+ sizeof(double)
+ the_struct.s.length()+1;
size_t int_offset = sizeof(int);
size_t dbl_offset = sizeof(int) + sizeof(double);

if (!Memblock.empty()) Memblock.clear();
Memblock.resize(block_len);

int * pInt = reinterpret_cast<int*>(&Memblock[0]);
double * pDbl = reinterpret_cast<double*>(&Memblock[int_offset]);
char * pChar = &(Memblock[dbl_offset]);

*pInt = the_struct.l;
*pDbl = the_struct.d;
strcpy(pChar, the_struct.s.c_str());
}

void unpack(MyStruct &the_struct, std::vector<char> const &block)
{
size_t int_offset = sizeof(int);
size_t dbl_offset = sizeof(int) + sizeof(double);

the_struct.l = *(reinterpret_cast<int const *>(&(block[0])));
the_struct.d =
*(reinterpret_cast<double const *>(&(block[int_offset])));
the_struct.s = &(block[dbl_offset]);
}

int main() {

using std::cout;
using std::endl;

MyStruct in, out;
std::vector<char> memblock;

in.l = 1000 ;
in.d = 3.142857;
in.s = "Simple Text";

pack(memblock, in) ;
unpack(out, memblock) ;

cout << "Int member has value : " << out.l
<< " (expected : " << in.l << ")\n";
cout << "Double member has value : " << out.d
<< " (expected : " << in.d << ")\n";
cout << "String member has value : " << out.s
<< " (expected : " << in.s << ")\n";
return 0;
}
 
B

Bob Hairgrove

Also, note how the pack() and unpack() functions work independently of
any quirks WRT struct member padding. We control the layout of the raw
memory block, and the compiler takes care of the struct ... as it
should be.

When we cross the boundary with memmove() etc., we can get into
trouble. But as you can see, it isn't necessary.
 
A

Alfonso Morra

Bob said:
I think I'll post this "as is" and try to do a rewrite in C++. Once
you see the difference, I think you won't go back to using malloc and
free ever again.


As promised. :) OK, below is my version. I tried not to change the
basic structure of your program. It works; however, I would never
write it this way because:

(a) It is not (yet) exception-safe. However, it is a whole lot safer
WRT memory management issues than using new and delete (or malloc and
free);

(b) It does way too little sanity-checking. However, using the STL
containers such as std::string and std::vector make a lot of this
unnecessary;

(c) It is highly inflexible. Your pack() and unpack() functions make
way too many assumptions about how MyStruct looks. Ideally, in C++,
you let an object serialize and deserialize itself because only the
object needs to know about its own structure. But you'll have to read
up more about C++ and OOP first to grasp this.

Look at how short the unpack() function has become. And we get along
fine without a single new or delete -- anywhere!

I haven't added any comments because most of the code now "speaks for
itself". However, I'd be glad to comment later on any parts you might
have questions about.

//~~~~~ test_pack.cpp ~~~~~~~~~~~~~~~~
// caveat: watch out for some line breaks

#include <vector>
#include <string>
#include <iostream>
#include <ostream>

struct MyStruct {
int l ;
double d ;
std::string s;
};

void pack(std::vector<char> &Memblock, MyStruct const &the_struct)
{
size_t block_len = sizeof(int)
+ sizeof(double)
+ the_struct.s.length()+1;
size_t int_offset = sizeof(int);
size_t dbl_offset = sizeof(int) + sizeof(double);

if (!Memblock.empty()) Memblock.clear();
Memblock.resize(block_len);

int * pInt = reinterpret_cast<int*>(&Memblock[0]);
double * pDbl = reinterpret_cast<double*>(&Memblock[int_offset]);
char * pChar = &(Memblock[dbl_offset]);

*pInt = the_struct.l;
*pDbl = the_struct.d;
strcpy(pChar, the_struct.s.c_str());
}

void unpack(MyStruct &the_struct, std::vector<char> const &block)
{
size_t int_offset = sizeof(int);
size_t dbl_offset = sizeof(int) + sizeof(double);

the_struct.l = *(reinterpret_cast<int const *>(&(block[0])));
the_struct.d =
*(reinterpret_cast<double const *>(&(block[int_offset])));
the_struct.s = &(block[dbl_offset]);
}

int main() {

using std::cout;
using std::endl;

MyStruct in, out;
std::vector<char> memblock;

in.l = 1000 ;
in.d = 3.142857;
in.s = "Simple Text";

pack(memblock, in) ;
unpack(out, memblock) ;

cout << "Int member has value : " << out.l
<< " (expected : " << in.l << ")\n";
cout << "Double member has value : " << out.d
<< " (expected : " << in.d << ")\n";
cout << "String member has value : " << out.s
<< " (expected : " << in.s << ")\n";
return 0;
}


Hi Bob,

This is indeed very elegant. Thanks for your help/effort. I shall peruse
through the code and add exception handling etc. I especially like the
fact that not many asumptions are made about MyStruct - although I AM a
little concerned about the liberal sprinkling of reinterpret casts ...

very good nonetheless. Thanks
 
A

Alfonso Morra

Bob said:
I think I'll post this "as is" and try to do a rewrite in C++. Once
you see the difference, I think you won't go back to using malloc and
free ever again.


As promised. :) OK, below is my version. I tried not to change the
basic structure of your program. It works; however, I would never
write it this way because:

(a) It is not (yet) exception-safe. However, it is a whole lot safer
WRT memory management issues than using new and delete (or malloc and
free);

(b) It does way too little sanity-checking. However, using the STL
containers such as std::string and std::vector make a lot of this
unnecessary;

(c) It is highly inflexible. Your pack() and unpack() functions make
way too many assumptions about how MyStruct looks. Ideally, in C++,
you let an object serialize and deserialize itself because only the
object needs to know about its own structure. But you'll have to read
up more about C++ and OOP first to grasp this.

Look at how short the unpack() function has become. And we get along
fine without a single new or delete -- anywhere!

I haven't added any comments because most of the code now "speaks for
itself". However, I'd be glad to comment later on any parts you might
have questions about.

//~~~~~ test_pack.cpp ~~~~~~~~~~~~~~~~
// caveat: watch out for some line breaks

#include <vector>
#include <string>
#include <iostream>
#include <ostream>

struct MyStruct {
int l ;
double d ;
std::string s;
};

void pack(std::vector<char> &Memblock, MyStruct const &the_struct)
{
size_t block_len = sizeof(int)
+ sizeof(double)
+ the_struct.s.length()+1;
size_t int_offset = sizeof(int);
size_t dbl_offset = sizeof(int) + sizeof(double);

if (!Memblock.empty()) Memblock.clear();
Memblock.resize(block_len);

int * pInt = reinterpret_cast<int*>(&Memblock[0]);
double * pDbl = reinterpret_cast<double*>(&Memblock[int_offset]);
char * pChar = &(Memblock[dbl_offset]);

*pInt = the_struct.l;
*pDbl = the_struct.d;
strcpy(pChar, the_struct.s.c_str());
}

void unpack(MyStruct &the_struct, std::vector<char> const &block)
{
size_t int_offset = sizeof(int);
size_t dbl_offset = sizeof(int) + sizeof(double);

the_struct.l = *(reinterpret_cast<int const *>(&(block[0])));
the_struct.d =
*(reinterpret_cast<double const *>(&(block[int_offset])));
the_struct.s = &(block[dbl_offset]);
}

int main() {

using std::cout;
using std::endl;

MyStruct in, out;
std::vector<char> memblock;

in.l = 1000 ;
in.d = 3.142857;
in.s = "Simple Text";

pack(memblock, in) ;
unpack(out, memblock) ;

cout << "Int member has value : " << out.l
<< " (expected : " << in.l << ")\n";
cout << "Double member has value : " << out.d
<< " (expected : " << in.d << ")\n";
cout << "String member has value : " << out.s
<< " (expected : " << in.s << ")\n";
return 0;
}

one last thing though - it may be better (safer) to use the offsetof
macro to calculate the offsets - because of any padding that the
compiler may have inserted, to align the struct fields.
 
B

Bob Hairgrove

one last thing though - it may be better (safer) to use the offsetof
macro to calculate the offsets - because of any padding that the
compiler may have inserted, to align the struct fields.

I think you need to look at the code once more, and exactly what it
does. There is absolutely no place in it at all where we need to know
(or worry about) how the compiler puts a MyStruct in memory. The
offsets are solely used BY US to place some binary bits in a raw
memory buffer which has nothing to do with how the compiler aligns or
pads anything. We read the data exactly the same way as we write it,
but we use normal assignment operations to get it there and back.
There is no memcpy() etc.

sizeof(<whatever>), of course, will possibly be different on different
platforms. This is where things like XML come in handy.
 
B

Bob Hairgrove

I especially like the
fact that not many asumptions are made about MyStruct - although I AM a
little concerned about the liberal sprinkling of reinterpret casts ...

Well, pack() and unpack() certainly DO make many assumtions about
MyStruct. For example, if you change MyStruct and add a member, you'll
have to re-write these functions -- both of them. You have a very
tight dependency on it.

As to reinterpret_cast, it is not much different than C-style casts
which you seem to have no qualms about using, although they are
potentially much more dangerous. If you need to cast between unrelated
pointer types, as we do here, or pointers and int, use
reinterpret_cast (with void pointers you can also use static_cast).
There are situations where you just have to cast, espcially when doing
things involving raw memory or wrapping legacy C code in C++ classes.

The "shock and awe" <g> effect of seeing these long cast names is very
effective because it is so easy to overlook a C-style cast when trying
to maintain the code. And since it potentially involves more typing,
it makes you try to find ways of doing things without resorting to a
cast in the first place, thus resulting in improved code.

I recommend you get Scott Meyers' books "Effective C++", "More
Effective C++", and "Effective STL". They taught me more about C++
programming than almost any other book with the possible exception of
Stroustrup's "The C++ Programming Language". I find Scott Meyers'
books easier to read, and the advice given is worth the book's weight
in gold. He tells you pretty much all you need to know about casts in
C++.

After Meyers and Stroustrup, try Herb Sutter's excellent book
"Exceptional C++". It deals with some fairly advanced concepts. At
least for me, I was glad I had read the other books first. And for
getting into template programming, nothing beats the book "C++
Templates" by Josuttis and Vandevoorde.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top