composite type initilization
Given a composite type such that:
struct tp2
{
unsigned int val1;
tp2 ()
: val1(0)
{}
};
And yet again another composite type that's is comprised of the the one
above.
struct tp1 {
unsigned int val2;
tp2 tp;
tp1()
: val2(0)
{}
};
Besides the fictious names, thats the general constructs for messaging
between my different systems.
What's interesting to me though is the fact that if I transfer tp1
between a source and destination, the struct tp2 in tp is
'unitialized'. In other words, val1 in tp2 is 'non zero' ( some
FFFFFF nonesense). This leads me to believe the initilzation of val1
in tp2 is not getting honored which is puzzling.
So I take it a step futher. So now:
struct tp1 {
unsigned int val2;
tp2 tp;
tp1()
: val2(0)
, tp() //<-- lets initialize tp here.
{}
};
This works out better. i.e val1 is now zero. This approach though
suggest that I'm initializing tp twice. Yes/No?
I'm using gcc 2.96 ( which I'm forced to live with for now ) but I'm
just trying to get a feel for why the first option didn't work out.
I suspect in this day and age, what I need to do is something akin to:
struct tp2
{
unsigned int val1;
tp2 ( int v )
: val1(v)
{}
};
struct tp1
{
unsigned int val2;
tp2 tt;
tp1 ( const tp2& t, int v )
: tt(t)
, val2 ( v )
{}
};
[1738 byte] By [
mop65715] at [2007-11-19 18:30:24]

# 1 Re: composite type initilization
What's interesting to me though is the fact that if I transfer tp1What do you mean by "transfer"?
The struct is non-POD, since it contains a user-defined constructor. The only valid "transfer" is through copy construction or assignment. If you mean memset(), memmove(), or anything where you are copying raw bytes, then the behavior is undefined.
struct tp1 {
unsigned int val2;
tp2 tp;
tp1()
: val2(0)
, tp() //<-- lets initialize tp here.
{}
};
This works out better. i.e val1 is now zero. This approach though
suggest that I'm initializing tp twice. Yes/No? There is only one initialization, and that is when you construct a tp1 object. Where do you believe there are two initializations?
I'm using gcc 2.96 ( which I'm forced to live with for now ) but I'm
just trying to get a feel for why the first option didn't work out. You need to explain in C++ terms what you did. From what you stated, it looks like you copied the two "raw", and that doesn't work with non-POD types. It has nothing to do with the compiler.
Regards,
Paul McKenzie
# 2 Re: composite type initilization
You must know that fields of a class are initialized in the order they appear in the class's declaration and not in the order they are defined in the initialization list.
I mean that in that code:
struct tp1 {
unsigned int val2;
tp2 tp;
tp1()
: tp(), val2(0)
{}
};
val2 is initialized before tp, and thus, before tp.val1.
What mean that if you did something like that:
void func(tp1* p)
{
// here p->tp.val1 is not yet initialized
}
struct tp1 {
unsigned int val2;
tp2 tp;
tp1()
: tp(), val2(func(this))
{}
};
Perhaps that fact confused you.
But, for now, I don't understand what you meant in the word "transfer".
If you mean assignment or construction-copy, then, please post the faulty code that behaves strangely.
# 3 Re: composite type initilization
Greetings Paul,
I'm transferring data between two cards. It's a PCI 'transfer' utilizing a vendor API across a protocol called 'starlink'. All that's irrelevant though. The important thing though is that, I
1. I allocate a bucket of memory on the destination card.
2. the source card gets what's called a handle to that bucket of memory on the destination card.
As a result I transfer header and data to the destination cards memory.
As part of my upfront design, I'm trying to determine a way to initailize these headers used during transfers between cards. For instance:
typedef unsigned int uint_type;
char const hdr_version[] = "2005.10.12.09:30";
char const hdr_synch [] = " **** hdr_synch *** ";
enum msg_id_e { msg_01, msg_02, invalid_msg = -1, };
struct segment_id {
int seg_id;
};
struct input_def
{
segment_id seg_id;
uint_type tp_size;
uint_type rp_size;
};
struct common_hdr {
uint_type message_size;
msg_id_e msg_id;
uint_type message_id;
char* const hdr_synch;
char* const hdr_version;
};
struct vp_hdr_t
{
common_hdr common_hdr_t;
input_def idef [ 5 ] [ 5 ];
// more stuff
};
So I thought well. The ideal thing to do would involve giving each struct a constructor. So now:
struct segment_id {
int seg_id;
segment_id()
: seg_id(-1) {}
};
struct input_def
{
segment_id seg_id;
uint_type tp_size;
uint_type rp_size;
input_def ()
: tp_size(0)
, rp_size(0)
{}
};
struct common_hdr {
uint_type message_size;
msg_id_e msg_id;
char* const hdr_synch;
char* const hdr_version;
common_hdr()
: message_size(0)
, msg_id(invalid_msg)
, hdr_synch((char* const )hdr_synch)
, hdr_version((char* const )hdr_version)
{}
};
struct vp_hdr_t
{
common_hdr common_hdr_t;
input_def idef [ 5 ] [ 5 ];
vp_hdr_t ()
{} // here I need to initialize idef
// common_hdr is already initialized ( i think )
};
Now given vp_hdr_t , how would you go about default initializing this struct?
Note also that. When the destiantion receives the header. All the destination needs to do at that point is cast to the appropriate type.
i.e
vp_hdr_t *ptr_vp = (vp_hdr_t*)0xD0000000;
Upon receipt of the interrupt from the source, I (the destination) already know where the header will be in my memory.
Each interrupt from source to destination and vice versa is accompanied by - what's called a doorbell value. That said, I send unique values indicating header or data. All that's irrelevalant. The important thing is how to default initialze the struct.
Are you suggesting that I need to provide a copy constructor/assignment operator with the struct? This because I introduced a constructor?
Thanks in advance.
# 4 Re: composite type initilization
Greetings Paul,
I'm transferring data between two cards. It's a PCI 'transfer' utilizing a vendor API across a protocol called 'starlink'. All that's irrelevant though. Why do you say it's irrelevant? It is very relevant if you are attempting to copy the object in question this way. Are you attempting to do this with the object you described in your first post?
The important thing though is that, I
1. I allocate a bucket of memory on the destination card.
2. the source card gets what's called a handle to that bucket of memory on the destination card.
As a result I transfer header and data to the destination cards memory.
...
So I thought well. The ideal thing to do would involve giving each struct a constructor
...
Note also that. When the destiantion receives the header. All the destination needs to do at that point is cast to the appropriate type.
i.e
vp_hdr_t *ptr_vp = (vp_hdr_t*)0xD0000000;
...
You drew up a plan, but none of it is guaranteed to work in C++.
Again, the original structs were POD, and now the structs are now non-POD. I know it may sound strange, but C++ does not work this way with non-POD types, where you take a memory location, and bingo, you can say an object is there. In C++, non-POD objects must be constructed, assigned, destroyed (with one exeception, operator new using the placement syntax). Maybe placement-new is what you're looking for?
Are you suggesting that I need to provide a copy constructor/assignment operator with the struct? This because I introduced a constructor?No, what I'm saying is once you introduced a constructor, you've gone from the 'C' world into the C++ world.
Basically, non-POD objects cannot be copied, moved, assigned, created, written to files, written across sockets, etc. using 'C' techniques where you are copying raw bytes of memory. This means that you cannot use 'C' functions such as malloc() or calloc() to create such objects, memcpy() or memset() to initialize these objects, you can't use fwrite(..., sizeof(object)) or similar functions to write these objects to a file, and you certainly cannot pass these objects over a socket (which is essentially moving the raw bytes of the object from one location to another).
In C++, the only way to create and copy non-POD types is to actually construct these objects, and using proper copy constructor and assignment to assign these objects. Use any othe method, and the behavior is undefined. From looking at what you're doing, maybe you should be using placement-new, I don't know.
The other workaround is to properly serialize the objects, by sending POD data that gives you enough information to recreate the object on the other side. This is no different than if you were to attempt to write a std::vector<int> to a file -- you don't write the std::vector<int>, you write the number of values in the vector, followed by each integer. Then when you read the file, you create an empty vector, read the number of integers, and populate the empty vector with the values.
The thing is that what you thought of as irrelevant wasn't, and goes into the heart of why you are failing to see the results you expected to see.
Regards,
Paul McKenzie
# 5 Re: composite type initilization
Greetings Paul,
It seems to me that I'd be better leaving things as 'C' style structs and just using memset on them. Placement new wont help me here. Since I already know where in memory the data will arrive. i.e. I've already carved out a location in memory where the 'header' will be stored. That said the source has a 'handle' to that region of memory and transfers the header there. Once the destination is interrupted. All the destination needs to do is:
header *ptr_header = (header*)0xD0000000;
# 6 Re: composite type initilization
The best thing to do with C++ classes of this nature that need to be serialised in a binary format is to give them a data() method (not necessarily that name) that should return the required opaque format. It is also useful to have your classes have a constructor or a load method that takes such a format should that also be required.
Internally these methods can use memcpy if they want (char_traits calls it anyway) but users of your class should not be calling it directly, and there is no need for casting.
If you really want to optimise the copying then design your class to store the data in the required format in contiguous memory and use / return that.
Remember to make your data() function const if it does not modify the class nor supply a buffer that can be modified.
# 7 Re: composite type initilization
The best thing to do with C++ classes of this nature that need to be serialised in a binary format is to give them a data() method (not necessarily that name) that should return the required opaque format. It is also useful to have your classes have a constructor or a load method that takes such a format should that also be required.
Internally these methods can use memcpy if they want (char_traits calls it anyway) but users of your class should not be calling it directly, and there is no need for casting.
can you provide an example of this?
I now understand where I went wrong in my thoughts after Paul's post.
So given a header I'd like to transmit from a source to a destionation. My initial approach.
//1
struct header {
int message_id;
unsigned int message_size;
unsigned int arr[ 5 ] [ 5];
}:
header h;
memset ( &h, 0, sizeof ( header ) );
Now something didn't seem right about the 'C' style construct so I changed to:
//2
struct header {
int message_id;
unsigned int message_size;
unsigned int arr[ 5 ] [ 5];
header ()
: message_id(-1) // i could make it what ever - say -1.. beauty
with this approach
, message_size(0)
{
for ( int idx = 0; idx < 5; ++idx )
for ( int jdx = 0; jdx < 5; +jdx )
arr[idx][jdx] = 0;
}
};
More elegant IMHO, but those two structs are no longer the same. IOW.
Given a vendor API that I suspect is memcopy under the hood such that I could do:
[source] [destiniation] [size]
vendor_api( &f, 0x50000000, sizeof ( foo ) );
Is 1 and 2 guaranteed to be the same. The answer is an emphatic NO. I might get lucky ( which I did ) but I'm bordering on undefined behavior.
Having said , for simplicity I resorted back to 1. To get around memset, I could do:
header hdr = header();
or aggregate initilization
header hd = { 0 };
Of course what this means is I cant get the more general forms allowed with constructors. I'm told C++0x (the next C++ version) might help
with this by defining traits such as has_trivial_assign to tell me when a type can be safely memcpy'd.
# 8 Re: composite type initilization
In a "performance critical" situation one takes all sorts of short-cuts, including some that you cannot be guaranteed to get away with.
Assuming we are not in such a situation but want to write integers as 4-byte big-endian we will know that the size of your header is 108 and we will know the size of the message. A routine to write an unsigned int is fairly straightforward using shifts thus:
void UIntToBuf( unsigned char * buf, unsigned int num )
{
for ( int byteNum = 3; byteNum >=0; --byteNum )
{
buf[ byteNum ] = static_cast<unsigned char >(num & 0xff);
num >>= 8;
}
}
# 9 Re: composite type initilization
In a "performance critical" situation one takes all sorts of short-cuts, including some that you cannot be guaranteed to get away with.
Indeed.
So I went off and bought a copy of the C++ standard to become more accliamated with the requirements from the standards perspective. I discovered that the approach:
struct foo_test {
unsigned int val2;
unsigned int val3;
};
struct test {
unsigned int val;
foo_test tp;
void initialize()
{
val = 100;
tp.val2 = 0;
tp.val3 = 0;
}
};
int main()
{
test t;
t.initialize();
test t2;
memcpy( &t2, &t, sizeof ( test ) );
// prefer to use std::copy but would need to reseach how to use that here
std::cout << t2.tp.val2 << std::endl;
std::cout << t2.tp.val3 << std::endl;
std::cout << t2.val << std::endl;
}
Works too. i.e the struct is _still_ POD. The only issue is, I'll need to remember to call initialze.
Am I correct on this?
# 10 Re: composite type initilization
Works too. i.e the struct is _still_ POD. The only issue is, I'll need to remember to call initialze.
Yes, it is a POD type (at least, with my understanding of the standard).
You are correct!
:)