Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361622

Casting big POD into small POD - guaranteed to work?

Suppose I've a POD struct which has more than 40 members. These members are not built-in types, rather most of them are POD structs, which in turn has lots of members, most of which are POD struct again. This pattern goes up to many levels - POD has POD has POD and so on - up to even 10 or so levels.

I cannot post the actual code, so here is one very simple example of this pattern:

//POD struct
struct Big  
{
    A a[2];  //POD has POD
    B b;     //POD has POD
    double dar[6];
    int m;
    bool is;
    double d;
    char c[10];
};

And A and B are defined as:

struct A
{
    int i;
    int j;
    int k;
};

struct B
{
    A a;   //POD has POD 
    double x;
    double y;
    double z;
    char *s;
};

It's really very simplified version of the actual code which was written (in C) almost 20 years back by Citrix Systems when they came up with ICA protocol. Over the years, the code has been changed a lot. Now we've the source code, but we cannot know which code is being used in the current version of ICA, and which has been discarded, as the discarded part is also present in the source code.

That is the background of the problem. The problem is: now we've the source code, and we're building a system on the top of ICA protocol, for which at some point we need to know the values of few members of the big struct. Few members, not all. Fortunately, those members appear in the beginning of the struct, so can we write a struct which is part of the big struct, as:

//Part of struct B 
//Whatever members it has, they are in the same order as they appear in Big.
struct partBig
{
    A a[2];
    B b;
    double dar[6];
    //rest is ignored
};

Now suppose, we know pointer to Big struct (that we know by deciphering the protocol and data streams), then can we write this:

Big *pBig = GetBig(); 
partBig *part = (partBig*)pBig; //Is this safe?
/*here can we pretend that part is actually Big, so as to access 
first few members safely (using part pointer), namely those 
which are defined in partBig?*/

I don't want to define the entire Big struct in our code, as it has too many POD members and if I define the struct entirely, I've to first define hundreds of other structs. I don't want to that, as even if I do, I doubt if I can do that correctly, as I don't know all the structs correctly (as to which version is being used today, and which is discarded).

I've done the casting already, and it seems to work for last one year, I didn't see any problem with that. But now I thought why not start a topic and ask everyone. Maybe, I'll have a better solution, or at least, will make some important notes.

Relevant references from the language specification will be appreciated. :-)

Here is a demo of such casting: http://ideone.com/c7SWr

Upvotes: 1

Views: 260

Answers (4)

MSalters
MSalters

Reputation: 179991

The relevant type-aliasing rule is 3.10/10: "If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined: ...".

The list contains two cases that are relevant to us:

  1. a type similar (as defined in 4.4) to the dynamic type of the object
  2. an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic data members (including, recursively, an element or non-static data member of a subaggregate or contained union)

The first case isn't sufficient: it merely covers cv-qualifications. The second case allows at the union hack that AndreyT mentions (the common initial sequence), but not anything else.

Upvotes: 0

AnT stands with Russia
AnT stands with Russia

Reputation: 320641

The language specification has some features that are similar to what you are trying to do: the 6.5.2.3/5 states that if you have several structs that share common initial sequence of members, you are allowed to inspect these common members through any of the structs in the union, regardless of which one is currently active. So, from this guarantee one can easily derive that structs with common initial sequence of members should have identical memory layout for these common members.

However, the language does not seem to explicitly allow doing what you are doing, i.e. reinterpreting one struct object as another unrelated struct object through a pointer cast. (The language allows you to access the first member of a struct object through a pointer cast, but not what you do in your code). So, from that perspective, what you are doing might be a strict-aliasing violation (see 6.5/7). I'm not sure about this though, since it is not immediately clear to me whether the intent of 6.5/7 was to outlaw this kind of access.

However, in any case, I don't think that compilers that use strict-aliasing rules for optimization do it at aggregate type level. Most likely they only do it at fundamental type level, meaning that your code should work fine in practice.

Upvotes: 5

BЈовић
BЈовић

Reputation: 64253

The only way to get BigPart from Big is using reinterpret_cast (or C style, as in your question), and the standard does not specify what happens in that case. However, it should work as expected. It would probably fail only for some super exotic platforms.

Upvotes: 0

Carl Norum
Carl Norum

Reputation: 225072

Yes, assuming your small structure is laid out with the same packing/padding rules as the big one, you'll be fine. The memory layout order of fields in a structure is well-defined by the language specifications.

Upvotes: 3

Related Questions