By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,771 Members | 1,858 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,771 IT Pros & Developers. It's quick & easy.

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

P: n/a
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 ;
}

Oct 3 '05 #1
Share this Question
Share on Google+
16 Replies


P: n/a

"Alfonso Morra" <sw***********@the-ring.com> wrote in message
news:dh**********@nwrdmz02.dmz.ncs.ea.ibs-infra.bt.com...
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().

--
David Hilsee
Oct 3 '05 #2

P: n/a
Alfonso Morra wrote:
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

Oct 3 '05 #3

P: n/a


David Hilsee wrote:
"Alfonso Morra" <sw***********@the-ring.com> wrote in message
news:dh**********@nwrdmz02.dmz.ncs.ea.ibs-infra.bt.com...
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 !

Oct 3 '05 #4

P: n/a


Greg wrote:


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

Oct 3 '05 #5

P: n/a
On Mon, 3 Oct 2005 12:16:09 +0000 (UTC), Alfonso Morra
<sw***********@the-ring.com> wrote:
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 <string>
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.

--
Bob Hairgrove
No**********@Home.com
Oct 3 '05 #6

P: n/a
Bob Hairgrove wrote:
First of all, you have posted this to comp.lang.c++, so you should get
used to doing things the C++ way:

[snip]

The program is very much C-style, not C++. The latter would ordinarily
much prefer std::string to a char*. Check out these FAQs for more on
C++-style serialization:

http://www.parashift.com/c++-faq-lit...alization.html

Cheers! --M

Oct 3 '05 #7

P: n/a
Bob Hairgrove wrote:
On Mon, 3 Oct 2005 12:16:09 +0000 (UTC), Alfonso Morra
<sw***********@the-ring.com> wrote:
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.
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
Oct 3 '05 #8

P: n/a


Default User wrote:
Bob Hairgrove wrote:

On Mon, 3 Oct 2005 12:16:09 +0000 (UTC), Alfonso Morra
<sw***********@the-ring.com> wrote:

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.


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


You sir, are an idiot.

Oct 3 '05 #9

P: n/a
Alfonso Morra wrote:
You sir, are an idiot.

And you are plonked.


Brian
Oct 3 '05 #10

P: n/a
On Mon, 03 Oct 2005 15:27:35 +0200, Bob Hairgrove
<in*****@bigfoot.com> wrote:
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;
}

--
Bob Hairgrove
No**********@Home.com
Oct 3 '05 #11

P: n/a
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.

--
Bob Hairgrove
No**********@Home.com
Oct 4 '05 #12

P: n/a


Bob Hairgrove wrote:
On Mon, 03 Oct 2005 15:27:35 +0200, Bob Hairgrove
<in*****@bigfoot.com> wrote:

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;
}

--
Bob Hairgrove
No**********@Home.com

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

Oct 4 '05 #13

P: n/a


Bob Hairgrove wrote:
On Mon, 03 Oct 2005 15:27:35 +0200, Bob Hairgrove
<in*****@bigfoot.com> wrote:

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;
}

--
Bob Hairgrove
No**********@Home.com


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.

Oct 4 '05 #14

P: n/a
On Tue, 4 Oct 2005 09:50:49 +0000 (UTC), Alfonso Morra
<sw***********@the-ring.com> wrote:
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.

--
Bob Hairgrove
No**********@Home.com
Oct 4 '05 #15

P: n/a
On Tue, 4 Oct 2005 09:47:45 +0000 (UTC), Alfonso Morra
<sw***********@the-ring.com> wrote:
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.

--
Bob Hairgrove
No**********@Home.com
Oct 4 '05 #16

P: 1
You could also use www.webEbenezer.net. The site writes C++
serialization code based on user input. Send and Receive functions
are written similar to the pack/unpack functions suggested.

Brian
Oct 5 '05 #17

This discussion thread is closed

Replies have been disabled for this discussion.