cast struct question

M

Milux

Hi All,

This question has to do with interface design. Suppose I have an translation
tool. It can translates structs from type "general" to other types and then
does some processing.

Example:

typedef struct
{
int data1;
int data2;
}general;

typedef struct
{
int data3;
}specific_a;

For example, the translation looks like this:

void translate1(general * input)
{
specific_a * new_specific_a = malloc (.....);
new_specific_a->data3 = input->data1 * input->data2;

/* use here new_specific_a for a while */


}

Suppose have another type:
typedef struct
{
int data4;
int data5;
}specific_b;

In this specific case another translation looks like:

void translate2(general * input)
{
specific_b * new_specific_b = malloc(....);
new_specific_b->data4 = input->data1;
new_specific_b->data5 = input->data2;

/* do some processing with new_specific_b */

}

Because other interfaces are fixed I need type "specific_a" in the
"translate1" method and "specific_b" in "translate2".

I see an optimalization here in the translate2 method, namely:
void translate2(general * input)
{
specific_b * new_specific_b = (specific_b *) input;
}

(Note: I use the structures specific_b and specific_a only for reading).

Instead of generating the new message, I want to cast the struct (because in
this specific case the data items match exactly). Is this allowed?? In other
words, returns a call to new_specific_b->data4 return data1 and
new_specific_b->data5 return data2 or are there some compiler issues to take
keep in mind?

Grz Milux
 
E

Eric Sosman

Milux wrote On 06/11/07 13:01,:
Hi All,

This question has to do with interface design. Suppose I have an translation
tool. It can translates structs from type "general" to other types and then
does some processing.

Example:

typedef struct
{
int data1;
int data2;
}general;

typedef struct
{
int data3;
}specific_a;

For example, the translation looks like this:

void translate1(general * input)
{
specific_a * new_specific_a = malloc (.....);
new_specific_a->data3 = input->data1 * input->data2;

/* use here new_specific_a for a while */


}

Suppose have another type:
typedef struct
{
int data4;
int data5;
}specific_b;

In this specific case another translation looks like:

void translate2(general * input)
{
specific_b * new_specific_b = malloc(....);
new_specific_b->data4 = input->data1;
new_specific_b->data5 = input->data2;

/* do some processing with new_specific_b */

}

Because other interfaces are fixed I need type "specific_a" in the
"translate1" method and "specific_b" in "translate2".

I see an optimalization here in the translate2 method, namely:
void translate2(general * input)
{
specific_b * new_specific_b = (specific_b *) input;
}

(Note: I use the structures specific_b and specific_a only for reading).

Instead of generating the new message, I want to cast the struct (because in
this specific case the data items match exactly). Is this allowed?? In other
words, returns a call to new_specific_b->data4 return data1 and
new_specific_b->data5 return data2 or are there some compiler issues to take
keep in mind?

In some very specific circumstances (which don't
apply here), the language requires the type punning
to work. The way most compilers make it work in those
special circumstances has the side-effect of making it
work even in cases like yours, where strictly speaking
it is not guaranteed. You are stepping outside the
bounds of the language's guarantees, but the chance
that it will cause trouble is small.

... except that the chance of trouble approaches
100% as time goes by. The two struct types are perfect
matches today, but what will happen to them as the
program is maintained, refined, and extended? You are
pinning your hopes on nothing more than a coincidence,
really. Somebody, someday will change one or the other
of the two "identical twin" structs, and all of a sudden
things will stop working.

You may think that you can solve this problem by
putting great big comment blocks on both structs, warning
the programmer who changes one of them that he must make
the same change in the other. Take it from a battle-
scarred veteran: This does *not* work in the long run.
The compiler does not enforce what you say in comments,
and mistakes *will* happen. Besides, the programmers who
read the comments will curse your name: You will have
violated an important principle of safe implementation
that states there should be exactly one authoritative
description of each data type, function, interface, or
other "thing" in the program. One and only one -- because
as soon as there are two, there can be disagreement.

You are erecting a house of cards on a day when the
air is still, even though you know for certain that
hurricanes will eventually come. Build your card house
if you insist -- but I for one will not sell you an
insurance policy on it.
 
R

Richard Urich

Milux wrote On 06/11/07 13:01,:

In some very specific circumstances (which don't
apply here), the language requires the type punning
to work. The way most compilers make it work in those
special circumstances has the side-effect of making it
work even in cases like yours, where strictly speaking
it is not guaranteed. You are stepping outside the
bounds of the language's guarantees, but the chance
that it will cause trouble is small.

Why is this not guaranteed behavior? Is a compiler allowed to generate
struct{int,int} with varying memory footprints? If so, can you please
elaborate what freedoms a compiler has in this case?

I had always thought struct guaranteed the order of the elements. Is
this accurate?
... except that the chance of trouble approaches
100% as time goes by. The two struct types are perfect
matches today, but what will happen to them as the
program is maintained, refined, and extended? You are
pinning your hopes on nothing more than a coincidence,
really. Somebody, someday will change one or the other
of the two "identical twin" structs, and all of a sudden
things will stop working.

The OP did not provide sufficient context to make this conclusion.
Many structures never change by their very definition.
 
E

Eric Sosman

Richard Urich wrote On 06/11/07 15:44,:
Why is this not guaranteed behavior? Is a compiler allowed to generate
struct{int,int} with varying memory footprints? If so, can you please
elaborate what freedoms a compiler has in this case?

We hashed this out a couple weeks ago, in a thread
titled "Is this legal? Aesthetically acceptable?". Read
through it, and come back if you still have questions.
I had always thought struct guaranteed the order of the elements. Is
this accurate?

Yes, but it's not enough to ensure that the O.P.'s
code will work.
The OP did not provide sufficient context to make this conclusion.
Many structures never change by their very definition.

It seems you've never met Major Murphy:

http://en.wikipedia.org/wiki/Edward_A._Murphy,_Jr.

Seriously, if two things are supposed to be -- in fact,
required to be -- identical, they should probably be just
one thing. Otherwise, the proper operation of the program
rests on nothing sturdier than a coincidence. Those of us
who *have* made the Major's acquaintance are distrustful
of mere coincidence.
 
R

Richard Urich

Richard Urich wrote On 06/11/07 15:44,:

We hashed this out a couple weeks ago, in a thread
titled "Is this legal? Aesthetically acceptable?". Read
through it, and come back if you still have questions.

Thank you for letting me know about the article. It was worth the
read, but unfortunately I could not figure out how to apply it to this
example.

Since struct{int,int} S1 need not be equivalent to struct{int,int} S2,
which of these assumptions is not valid? Or is there another
assumption I have failed to enumerate below?

- the alignment requirements of a type (int) is constant throughout a
compiler
- a struct will maintain program order for all elements
- a struct will pad after each variable only enough to meet the
alignment for the next variable's type (int)
It seems you've never met Major Murphy:

http://en.wikipedia.org/wiki/Edward_A._Murphy,_Jr.

Seriously, if two things are supposed to be -- in fact,
required to be -- identical, they should probably be just
one thing. Otherwise, the proper operation of the program
rests on nothing sturdier than a coincidence. Those of us
who *have* made the Major's acquaintance are distrustful
of mere coincidence.

A network-aligned item and a host-aligned item can be identical on a
given architecture, yet I feel the items should still hold
distinction. I have used type-casting of structs in this instance
before (if same alignment, cast; else, byteswap), and apparently this
was non-portable code. Without devolving into a style debate, can you
please explain why these casts can fail for structs?

My sincere gratitude for the help,

Richard Urich
 
E

Eric Sosman

Richard said:
[... about type-punning between "identically arranged" structs ...]

A network-aligned item and a host-aligned item can be identical on a
given architecture, yet I feel the items should still hold
distinction. I have used type-casting of structs in this instance
before (if same alignment, cast; else, byteswap), and apparently this
was non-portable code. Without devolving into a style debate, can you
please explain why these casts can fail for structs?

The point is that the distinct items should be treated
as such, and the program should not try to gloss over the
distinction. It may happen that on Machine M the two variants
are in fact identical -- but does that mean that a program
should rely on this coincidence of representation? No, I'd
say, because on Machine N where the variants differ in some
way the program will break. That's a fragile program; I have
a distaste for fragile programs.

The O.P. noticed that two of his structs just happened to
have similar members and in a similar order, and he tried to
achieve an "optimalization" by pretending the two structs were
identical. I advise against this course unless they truly
*are* identical, and the only way I know of to be sure of that
is to arrange that they are in fact the same struct type.

And, as I said at the end of my original response, the O.P.
is at liberty to ignore my advice and to persist in his folly;
it's not my funeral. (Cue the Therac-25 reference -- but that
was, in fairness, a different sort of problem altogether. Still,
it serves as a sobering, or even terrifying reminder that code
has a way of outlasting its original design parameters.)
 
E

Eric Sosman

CBFalconer said:
Ah, you have a vision of the future, and know how the program will
be modified, maintained, refined, extended in that future. How did
you acquire this very useful ability?

In fairness to the poor, benighted, ignorant sod -- 'scuse
me, to the "aspirant" -- you and I are overstating the likelihood
that maintenance and modification will break anything that isn't
nailed down. I said it approached 100%, and while that may be
true I conveniently didn't say anything about the rate of approach.
It could be that the half-life of bug appearance might be on the
order of a few million years.

... but you and I, Chuck, bitter experience tells us that it's
on the order of a few million milliseconds. "Oh, ay; and you try
and tell the young people of today that, and they won't believe
you!"
 
R

Richard Urich

The O.P. noticed that two of his structs just happened to
have similar members and in a similar order, and he tried to
achieve an "optimalization" by pretending the two structs were
identical. I advise against this course unless they truly
*are* identical, and the only way I know of to be sure of that
is to arrange that they are in fact the same struct type.

The OP noticed he had two structs with identically-typed, identically-
ordered members. He asked if, under those specific circumstances, the
C Standard guarantees they are identical.

You have claimed two structs that are identically coded are, in fact,
not identical as far as C is concerned. I am merely asking for some
guidance on where in the rather lengthy standard I might look to find
supporting evidence for this claim.

The draft standard does show 6.7.2.1#16:
EXAMPLE Assuming that all array members are aligned the same, after
the declarations:
struct s { int n; double d[]; };
struct ss { int n; double d[1]; };
the three expressions:
sizeof (struct s)
offsetof(struct s, d)
offsetof(struct ss, d)
have the same value. The structure struct s has a flexible array
member d.

This implies to me that it is safe to cast from one identically-coded
struct to another. If there is some part of the standard which
overrides this behavior, please let me know.


Richard Urich
 
K

Keith Thompson

Richard Urich said:
The O.P. noticed that two of his structs just happened to
have similar members and in a similar order, and he tried to
achieve an "optimalization" by pretending the two structs were
identical. I advise against this course unless they truly
*are* identical, and the only way I know of to be sure of that
is to arrange that they are in fact the same struct type.

The OP noticed he had two structs with identically-typed, identically-
ordered members. He asked if, under those specific circumstances, the
C Standard guarantees they are identical.

You have claimed two structs that are identically coded are, in fact,
not identical as far as C is concerned. I am merely asking for some
guidance on where in the rather lengthy standard I might look to find
supporting evidence for this claim.

The draft standard does show 6.7.2.1#16:
EXAMPLE Assuming that all array members are aligned the same, after
the declarations:
struct s { int n; double d[]; };
struct ss { int n; double d[1]; };
the three expressions:
sizeof (struct s)
offsetof(struct s, d)
offsetof(struct ss, d)
have the same value. The structure struct s has a flexible array
member d.

This implies to me that it is safe to cast from one identically-coded
struct to another. If there is some part of the standard which
overrides this behavior, please let me know.

The example specifically assumes that all array members are aligned
the same. The standard does not guarantee that.

I don't think you'll find a specific passage in the standard that says
two identically declared structures aren't guaranteed to have the same
layout. The point is that you won't find a passage that says they
*are* guaranteed to have the same layout.

The ordering of structure members is guaranteed, but the amount of
padding is not. The compiler is not reuqired to use only the minimal
padding required for alignment.

See also C99 6.5.2.3p5:

One special guarantee is made in order to simplify the use of
unions: if a union contains several structures that share a common
initial sequence (see below), and if the union object currently
contains one of these structures, it is permitted to inspect the
common initial part of any of them anywhere that a declaration of
the complete type of the union is visible. Two structures share a
_common initial sequence_ if corresponding members have compatible
types (and, for bit-fields, the same widths) for a sequence of one
or more initial members.

This is guaranteed *only* when the structures are members of a union.
If they aren't, all bets are off.

In practice, it's very likely that you can get away with assuming the
same layout for sufficiently similar structure types. But it's safer,
and much clearer, simply to declare a single structure type so you
*know* that it will always have the same layout.
 
E

Eric Sosman

Richard Urich wrote On 06/12/07 12:56,:
The OP noticed he had two structs with identically-typed, identically-
ordered members. He asked if, under those specific circumstances, the
C Standard guarantees they are identical.

You have claimed two structs that are identically coded are, in fact,
not identical as far as C is concerned. I am merely asking for some
guidance on where in the rather lengthy standard I might look to find
supporting evidence for this claim.

Sorry; I misunderstood your question. The passage
that says the two types are incompatible is 6.2.7p1,
third sentence. The description of the special case in
which the type-punning *is* safe is 6.5.2.3p5.
The draft standard does show 6.7.2.1#16:
EXAMPLE Assuming that all array members are aligned the same, after
the declarations:
struct s { int n; double d[]; };
struct ss { int n; double d[1]; };
the three expressions:
sizeof (struct s)
offsetof(struct s, d)
offsetof(struct ss, d)
have the same value. The structure struct s has a flexible array
member d.

This implies to me that it is safe to cast from one identically-coded
struct to another. If there is some part of the standard which
overrides this behavior, please let me know.

Note the words "assuming that," which describe a
precondition the language allows but does not require.
Also, examples aren't normative.
 
R

Richard Urich

Sorry; I misunderstood your question. The passage
that says the two types are incompatible is 6.2.7p1,
third sentence. The description of the special case in
which the type-punning *is* safe is 6.5.2.3p5.

Thank you. This is exactly what I was looking for.
 
D

David Thompson

On Mon, 11 Jun 2007 22:52:21 -0000, Richard Urich
Since struct{int,int} S1 need not be equivalent to struct{int,int} S2,
which of these assumptions is not valid? Or is there another
assumption I have failed to enumerate below?

- the alignment requirements of a type (int) is constant throughout a
compiler

I'm not sure this is actually guaranteed. It clearly seems intended;
the Standard routinely talks about the alignment requirement as an
attribute of a type, implying it applies to all objects of that type.
OTOH I'm not sure any standard/portable program can be affected, or
even detect, if it's not. Imagine if 'int' in a struct is aligned to
2, but alone in memory to 4 -- and pointer (to int, or void) has the
bits all scrambled so you can't figure out the representation and
converting it to an integer type yields a result not arithmetically
linear in the actual address; how do you 'see' that int's alignment?

But it is true in the Standard that any T * must be able to point to
and validly access any actual (valid) T object in the program, even if
that T * has been converted through void *. Thus either pointers must
work without 'knowing' the alignment of the target, or all (?) data
pointer types must carry target alignment in at least equivalent ways.
- a struct will maintain program order for all elements

That one is explicitly guaranteed.
- a struct will pad after each variable only enough to meet the
alignment for the next variable's type (int)
But that one is not, and is the kicker. An implementation can pad
after elements in a struct (type) for any reason and in amount it
likes, except no padding before the first element and for the special
case already described of common initial sequences in a union it must
be consistent. Yes in practice implementations generally pad only as
needed for alignment (and that need is consistent), and thus in
practice all structs with the same order and type of elements are
compatible -- but not strictly guaranteed.

Except of course if you change compiler options relating to target
architecture/model/etc., but in Standard terms if you do that it's not
the same implementation anymore. Although in practice it is a problem.

- formerly david.thompson1 || achar(64) || worldnet.att.net
 
S

Stephen Sprunk

Richard Urich said:
Since struct{int,int} S1 need not be equivalent to struct{int,int} S2,
which of these assumptions is not valid? Or is there another
assumption I have failed to enumerate below?

- the alignment requirements of a type (int) is constant throughout a
compiler
- a struct will maintain program order for all elements
- a struct will pad after each variable only enough to meet the
alignment for the next variable's type (int)

The middle assumption is guaranteed by the standard, but the first and last
of those aren't. However, all will be true in any sane implementation.

The bigger issue is what happens when some other coder modifies struct S1 to
insert a new member between the first and (currently) second members. Then,
your code that casts a struct S1 to a struct S2 (or vice versa) is no longer
accessing the member you expected.

If two structs are supposed to have "compatible" definitions, and your code
is going to rely on that, then _use the same type_ and you won't need to
cast. If using the same type is not possible, or would make future
maintenance more difficult, then use different types and don't assume
they're "compatible", i.e. don't cast between them.

S
 

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

Similar Threads

comparing sizes of two structures 20
A cast question 3
struct and pointer question 33
cast 20
pls advise on AVL tree 1
cast question 4
struct arguments to function 4
cast musings 19

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top