Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #4088 > unrolled thread
| Started by | Jef Driesen <jefdriesen@hotmail.com.invalid> |
|---|---|
| First post | 2011-05-16 20:06 +0200 |
| Last post | 2011-05-30 23:47 -0700 |
| Articles | 16 — 9 participants |
Back to article view | Back to comp.lang.c
Extending unions and ABI? Jef Driesen <jefdriesen@hotmail.com.invalid> - 2011-05-16 20:06 +0200
Re: Extending unions and ABI? Tom St Denis <tom@iahu.ca> - 2011-05-16 11:20 -0700
Re: Extending unions and ABI? jt@toerring.de (Jens Thoms Toerring) - 2011-05-16 19:12 +0000
Re: Extending unions and ABI? Shao Miller <sha0.miller@gmail.com> - 2011-05-16 14:38 -0400
Re: Extending unions and ABI? "Morris Keesan" <mkeesan@post.harvard.edu> - 2011-05-16 14:54 -0400
Re: Extending unions and ABI? Shao Miller <sha0.miller@gmail.com> - 2011-05-16 15:13 -0400
Re: Extending unions and ABI? Jef Driesen <jefdriesen@hotmail.com.invalid> - 2011-05-16 20:55 +0200
Re: Extending unions and ABI? Shao Miller <sha0.miller@gmail.com> - 2011-05-16 15:43 -0400
Re: Extending unions and ABI? Shao Miller <sha0.miller@gmail.com> - 2011-05-16 15:48 -0400
Re: Extending unions and ABI? Jef Driesen <jefdriesen@hotmail.com.invalid> - 2011-05-17 20:10 +0200
Re: Extending unions and ABI? Shao Miller <sha0.miller@gmail.com> - 2011-05-18 00:28 -0500
Re: Extending unions and ABI? Jef Driesen <jefdriesen@hotmail.com.invalid> - 2011-05-16 21:54 +0200
Re: Extending unions and ABI? Columbus sailed the ocean China Blue <chine.bleu@yahoo.com> - 2011-05-17 12:53 -0700
Re: Extending unions and ABI? "io_x" <a@b.c.invalid> - 2011-05-18 19:31 +0200
Re: Extending unions and ABI? gordonb.4d208@burditt.org (Gordon Burditt) - 2011-05-23 04:03 -0500
Re: Extending unions and ABI? Tim Rentsch <txr@alumni.caltech.edu> - 2011-05-30 23:47 -0700
| From | Jef Driesen <jefdriesen@hotmail.com.invalid> |
|---|---|
| Date | 2011-05-16 20:06 +0200 |
| Subject | Extending unions and ABI? |
| Message-ID | <yIdAp.1305$Wy2.200@newsfe23.ams2> |
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
[toc] | [next] | [standalone]
| From | Tom St Denis <tom@iahu.ca> |
|---|---|
| Date | 2011-05-16 11:20 -0700 |
| Message-ID | <aa7aac67-f6dc-40b0-9147-b593d82a6203@w21g2000yqm.googlegroups.com> |
| In reply to | #4088 |
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
[toc] | [prev] | [next] | [standalone]
| From | jt@toerring.de (Jens Thoms Toerring) |
|---|---|
| Date | 2011-05-16 19:12 +0000 |
| Message-ID | <93db8pFrevU1@mid.uni-berlin.de> |
| In reply to | #4092 |
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://toerring.de
[toc] | [prev] | [next] | [standalone]
| From | Shao Miller <sha0.miller@gmail.com> |
|---|---|
| Date | 2011-05-16 14:38 -0400 |
| Message-ID | <iqrqud$qdi$1@dont-email.me> |
| In reply to | #4088 |
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 '>'?
[toc] | [prev] | [next] | [standalone]
| From | "Morris Keesan" <mkeesan@post.harvard.edu> |
|---|---|
| Date | 2011-05-16 14:54 -0400 |
| Message-ID | <op.vvk75dlm5qv6o3@toshiba-laptop> |
| In reply to | #4095 |
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
[toc] | [prev] | [next] | [standalone]
| From | Shao Miller <sha0.miller@gmail.com> |
|---|---|
| Date | 2011-05-16 15:13 -0400 |
| Message-ID | <iqrt0n$uc9$1@dont-email.me> |
| In reply to | #4099 |
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.
[toc] | [prev] | [next] | [standalone]
| From | Jef Driesen <jefdriesen@hotmail.com.invalid> |
|---|---|
| Date | 2011-05-16 20:55 +0200 |
| Message-ID | <IqeAp.2489$ue1.1142@newsfe28.ams2> |
| In reply to | #4095 |
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.
[toc] | [prev] | [next] | [standalone]
| From | Shao Miller <sha0.miller@gmail.com> |
|---|---|
| Date | 2011-05-16 15:43 -0400 |
| Message-ID | <iqrupp$21b$1@dont-email.me> |
| In reply to | #4100 |
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.
[toc] | [prev] | [next] | [standalone]
| From | Shao Miller <sha0.miller@gmail.com> |
|---|---|
| Date | 2011-05-16 15:48 -0400 |
| Message-ID | <iqrv2c$2gv$1@dont-email.me> |
| In reply to | #4113 |
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. :)
[toc] | [prev] | [next] | [standalone]
| From | Jef Driesen <jefdriesen@hotmail.com.invalid> |
|---|---|
| Date | 2011-05-17 20:10 +0200 |
| Message-ID | <%RyAp.7647$Am5.554@newsfe05.ams2> |
| In reply to | #4114 |
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.
[toc] | [prev] | [next] | [standalone]
| From | Shao Miller <sha0.miller@gmail.com> |
|---|---|
| Date | 2011-05-18 00:28 -0500 |
| Message-ID | <iqvleg$23t$1@dont-email.me> |
| In reply to | #4171 |
On 5/17/2011 1:10 PM, Jef Driesen wrote:
> 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.
I'd figured that you'd meant that.
Hopefully a user doesn't do something like make a copy of a 'foobar_t';
they'd be using an outdated size if you changed it library-wise. And
yet fortunately, since they only know about 'foo_t' and 'bar_t', they'd
still copy as many bytes as they care about. :)
Along the lines of the suggestion of "Columbus sailed the ocean China
Blue," you could certainly pass a pointer to a common, first member.
This strategy is useful with such macros as 'container_of()' or
'CONTAINING_RECORD()' and I've used and seen that strategy used quite a bit.
A useful first member of 'foo_t' and 'bar_t' might be (but is obviously
not limited to being):
- A 'size_t' with the intention of expressing the size of the object
(potentially even useful as a make-shift signature if you can guarantee
all 'XXX_t' will have different sizes).
- Your original 'enum' value for distinguishing the type (you had
'foobar_type_t' for this). Woe be to you if the day comes that you
think the order of the 'enum' values would be prettier in some other order.
- A function pointer for pointing to a function whose type is useful
across all 'XXX_t'. While such a "handler" might not immediately seem
as useful as comparing against your 'enum' values, it can be useful as a
way to "query" the object; perhaps for information including... Its
type. Or the operations that are supported on that type/object. Or any
extensions available for that type/object. :)
- A little 'struct' type that you will be happy with for eternity. I
don't care much for this one, since I don't know what the future might
teach, and because it seems to stray from "keeping it simple," and
because it could cost more memory than some of the other options.
- A pointer to a little 'const struct' type that you will be happy with
for eternity. The pointed-to structure can be common across all 'foo_t'
and encompass information about that type.
The following example is also available (with some nice syntax
colouring) at:
http://codepad.org/sMZemHGx
/**** public.h */
/*** Object types */
typedef struct s_animal_ s_animal;
typedef struct s_cat_ s_cat;
typedef struct s_dog_ s_dog;
/*** Function types */
typedef void f_any(void);
typedef f_any * f_handler(s_animal *, f_any op);
typedef void f_pet(s_animal *);
/*** Struct/union definitions */
struct s_animal_ {
s_animal * self;
f_handler * handler;
};
struct s_cat_ {
s_animal animal;
int lives;
const char * pet_noise;
};
struct s_dog_ {
s_animal animal;
const char * pet_noise;
};
/*** Function declarations */
extern f_pet pet;
extern s_cat * make_cat(void);
extern s_dog * make_dog(void);
/**** private.c */
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
/*** Function definitions */
void pet(s_animal * animal) {
f_any * pet_func = animal->handler(animal, (f_any *)pet);
f_pet * pet_func2;
if (pet_func == (f_any *)0) {
puts("Huh? (Type \"help\" for help.)");
return;
};
pet_func2 = (f_pet *)pet_func;
pet_func2(animal);
return;
}
static f_pet pet_cat;
static void pet_cat(s_animal * animal) {
s_cat * cat = (void *)animal;
puts(cat->pet_noise);
return;
}
static f_handler handle_cat;
static f_any * handle_cat(s_animal * animal, f_any * op) {
if (op == (f_any *)pet)
return (f_any *)pet_cat;
return (f_any *)0;
}
s_cat * make_cat(void) {
s_cat * cat = malloc(sizeof *cat);
if (!cat) return NULL;
cat->animal.self = &cat->animal;
cat->animal.handler = handle_cat;
cat->lives = 9;
cat->pet_noise = "Purr, purr...";
return cat;
}
static f_pet pet_dog;
static void pet_dog(s_animal * animal) {
s_dog * dog = (void *)animal;
puts(dog->pet_noise);
return;
}
static f_handler handle_dog;
static f_any * handle_dog(s_animal * animal, f_any * op) {
if (op == (f_any *)pet)
return (f_any *)pet_dog;
return (f_any *)0;
}
s_dog * make_dog(void) {
s_dog * dog = malloc(sizeof *dog);
if (!dog) return NULL;
dog->animal.self = &dog->animal;
dog->animal.handler = handle_dog;
dog->pet_noise = "Pant, pant...";
return dog;
}
/**** test.c */
#include "public.h"
int main(void) {
s_cat * kitty = make_cat();
s_dog * doggy = make_dog();
if (kitty) {
pet(&kitty->animal);
free(kitty);
}
if (doggy) {
pet(&doggy->animal);
free(doggy);
}
return 0;
}
[toc] | [prev] | [next] | [standalone]
| From | Jef Driesen <jefdriesen@hotmail.com.invalid> |
|---|---|
| Date | 2011-05-16 21:54 +0200 |
| Message-ID | <OhfAp.2540$ue1.266@newsfe28.ams2> |
| In reply to | #4113 |
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 :-)
[toc] | [prev] | [next] | [standalone]
| From | Columbus sailed the ocean China Blue <chine.bleu@yahoo.com> |
|---|---|
| Date | 2011-05-17 12:53 -0700 |
| Message-ID | <chine.bleu-836BAB.12531417052011@news.eternal-september.org> |
| In reply to | #4088 |
In article <yIdAp.1305$Wy2.200@newsfe23.ams2>,
Jef Driesen <jefdriesen@hotmail.com.invalid> wrote:
> Suppose I have defined a union:
Because the address of the struct is also the address of its first element, an
alternate strategy is to begin each struct with a tag.
typedef struct {
int tag;
} tag_t;
> typedef struct foo_t {
int tag;
> ...
> } foo_t;
>
> typedef struct bar_t {
int tag;
> ...
> } bar_t;
So instead of
> typedef union foobar_t {
> foo_t foo;
> bar_t bar;
> } foobar_t;
you can use (tag_t*).
void xyzzy(tag_t *parameter) {
if (parameter->tag==FOO) {
foo_t *foo = (foo_t*)parameter;
...
}else if(parameter->tag==BAR) {
bar_t *bar = (bar_t*)parameter;
...
}
}
--
Damn the living - It's a lovely life. I'm whoever you want me to be.
Silver silverware - Where is the love? At least I can stay in character.
Oval swimming pool - Where is the love? Annoying Usenet one post at a time.
Damn the living - It's a lovely life. In 1492....
[toc] | [prev] | [next] | [standalone]
| From | "io_x" <a@b.c.invalid> |
|---|---|
| Date | 2011-05-18 19:31 +0200 |
| Message-ID | <4dd40217$0$38643$4fafbaef@reader1.news.tin.it> |
| In reply to | #4088 |
"Jef Driesen" <jefdriesen@hotmail.com.invalid> ha scritto nel messaggio
news:yIdAp.1305$Wy2.200@newsfe23.ams2...
> 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);
do you know that when the above function is call here
"foobar_type_t type" could be written in the stack so instead of pointer
many data...
i would access data with a pointer to char* or a pointer to void*
and not at union but to each data type
but i'm just a troll i could be wrong
[toc] | [prev] | [next] | [standalone]
| From | gordonb.4d208@burditt.org (Gordon Burditt) |
|---|---|
| Date | 2011-05-23 04:03 -0500 |
| Message-ID | <kMCdnXKTvNNrv0fQnZ2dnUVZ_hqdnZ2d@posted.internetamerica> |
| In reply to | #4088 |
> 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;
This presents a problem, although this particular one isn't really
a C standard issue. How was foobar_type_t documented? You may
find that some code assumes that "if it's not an apple it must be
a banana" and other code assumes that "if it's not a banana it must
be an apple". Then if the code is handed a cucumber, both sets are
confused but in different ways. Other code might contain code like:
assert(p->type == APPLE || p->type == BANANA);
If your documentation said that you have to be prepared for unknown
types and not touch types it doesn't recognize, you should be fine,
if everyone heeded that advice.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2011-05-30 23:47 -0700 |
| Message-ID | <kfnd3izqsd6.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #4088 |
Jef Driesen <jefdriesen@hotmail.com.invalid> writes:
> 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? [.. snip alternative ..]
Sorry to be late in responding here.
If, after adding another member to foobar_t, all translation
units that can see these members are recompiled, all is
well. Conversely if some TU's that can see those members
are not recompiled, technically that's undefined behavior.
So, any "public api" project source would need to be
recompiled, _unless_ it can't see the members of the union
type foobar_t. Assuming they don't access such members, but
only pass pointers around, a way to do that would be to just
declare the type
union foobar_t;
typedef union foobar_t foobar_t;
and _not_ define the contents of 'union foobar_t', and use just
that declaration for those "public api" sources/TU's that are
not to be recompiled. After that, if all TU's that _can_ access
the union's members are recompiled whenever its definition
changes, that stays inside the Standard's boundaries and does
not transgress, even technically, into undefined behavior.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c
csiph-web