[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

Extending unions and ABI?

Jef Driesen

5/16/2011 6:06:00 PM

Suppose I have defined a union:

typedef struct foo_t {
...
} foo_t;

typedef struct bar_t {
...
} bar_t;

typedef union foobar_t {
foo_t foo;
bar_t bar;
} foobar_t;

Which is part of the public api of my project. The union is used to pass
different types of data (e.g. either a foo or a bar struct) back to the caller
through a callback function. The type is used distinguish between the two:

typedef enum foobar_type_t {
FOO;
BAR;
} foobar_type_t;

typedef void (*callback_t)(foobar_type_t type, const foobar_t *foobar);

void
dosomething (callback_t callback)
{

foobar_t foobar;

foobar.foo = ...
callback (FOO, &foobar);

foobar.bar = ...
callback (BAR, &foobar);
}

Note that I only use pointers to the union in the public api. The union itself
appears only in the implementation (e.g. in the dosomething() function).

Now suppose I need to extend my api to support a third type of data. Is it
portable to just add a third struct to the union? Will it break the ABI rules
and thus backwards compatibility. Is this a violation of the C standard?

The alternative would be to use a void pointer and require the user to cast to
the appropriate data type, but this is less readable:

foobar>foo.member

vs

((foo_t *)foobar)>member

Jef
15 Answers

Tom St Denis

5/16/2011 6:21:00 PM

0

On May 16, 2:06 pm, Jef Driesen <jefdrie...@hotmail.com.invalid>
wrote:
> Suppose I have defined a union:
>
> typedef struct foo_t {
>     ...
>
> } foo_t;
>
> typedef struct bar_t {
>     ...
>
> } bar_t;
>
> typedef union foobar_t {
>     foo_t foo;
>     bar_t bar;
>
> } foobar_t;
>
> Which is part of the public api of my project. The union is used to pass
> different types of data (e.g. either a foo or a bar struct) back to the caller
> through a callback function. The type is used distinguish between the two:
>
> typedef enum foobar_type_t {
>     FOO;
>     BAR;
>
> } foobar_type_t;
>
> typedef void (*callback_t)(foobar_type_t type, const foobar_t *foobar);
>
> void
> dosomething (callback_t callback)
> {
>
>     foobar_t foobar;
>
>     foobar.foo = ...
>     callback (FOO, &foobar);
>
>     foobar.bar = ...
>     callback (BAR, &foobar);
>
> }
>
> Note that I only use pointers to the union in the public api. The union itself
> appears only in the implementation (e.g. in the dosomething() function).
>
> Now suppose I need to extend my api to support a third type of data. Is it
> portable to just add a third struct to the union? Will it break the ABI rules
> and thus backwards compatibility. Is this a violation of the C standard?

If you add things to the union you potentially change the size of it,
so any code compiled against the definition already won't be able to
work with the object properly.

IOW, don't do that.

If you must, pass a void* to your callback prototype and have the
instances of the function deref it appropriately. e.g.

void callback_foo(void *data, ...)
{
struct foo *foo_data = data;

foo_data->whatever = 3;
}

similar for bar...

Tom

Shao Miller

5/16/2011 6:38:00 PM

0

On 5/16/2011 14:06, Jef Driesen wrote:
> Suppose I have defined a union:
>
> typedef struct foo_t {
> ...
> } foo_t;
>
> typedef struct bar_t {
> ...
> } bar_t;
>
> typedef union foobar_t {
> foo_t foo;
> bar_t bar;
> } foobar_t;
>
> Which is part of the public api of my project.

As in, these are in a header file and the struct and union definitions
will thus be available within all users' translation units, assuming
they include the header?

> The union is used to pass
> different types of data (e.g. either a foo or a bar struct) back to the
> caller through a callback function. The type is used distinguish between
> the two:
>
> typedef enum foobar_type_t {
> FOO;
> BAR;
> } foobar_type_t;
>

Did you mean to use commas instead of semi-colons for the two values?

> typedef void (*callback_t)(foobar_type_t type, const foobar_t *foobar);
>
> void
> dosomething (callback_t callback)
> {
>
> foobar_t foobar;
>
> foobar.foo = ...
> callback (FOO, &foobar);
>
> foobar.bar = ...
> callback (BAR, &foobar);
> }
>
> Note that I only use pointers to the union in the public api. The union
> itself appears only in the implementation (e.g. in the dosomething()
> function).
>

Do you mean that the union definition you gave closer to the beginning
of your post is only within the translation unit with the
'dosomething()' definition? Or do you mean that the only code which
will use the 'sizeof' or the members of the union will be within the
translation unit with the 'dosomething()' definition?

> Now suppose I need to extend my api to support a third type of data. Is
> it portable to just add a third struct to the union? Will it break the
> ABI rules and thus backwards compatibility. Is this a violation of the C
> standard?
>

From n1256.pdf, section 6.2.5, point 27:

"...All pointers to union types shall have the same representation
and alignment requirements as each other..."

:)

> The alternative would be to use a void pointer and require the user to
> cast to the appropriate data type,

No need to cast a 'void *' to another pointer-to-object type.

> but this is less readable:
>
> foobar>foo.member
>
> vs
>
> ((foo_t *)foobar)>member

Are you meaning '->' instead of '>'?

Morris Keesan

5/16/2011 6:55:00 PM

0

On Mon, 16 May 2011 14:38:02 -0400, Shao Miller <sha0.miller@gmail.com>
wrote:

> On 5/16/2011 14:06, Jef Driesen wrote:
>> Suppose I have defined a union:
>>
>> typedef struct foo_t {
>> ...
>> } foo_t;
>>
>> typedef struct bar_t {
>> ...
>> } bar_t;
>>
>> typedef union foobar_t {
>> foo_t foo;
>> bar_t bar;
>> } foobar_t;
>>
>> Which is part of the public api of my project.
>
> As in, these are in a header file and the struct and union definitions
> will thus be available within all users' translation units, assuming
> they include the header?
>
>> The union is used to pass
>> different types of data (e.g. either a foo or a bar struct) back to the
>> caller through a callback function. The type is used distinguish between
>> the two:
>>
>> typedef enum foobar_type_t {
>> FOO;
>> BAR;
>> } foobar_type_t;
>>
>
> Did you mean to use commas instead of semi-colons for the two values?
>
>> typedef void (*callback_t)(foobar_type_t type, const foobar_t *foobar);
>>
>> void
>> dosomething (callback_t callback)
>> {
>>
>> foobar_t foobar;
>>
>> foobar.foo = ...
>> callback (FOO, &foobar);
>>
>> foobar.bar = ...
>> callback (BAR, &foobar);
>> }
>>
>> Note that I only use pointers to the union in the public api. The union
>> itself appears only in the implementation (e.g. in the dosomething()
>> function).
>>
>
> Do you mean that the union definition you gave closer to the beginning
> of your post is only within the translation unit with the
> 'dosomething()' definition? Or do you mean that the only code which
> will use the 'sizeof' or the members of the union will be within the
> translation unit with the 'dosomething()' definition?
>
>> Now suppose I need to extend my api to support a third type of data. Is
>> it portable to just add a third struct to the union? Will it break the
>> ABI rules and thus backwards compatibility. Is this a violation of the C
>> standard?
>>
>
> From n1256.pdf, section 6.2.5, point 27:
>
> "...All pointers to union types shall have the same representation
> and alignment requirements as each other..."
>
> :)
>
>> The alternative would be to use a void pointer and require the user to
>> cast to the appropriate data type,
>
> No need to cast a 'void *' to another pointer-to-object type.

There is if you're going to do what he does here:

>> ((foo_t *)foobar)->member

Unless you want to declare an extra pointer to hold the converted type:
(foo_t *foo = foobar)->member
--
Morris Keesan -- mkeesan@post.harvard.edu

Jef Driesen

5/16/2011 6:56:00 PM

0

On 16/05/11 20:38, Shao Miller wrote:
> On 5/16/2011 14:06, Jef Driesen wrote:
>> Suppose I have defined a union:
>>
>> typedef struct foo_t {
>> ...
>> } foo_t;
>>
>> typedef struct bar_t {
>> ...
>> } bar_t;
>>
>> typedef union foobar_t {
>> foo_t foo;
>> bar_t bar;
>> } foobar_t;
>>
>> Which is part of the public api of my project.
>
> As in, these are in a header file and the struct and union definitions
> will thus be available within all users' translation units, assuming
> they include the header?

Yes. How would they be able to use it otherwise?

>> The union is used to pass
>> different types of data (e.g. either a foo or a bar struct) back to the
>> caller through a callback function. The type is used distinguish between
>> the two:
>>
>> typedef enum foobar_type_t {
>> FOO;
>> BAR;
>> } foobar_type_t;
>>
>
> Did you mean to use commas instead of semi-colons for the two values?

Of course.

>> typedef void (*callback_t)(foobar_type_t type, const foobar_t *foobar);
>>
>> void
>> dosomething (callback_t callback)
>> {
>>
>> foobar_t foobar;
>>
>> foobar.foo = ...
>> callback (FOO,&foobar);
>>
>> foobar.bar = ...
>> callback (BAR,&foobar);
>> }
>>
>> Note that I only use pointers to the union in the public api. The union
>> itself appears only in the implementation (e.g. in the dosomething()
>> function).
>>
>
> Do you mean that the union definition you gave closer to the beginning
> of your post is only within the translation unit with the
> 'dosomething()' definition? Or do you mean that the only code which
> will use the 'sizeof' or the members of the union will be within the
> translation unit with the 'dosomething()' definition?

The dosomething() function would be exposed in the public api, but the
implementation would be internally to the library. A user would have to do
something like this:

#include <dosomething.h>

void mycallback(foobar_type_t type, const foobar_t *foobar)
{
switch (type) {
case FOO:
/* Use foobar->foo.xyz */
break;
case BAR:
/* Use foobar->bar.abc */
break;
}
}

int
main (void)
{
dosomething (mycallback);
}

So the union is allocated in the dosomething() function in the library and
passed back to the caller, not the other way around.

>> Now suppose I need to extend my api to support a third type of data. Is
>> it portable to just add a third struct to the union? Will it break the
>> ABI rules and thus backwards compatibility. Is this a violation of the C
>> standard?
>>
>
> From n1256.pdf, section 6.2.5, point 27:
>
> "...All pointers to union types shall have the same representation
> and alignment requirements as each other..."
>
> :)
>
>> The alternative would be to use a void pointer and require the user to
>> cast to the appropriate data type,
>
> No need to cast a 'void *' to another pointer-to-object type.

I know, but if it's not assigned to another variable, but converted on-the-fly
like below I don't think you can avoid the cast.

>> but this is less readable:
>>
>> foobar>foo.member
>>
>> vs
>>
>> ((foo_t *)foobar)>member
>
> Are you meaning '->' instead of'>'?

Of course.

jt

5/16/2011 7:12:00 PM

0

Tom St Denis <tom@iahu.ca> wrote:
> On May 16, 2:06 pm, Jef Driesen <jefdrie...@hotmail.com.invalid>
> wrote:
> > Suppose I have defined a union:
> >
> > typedef struct foo_t {
> >     ...
> >
> > } foo_t;
> >
> > typedef struct bar_t {
> >     ...
> >
> > } bar_t;
> >
> > typedef union foobar_t {
> >     foo_t foo;
> >     bar_t bar;
> >
> > } foobar_t;
> >
> > Which is part of the public api of my project. The union is used to pass
> > different types of data (e.g. either a foo or a bar struct) back to the
> > caller through a callback function. The type is used distinguish between
> > the two:
> >
> > typedef enum foobar_type_t {
> >     FOO;
> >     BAR;
> >
> > } foobar_type_t;
> >
> > typedef void (*callback_t)(foobar_type_t type, const foobar_t *foobar);
> >
> > void
> > dosomething (callback_t callback)
> > {
> >
> >     foobar_t foobar;
> >
> >     foobar.foo = ...
> >     callback (FOO, &foobar);
> >
> >     foobar.bar = ...
> >     callback (BAR, &foobar);
> >
> > }
> >
> > Note that I only use pointers to the union in the public api. The union
> > itself appears only in the implementation (e.g. in the dosomething()
> > function). Now suppose I need to extend my api to support a third type of
> > data. Is it portable to just add a third struct to the union? Will it
> > break the ABI rules and thus backwards compatibility. Is this a violation
> > of the C standard?

> If you add things to the union you potentially change the size of it,
> so any code compiled against the definition already won't be able to
> work with the object properly.

Well, but the user gets, as far as the OP wrote, only pointers
to it and not copies, so I would think the size of what's pointef
to is irrelevant - the size of the pointer remains the same. The
only information used when compiling a program using the header
file declaring the union when only using pointers is thus what
members there are - and not being aware that there could be
others won't hurt (except that the user can't access them).

> IOW, don't do that.

I think it's completely safe to extend the union as long as the
user (i.e. the callback function written by the user) only gets
a pointer to an instance. Even if the user would create his own
instance of the union (using the old version without the new
members) and copies from the union pointed to he's still safe
since he will perhaps miss a few bytes but only for members he
can't access anyway because he doesn't know about them.

> If you must, pass a void* to your callback prototype and have the
> instances of the function deref it appropriately. e.g.

> void callback_foo(void *data, ...)
> {
> struct foo *foo_data = data;

Now you're talking structures, but the OP has a union that
contains different structures. And he's not going to change
the structures but just add another structure to the union.
I don't see how converting in the function that calls the
callback function to a void pointer and then converting back
in the user-written callback function would help.

Regards, Jens
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://t...

Shao Miller

5/16/2011 7:13:00 PM

0

On 5/16/2011 14:54, Morris Keesan wrote:
> On Mon, 16 May 2011 14:38:02 -0400, Shao Miller <sha0.miller@gmail.com>
> wrote:
>
>> On 5/16/2011 14:06, Jef Driesen wrote:
>>> Suppose I have defined a union:
>>>
>>> typedef struct foo_t {
>>> ...
>>> } foo_t;
>>>
>>> typedef struct bar_t {
>>> ...
>>> } bar_t;
>>>
>>> typedef union foobar_t {
>>> foo_t foo;
>>> bar_t bar;
>>> } foobar_t;
>>>
>>> Which is part of the public api of my project.
>>
>> As in, these are in a header file and the struct and union definitions
>> will thus be available within all users' translation units, assuming
>> they include the header?
>>
>>> The union is used to pass
>>> different types of data (e.g. either a foo or a bar struct) back to the
>>> caller through a callback function. The type is used distinguish between
>>> the two:
>>>
>>> typedef enum foobar_type_t {
>>> FOO;
>>> BAR;
>>> } foobar_type_t;
>>>
>>
>> Did you mean to use commas instead of semi-colons for the two values?
>>
>>> typedef void (*callback_t)(foobar_type_t type, const foobar_t *foobar);
>>>
>>> void
>>> dosomething (callback_t callback)
>>> {
>>>
>>> foobar_t foobar;
>>>
>>> foobar.foo = ...
>>> callback (FOO, &foobar);
>>>
>>> foobar.bar = ...
>>> callback (BAR, &foobar);
>>> }
>>>
>>> Note that I only use pointers to the union in the public api. The union
>>> itself appears only in the implementation (e.g. in the dosomething()
>>> function).
>>>
>>
>> Do you mean that the union definition you gave closer to the beginning
>> of your post is only within the translation unit with the
>> 'dosomething()' definition? Or do you mean that the only code which
>> will use the 'sizeof' or the members of the union will be within the
>> translation unit with the 'dosomething()' definition?
>>
>>> Now suppose I need to extend my api to support a third type of data. Is
>>> it portable to just add a third struct to the union? Will it break the
>>> ABI rules and thus backwards compatibility. Is this a violation of the C
>>> standard?
>>>
>>
>> From n1256.pdf, section 6.2.5, point 27:
>>
>> "...All pointers to union types shall have the same representation and
>> alignment requirements as each other..."
>>
>> :)
>>
>>> The alternative would be to use a void pointer and require the user to
>>> cast to the appropriate data type,
>>
>> No need to cast a 'void *' to another pointer-to-object type.
>
> There is if you're going to do what he does here:
>
>>> ((foo_t *)foobar)->member
>
> Unless you want to declare an extra pointer to hold the converted type:
> (foo_t *foo = foobar)->member

Oops. Good point. :) I didn't understand the need for a cast, since
one could use:

... foobar->foo.member ...

Assuming the union definition is available to the users... 'foo_t'
obviously must be.

Shao Miller

5/16/2011 7:44:00 PM

0

On 5/16/2011 14:55, Jef Driesen wrote:
> On 16/05/11 20:38, Shao Miller wrote:
>> On 5/16/2011 14:06, Jef Driesen wrote:
>>> Suppose I have defined a union:
>>>
>>> typedef struct foo_t {
>>> ...
>>> } foo_t;
>>>
>>> typedef struct bar_t {
>>> ...
>>> } bar_t;
>>>
>>> typedef union foobar_t {
>>> foo_t foo;
>>> bar_t bar;
>>> } foobar_t;
>>>
>>> Which is part of the public api of my project.
>>
>> As in, these are in a header file and the struct and union definitions
>> will thus be available within all users' translation units, assuming
>> they include the header?
>
> Yes. How would they be able to use it otherwise?

I didn't (and don't) understand your "the union itself appears only in
the implementation (e.g. in the dosomething() function)". What about it
only appears in the private TUs and not in the public headers?

[ Corrected typographical errors below. Not sure why indenting dies
with this news client. Sorry. ]
>
>>> The union is used to pass
>>> different types of data (e.g. either a foo or a bar struct) back to the
>>> caller through a callback function. The type is used distinguish between
>>> the two:
>>>
>>> typedef enum foobar_type_t {
>>> FOO,
>>> BAR,
>>> } foobar_type_t;
>>>
>>> typedef void (*callback_t)(foobar_type_t type, const foobar_t *foobar);
>>>
>>> void
>>> dosomething (callback_t callback)
>>> {
>>>
>>> foobar_t foobar;
>>>
>>> foobar.foo = ...
>>> callback (FOO,&foobar);
>>>
>>> foobar.bar = ...
>>> callback (BAR,&foobar);
>>> }
>>>
>>> Note that I only use pointers to the union in the public api. The union
>>> itself appears only in the implementation (e.g. in the dosomething()
>>> function).
>>>
>>

The union definition?
---
/* public.h */
...
typedef union foobar_t foobar_t;
...
---
/* private.c */
...
typedef union foobar_t {
foo_t foo;
bar_t bar;
} foobar_t;
...
---

Or is the definition in public.h?

>> Do you mean that the union definition you gave closer to the beginning
>> of your post is only within the translation unit with the
>> 'dosomething()' definition? Or do you mean that the only code which
>> will use the 'sizeof' or the members of the union will be within the
>> translation unit with the 'dosomething()' definition?
>
> The dosomething() function would be exposed in the public api, but the
> implementation would be internally to the library. A user would have to
> do something like this:
>
> #include <dosomething.h>
>
> void mycallback(foobar_type_t type, const foobar_t *foobar)
> {
> switch (type) {
> case FOO:
> /* Use foobar->foo.xyz */
> break;
> case BAR:
> /* Use foobar->bar.abc */
> break;
> }
> }
>
> int
> main (void)
> {
> dosomething (mycallback);
> }
>
> So the union is allocated in the dosomething() function in the library
> and passed back to the caller, not the other way around.

Looks good to me. :)

>
>>> Now suppose I need to extend my api to support a third type of data. Is
>>> it portable to just add a third struct to the union? Will it break the
>>> ABI rules and thus backwards compatibility. Is this a violation of the C
>>> standard?
>>>
>>
>> From n1256.pdf, section 6.2.5, point 27:
>>
>> "...All pointers to union types shall have the same representation
>> and alignment requirements as each other..."
>>
>> :)
>>
>>> The alternative would be to use a void pointer and require the user to
>>> cast to the appropriate data type,
>>
>> No need to cast a 'void *' to another pointer-to-object type.
>
> I know, but if it's not assigned to another variable, but converted
> on-the-fly like below I don't think you can avoid the cast.
>

Above, you have already shown:

foobar->foo.xyz

So why cast?

[ Corrected typographical errors below. ]
>>> but this is less readable:
>>>
>>> foobar->foo.member
>>>
>>> vs
>>>
>>> ((foo_t *)foobar)->member
>>

Really? I prefer the former, personally.

If you come up with version 2.0 of the "public.h" header and it includes
a new structure in the union, that structure could affect the alignment
requirements of the union.

If the only code that allocates such union objects is aware of the
change, the alignment requirements should still satisfy the old union
version, as long as you don't remove the old struct members, right?

I wouldn't expect that other users' code would care... Their foos and
bars are still aligned, and 'foo_t' and 'bar_t' still has the same tag
and same definition[n1256.pdf:6.2.7p1]. I'd view the use of a "newer"
union rather than the "outdated" union as "type-punning", under these
circumstances. I could be mistaken.

Shao Miller

5/16/2011 7:48:00 PM

0

On 5/16/2011 15:43, Shao Miller wrote:
> If you come up with version 2.0 of the "public.h" header and it includes
> a new structure in the union, that structure could affect the alignment
> requirements of the union.
>
> If the only code that allocates such union objects is aware of the
> change, the alignment requirements should still satisfy the old union
> version, as long as you don't remove the old struct members, right?
>
> I wouldn't expect that other users' code would care... Their foos and
> bars are still aligned, and 'foo_t' and 'bar_t' still has the same tag
> and same definition[n1256.pdf:6.2.7p1]. I'd view the use of a "newer"
> union rather than the "outdated" union as "type-punning", under these
> circumstances. I could be mistaken.

Of course, since union version 2.0 could also change the size, hopefully
your old library users aren't using arrays with elements of this union
type! Then they'd be borked. :)

Jef Driesen

5/16/2011 7:54:00 PM

0

On 16/05/11 21:43, Shao Miller wrote:
> On 5/16/2011 14:55, Jef Driesen wrote:
>> On 16/05/11 20:38, Shao Miller wrote:
>>> On 5/16/2011 14:06, Jef Driesen wrote:
>>>> Suppose I have defined a union:
>>>>
>>>> typedef struct foo_t {
>>>> ...
>>>> } foo_t;
>>>>
>>>> typedef struct bar_t {
>>>> ...
>>>> } bar_t;
>>>>
>>>> typedef union foobar_t {
>>>> foo_t foo;
>>>> bar_t bar;
>>>> } foobar_t;
>>>>
>>>> Which is part of the public api of my project.
>>>
>>> As in, these are in a header file and the struct and union definitions
>>> will thus be available within all users' translation units, assuming
>>> they include the header?
>>
>> Yes. How would they be able to use it otherwise?
>
> I didn't (and don't) understand your "the union itself appears only in
> the implementation (e.g. in the dosomething() function)". What about it
> only appears in the private TUs and not in the public headers?
>
> [ Corrected typographical errors below. Not sure why indenting dies
> with this news client. Sorry. ]
>>
>>>> The union is used to pass
>>>> different types of data (e.g. either a foo or a bar struct) back to the
>>>> caller through a callback function. The type is used distinguish between
>>>> the two:
>>>>
>>>> typedef enum foobar_type_t {
>>>> FOO,
>>>> BAR,
>>>> } foobar_type_t;
>>>>
>>>> typedef void (*callback_t)(foobar_type_t type, const foobar_t *foobar);
>>>>
>>>> void
>>>> dosomething (callback_t callback)
>>>> {
>>>>
>>>> foobar_t foobar;
>>>>
>>>> foobar.foo = ...
>>>> callback (FOO,&foobar);
>>>>
>>>> foobar.bar = ...
>>>> callback (BAR,&foobar);
>>>> }
>>>>
>>>> Note that I only use pointers to the union in the public api. The union
>>>> itself appears only in the implementation (e.g. in the dosomething()
>>>> function).
>>>>
>>>
>
> The union definition?
> ---
> /* public.h */
> ...
> typedef union foobar_t foobar_t;
> ...
> ---
> /* private.c */
> ...
> typedef union foobar_t {
> foo_t foo;
> bar_t bar;
> } foobar_t;
> ...
> ---
>
> Or is the definition in public.h?

In public.h, because if not I see no advantage in the union compared to using a
void pointer (instead of pointer to the union) together with the individual structs.

>>>> Now suppose I need to extend my api to support a third type of data. Is
>>>> it portable to just add a third struct to the union? Will it break the
>>>> ABI rules and thus backwards compatibility. Is this a violation of the C
>>>> standard?
>>>>
>>>
>>> From n1256.pdf, section 6.2.5, point 27:
>>>
>>> "...All pointers to union types shall have the same representation
>>> and alignment requirements as each other..."
>>>
>>> :)
>>>
>>>> The alternative would be to use a void pointer and require the user to
>>>> cast to the appropriate data type,
>>>
>>> No need to cast a 'void *' to another pointer-to-object type.
>>
>> I know, but if it's not assigned to another variable, but converted
>> on-the-fly like below I don't think you can avoid the cast.
>>
>
> Above, you have already shown:
>
> foobar->foo.xyz
>
> So why cast?
>
> [ Corrected typographical errors below. ]
>>>> but this is less readable:
>>>>
>>>> foobar->foo.member
>>>>
>>>> vs
>>>>
>>>> ((foo_t *)foobar)->member
>>>
>
> Really? I prefer the former, personally.

I think there is a little misunderstand here. I also prefer the first version.
But this is of course only possible with the union (e.g. foobar is a pointer to
the union). Without the union, foobar would be a void pointer and needs to be
cast to a pointer to the appropriate struct (e.g. foo_t). And that's the second
version above.

> If you come up with version 2.0 of the "public.h" header and it includes
> a new structure in the union, that structure could affect the alignment
> requirements of the union.
>
> If the only code that allocates such union objects is aware of the
> change, the alignment requirements should still satisfy the old union
> version, as long as you don't remove the old struct members, right?
>
> I wouldn't expect that other users' code would care... Their foos and
> bars are still aligned, and 'foo_t' and 'bar_t' still has the same tag
> and same definition[n1256.pdf:6.2.7p1]. I'd view the use of a "newer"
> union rather than the "outdated" union as "type-punning", under these
> circumstances. I could be mistaken.

I'm not sure either, that's why I'm asking :-)

Jef Driesen

5/17/2011 6:10:00 PM

0

On 16/05/11 21:48, Shao Miller wrote:
> On 5/16/2011 15:43, Shao Miller wrote:
>> If you come up with version 2.0 of the "public.h" header and it includes
>> a new structure in the union, that structure could affect the alignment
>> requirements of the union.
>>
>> If the only code that allocates such union objects is aware of the
>> change, the alignment requirements should still satisfy the old union
>> version, as long as you don't remove the old struct members, right?
>>
>> I wouldn't expect that other users' code would care... Their foos and
>> bars are still aligned, and 'foo_t' and 'bar_t' still has the same tag
>> and same definition[n1256.pdf:6.2.7p1]. I'd view the use of a "newer"
>> union rather than the "outdated" union as "type-punning", under these
>> circumstances. I could be mistaken.
>
> Of course, since union version 2.0 could also change the size, hopefully
> your old library users aren't using arrays with elements of this union
> type! Then they'd be borked. :)

If an application would define an array, that shouldn't cause any problems
because everywhere in that application they will be using one version of the
union. It's only when you have to pass them between the application and the
library that there can be a mismatch in version.

But in my case the data only needs to be passed from the library to the
application, and never the other way around. And I will be passing pointers, not
the structs or unions directly. The reason for the pointers is exactly the
ability to add new structs in the future without breaking backwards
compatibility, which is not possible with a plain union. Using the union would
be for convenience, to avoid the casting of a void pointer to the appropriate
struct.