Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #61932 > unrolled thread

C struct compatibility when they have the same initial members

Started by"James Harris" <james.harris.1@gmail.com>
First post2015-05-13 11:31 +0100
Last post2015-05-14 11:27 +0100
Articles 14 — 8 participants

Back to article view | Back to comp.lang.c


Contents

  C struct compatibility when they have the same initial members "James Harris" <james.harris.1@gmail.com> - 2015-05-13 11:31 +0100
    Re: C struct compatibility when they have the same initial members Johann Klammer <klammerj@NOSPAM.a1.net> - 2015-05-13 17:17 +0200
    Re: C struct compatibility when they have the same initial members James Kuyper <jameskuyper@verizon.net> - 2015-05-13 12:25 -0400
      Re: C struct compatibility when they have the same initial members Ian Collins <ian-news@hotmail.com> - 2015-05-14 14:33 +1200
      Re: C struct compatibility when they have the same initial members "James Harris" <james.harris.1@gmail.com> - 2015-05-14 08:04 +0100
        Re: C struct compatibility when they have the same initial members James Kuyper <jameskuyper@verizon.net> - 2015-05-15 00:19 -0400
          Re: C struct compatibility when they have the same initial members "James Harris" <james.harris.1@gmail.com> - 2015-05-15 21:32 +0100
            Re: C struct compatibility when they have the same initial members James Kuyper <jameskuyper@verizon.net> - 2015-05-15 17:11 -0400
              Re: C struct compatibility when they have the same initial members Keith Thompson <kst-u@mib.org> - 2015-05-15 15:27 -0700
    Re: C struct compatibility when they have the same initial members Barry Schwarz <schwarzb@dqel.com> - 2015-05-13 11:22 -0700
    Re: C struct compatibility when they have the same initial members pa@see.signature.invalid (Pierre Asselin) - 2015-05-14 01:33 +0000
      Re: C struct compatibility when they have the same initial members "James Harris" <james.harris.1@gmail.com> - 2015-05-14 08:15 +0100
        Re: C struct compatibility when they have the same initial members Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-05-14 11:22 +0100
          Re: C struct compatibility when they have the same initial members Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-05-14 11:27 +0100

#61932 — C struct compatibility when they have the same initial members

From"James Harris" <james.harris.1@gmail.com>
Date2015-05-13 11:31 +0100
SubjectC struct compatibility when they have the same initial members
Message-ID<miv94g$qme$1@dont-email.me>
The basic query is: If two or more struct types begin with the same 
types of members is it possible to treat a struct which has just the 
common part as compatible with the others without the use of casts? 
Specifics below.

Before someone tells me just to cast the pointers ... I know/think that 
can be done but was wondering if there is a more robust way that avoids 
them by simply making the types sufficiently compatible that casts 
become unnecessary.

This is for a fairly standard problem. I have a number of node types 
that begin with next and prev pointers so that the nodes are of the form

  struct node {
    struct node *next;
    struct node *prev;
    <type-specific data>
    <type-specific data>
    ...
    <type-specific data>
  };

The next and prev pointers will be used in order to make lists. To make 
manipulation of the lists more regular I thought about having separate 
head and tail nodes which pointed to the first and the last on any given 
list. I could define the head and tail nodes as full nodes but because 
they won't need anything more than the next and prev pointers I was 
thinking to save space and use nodes which were cut-down versions of the 
above. The head and tail node types would not need any of the 
<type-specific data> and would have just the two pointers.

First of all, is that a reasonable approach in C? Second, as nodes get 
added and deleted the pointers will need to be copied. Can that copying 
be done (including on the head and tail pseudo-nodes) without using 
casts?

I can think of three ways to approach this but I am not convinced that 
any of them is a good option.

1. Define the node types so that their initial two members match and one 
node type (the one used for the head and tail nodes) has just those two 
members. I don't think this will work without casts.

2. Define a struct which has just the pointers and embed that at the 
start of each of the other types as in

  struct common {
    struct common *next;
    struct common *prev;
  );

  struct specific1 {
    struct common c;
    <rest of this node type>
  };

Again, I don't think this will work without casts.

3. Define a union or unions such that the common struct type (the one 
with just the two pointers) is unioned with each of the other struct 
types. I see in C89 "3.3.2.3 Structure and union members" that this 
should make them compatible but it seems complex and even if it works I 
am not sure that it is a good way to go.

Any comments on the above options? Is there a better/best/idiomatic C 
way?

James

[toc] | [next] | [standalone]


#61939

FromJohann Klammer <klammerj@NOSPAM.a1.net>
Date2015-05-13 17:17 +0200
Message-ID<mivpv4$rmg$1@speranza.aioe.org>
In reply to#61932
On 05/13/2015 12:31 PM, James Harris wrote:
> The basic query is: If two or more struct types begin with the same 
> types of members is it possible to treat a struct which has just the 
> common part as compatible with the others without the use of casts? 
> Specifics below.
> 
> Before someone tells me just to cast the pointers ... I know/think that 
> can be done but was wondering if there is a more robust way that avoids 
> them by simply making the types sufficiently compatible that casts 
> become unnecessary.
> 
> This is for a fairly standard problem. I have a number of node types 
> that begin with next and prev pointers so that the nodes are of the form
> 
>   struct node {
>     struct node *next;
>     struct node *prev;
>     <type-specific data>
>     <type-specific data>
>     ...
>     <type-specific data>
>   };
> 
> The next and prev pointers will be used in order to make lists. To make 
> manipulation of the lists more regular I thought about having separate 
> head and tail nodes which pointed to the first and the last on any given 
> list. I could define the head and tail nodes as full nodes but because 
> they won't need anything more than the next and prev pointers I was 
> thinking to save space and use nodes which were cut-down versions of the 
> above. The head and tail node types would not need any of the 
> <type-specific data> and would have just the two pointers.
> 
> First of all, is that a reasonable approach in C? Second, as nodes get 
> added and deleted the pointers will need to be copied. Can that copying 
> be done (including on the head and tail pseudo-nodes) without using 
> casts?
Don't know, but everyone seems to be doing it...
list_head in the linux kernel sources, is one of those things, I think...
> 
> I can think of three ways to approach this but I am not convinced that 
> any of them is a good option.
> 
> 1. Define the node types so that their initial two members match and one 
> node type (the one used for the head and tail nodes) has just those two 
> members. I don't think this will work without casts.
> 
1) can be defeated by using nonstandard alignment when compiling.

> 2. Define a struct which has just the pointers and embed that at the 
> start of each of the other types as in
> 
>   struct common {
>     struct common *next;
>     struct common *prev;
>   );
> 
>   struct specific1 {
>     struct common c;
>     <rest of this node type>
>   };
> 
> Again, I don't think this will work without casts.
> 
2) seems rather popular. sometimes multiple list headers in one struct 
can be observed. Typically some function header_a_from_xyz() xyz_to_header_a()
or somesuch lurk nearby for converting those pointers back and forth 
(using ptr arithmetics +offsetof or somesuch cast variant)...

> 3. Define a union or unions such that the common struct type (the one 
> with just the two pointers) is unioned with each of the other struct 
> types. I see in C89 "3.3.2.3 Structure and union members" that this 
> should make them compatible but it seems complex and even if it works I 
> am not sure that it is a good way to go.
> 
> Any comments on the above options? Is there a better/best/idiomatic C 
> way?
> 
> James
> 
> 

[toc] | [prev] | [next] | [standalone]


#61941

FromJames Kuyper <jameskuyper@verizon.net>
Date2015-05-13 12:25 -0400
Message-ID<mivtsj$8bt$1@dont-email.me>
In reply to#61932
On 05/13/2015 06:31 AM, James Harris wrote:
> The basic query is: If two or more struct types begin with the same 
> types of members is it possible to treat a struct which has just the 
> common part as compatible with the others without the use of casts?

...
> 3. Define a union or unions such that the common struct type (the one 
> with just the two pointers) is unioned with each of the other struct 
> types. I see in C89 "3.3.2.3 Structure and union members" that this 
> should make them compatible but it seems complex and even if it works I 
> am not sure that it is a good way to go.

In C2011, the relevant clause is 6.5.2.3p6. I don't have a copy of C89,
so I can't be sure, but I don't think substantial changes have been made
to this clause:

> 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 completed 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

A couple of key points that you need to be aware of:
1. You must actually declare a union containing the structures you're
talking, and that declaration must be in scope at the point where you
rely upon this guarantee.
2. The guarantee applies only if there's an actual union object
containing the struct object you're working on.

People tend to think only in terms of the object layout, and that can
lead to misuse of this guarantee (and, as a practical matter, that often
works). Consider a union containing "struct person" and "struct mother"
objects, containing a common initial sequence of fields such as name and
birth date. Since all "struct person" objects are compatible with each
other, and all "struct mother" objects are compatible, since I can
access the common initial fields when the union is in scope, it seems I
should also be able to do so even when only the declaration of one of
the structs is visible.

However, except when 6.5.2.3p6 applies, such accesses violate the
aliasing rules (6.5p7). A compiler is therefore free to generate code
that assumes that the birth date field in a given "struct mother" object
is NOT the same object as the birth date field in a given "struct
person" object. This can only be an issue if at least one of them is
accessed through a pointer - if the actual objects are both visible in
the current scope, it will know whether or not they are the same.
Therefore, when it's allowed to assume that objects of different types
are different objects, it might keep an updated birth date value in a
register, rather than writing it to memory, and if you access the birth
date through a pointer to the other type, it might (or might not!)
retrieve the value from before the update, rather than after. It could
avoid such problems by comparing those pointers for equality - however,
without a union declaration in scope, 6.5p7 absolves it of any
responsibility for performing such a comparison.

If a union of both types is visible in scope, the generated code cannot
safely assume that pointers to the different types in the union are
pointers to different objects. Any optimizations based upon the
assumption that they do point to different objects would have to be
protected by a comparison of the pointers.
-- 
James Kuyper

[toc] | [prev] | [next] | [standalone]


#61966

FromIan Collins <ian-news@hotmail.com>
Date2015-05-14 14:33 +1200
Message-ID<crifr5FnbhrU1@mid.individual.net>
In reply to#61941
James Kuyper wrote:
> On 05/13/2015 06:31 AM, James Harris wrote:
>> The basic query is: If two or more struct types begin with the same
>> types of members is it possible to treat a struct which has just the
>> common part as compatible with the others without the use of casts?
>
> ....
>> 3. Define a union or unions such that the common struct type (the one
>> with just the two pointers) is unioned with each of the other struct
>> types. I see in C89 "3.3.2.3 Structure and union members" that this
>> should make them compatible but it seems complex and even if it works I
>> am not sure that it is a good way to go.
>
> In C2011, the relevant clause is 6.5.2.3p6. I don't have a copy of C89,
> so I can't be sure, but I don't think substantial changes have been made
> to this clause:
>
>> 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 completed 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
>
> A couple of key points that you need to be aware of:
> 1. You must actually declare a union containing the structures you're
> talking, and that declaration must be in scope at the point where you
> rely upon this guarantee.
> 2. The guarantee applies only if there's an actual union object
> containing the struct object you're working on.

I doubt that there have been changes, lots of well known code (X for 
example) has relied on this behaviour for decades!

-- 
Ian Collins

[toc] | [prev] | [next] | [standalone]


#61974

From"James Harris" <james.harris.1@gmail.com>
Date2015-05-14 08:04 +0100
Message-ID<mj1hci$g6h$1@dont-email.me>
In reply to#61941
"James Kuyper" <jameskuyper@verizon.net> wrote in message 
news:mivtsj$8bt$1@dont-email.me...
> On 05/13/2015 06:31 AM, James Harris wrote:
>> The basic query is: If two or more struct types begin with the same
>> types of members is it possible to treat a struct which has just the
>> common part as compatible with the others without the use of casts?
>
> ...
>> 3. Define a union or unions such that the common struct type (the one
>> with just the two pointers) is unioned with each of the other struct
>> types. I see in C89 "3.3.2.3 Structure and union members" that this
>> should make them compatible but it seems complex and even if it works 
>> I
>> am not sure that it is a good way to go.
>
> In C2011, the relevant clause is 6.5.2.3p6. I don't have a copy of 
> C89,
> so I can't be sure, but I don't think substantial changes have been 
> made
> to this clause:

I have been using

  http://port70.net/~nsz/c/c89/c89-draft.html

Even if I use the union guarantee it seems that I cannot avoid casting 
one pointer type to another. I say that because of the following. Say I 
have these types

struct task_ptrs {
  union task_node *next; /* Next on this task's wait queue */
  union task_node *prev; /* Previous on this task's wait queue */
};

struct task {
  union task_node *next; /* Next on this task's wait queue */
  union task_node *prev; /* Previous on this task's wait queue */
  int tid; /* Task id */
  unsigned cpu_registers[];
};

union task_node {
  struct task_ptrs task_ptrs;
  struct task task;
};

Given those, if the head and tail nodes are defined as

  union task_node head, tail;

then they will take up all the space of the larger component, something 
I wanted to avoid.

And if I define them as

  struct task_ptrs head, tail;

then, AIUI, they will be of different types to the struct task types 
used in the nodes so pointer copying may need casts anyway.... And if I 
have to use casts then I might as well do the job in a simpler way.

James

[toc] | [prev] | [next] | [standalone]


#62036

FromJames Kuyper <jameskuyper@verizon.net>
Date2015-05-15 00:19 -0400
Message-ID<mj3s2a$m42$1@dont-email.me>
In reply to#61974
On 05/14/2015 03:04 AM, James Harris wrote:
> "James Kuyper" <jameskuyper@verizon.net> wrote in message 
> news:mivtsj$8bt$1@dont-email.me...
>> On 05/13/2015 06:31 AM, James Harris wrote:
>>> The basic query is: If two or more struct types begin with the same
>>> types of members is it possible to treat a struct which has just the
>>> common part as compatible with the others without the use of casts?
>>
>> ...
>>> 3. Define a union or unions such that the common struct type (the one
>>> with just the two pointers) is unioned with each of the other struct
>>> types. I see in C89 "3.3.2.3 Structure and union members" that this
>>> should make them compatible but it seems complex and even if it works 
>>> I
>>> am not sure that it is a good way to go.
>>
>> In C2011, the relevant clause is 6.5.2.3p6. I don't have a copy of 
>> C89,
>> so I can't be sure, but I don't think substantial changes have been 
>> made
>> to this clause:
> 
> I have been using
> 
>   http://port70.net/~nsz/c/c89/c89-draft.html

Is there a reason you are concentrating on a standard that is already 16
years out-of-date, and a draft version of that standard? It seems
counterproductive. Versions of both the C99 and C2011 standards that re
almost identical to the official standard are available for free.

> Even if I use the union guarantee it seems that I cannot avoid casting 
> one pointer type to another. I say that because of the following. Say I 
> have these types
> 
> struct task_ptrs {
>   union task_node *next; /* Next on this task's wait queue */
>   union task_node *prev; /* Previous on this task's wait queue */
> };
> 
> struct task {
>   union task_node *next; /* Next on this task's wait queue */
>   union task_node *prev; /* Previous on this task's wait queue */
>   int tid; /* Task id */
>   unsigned cpu_registers[];
> };
> 
> union task_node {
>   struct task_ptrs task_ptrs;
>   struct task task;
> };
> 
> Given those, if the head and tail nodes are defined as
> 
>   union task_node head, tail;
> 
> then they will take up all the space of the larger component, something 
> I wanted to avoid.

Well, yes - the guarantee provided by 6.5.2.3p6 applies only to actual
union objects, not to individual structs that are not members of unions,
so each such object it necessarily takes up the space of the larger
struct type. It's really only useful if both struct types have members
not in the common initial sequence. Since your struct task_ptrs consists
solely of the common initial sequence, the union just adds complications
- you might as well just use struct task, leaving the tid and
cpu-registers with appropriate default values when unneeded.
-- 
James Kuyper

[toc] | [prev] | [next] | [standalone]


#62065

From"James Harris" <james.harris.1@gmail.com>
Date2015-05-15 21:32 +0100
Message-ID<mj5l3i$22e$1@dont-email.me>
In reply to#62036
"James Kuyper" <jameskuyper@verizon.net> wrote in message 
news:mj3s2a$m42$1@dont-email.me...
> On 05/14/2015 03:04 AM, James Harris wrote:
>> "James Kuyper" <jameskuyper@verizon.net> wrote in message
>> news:mivtsj$8bt$1@dont-email.me...
>>> On 05/13/2015 06:31 AM, James Harris wrote:

...

>> I have been using
>>
>>   http://port70.net/~nsz/c/c89/c89-draft.html
>
> Is there a reason you are concentrating on a standard that is already 
> 16
> years out-of-date, and a draft version of that standard? It seems
> counterproductive. Versions of both the C99 and C2011 standards that 
> re
> almost identical to the official standard are available for free.

Yes, there is. I want the code to work on a number of compilers so am 
writing for the lowest common denominator.

>> Even if I use the union guarantee it seems that I cannot avoid 
>> casting
>> one pointer type to another. I say that because of the following. Say 
>> I
>> have these types
>>
>> struct task_ptrs {
>>   union task_node *next; /* Next on this task's wait queue */
>>   union task_node *prev; /* Previous on this task's wait queue */
>> };
>>
>> struct task {
>>   union task_node *next; /* Next on this task's wait queue */
>>   union task_node *prev; /* Previous on this task's wait queue */
>>   int tid; /* Task id */
>>   unsigned cpu_registers[];
>> };
>>
>> union task_node {
>>   struct task_ptrs task_ptrs;
>>   struct task task;
>> };
>>
>> Given those, if the head and tail nodes are defined as
>>
>>   union task_node head, tail;
>>
>> then they will take up all the space of the larger component, 
>> something
>> I wanted to avoid.
>
> Well, yes - the guarantee provided by 6.5.2.3p6 applies only to actual
> union objects, not to individual structs that are not members of 
> unions,
> so each such object it necessarily takes up the space of the larger
> struct type. It's really only useful if both struct types have members
> not in the common initial sequence. Since your struct task_ptrs 
> consists
> solely of the common initial sequence, the union just adds 
> complications
> - you might as well just use struct task, leaving the tid and
> cpu-registers with appropriate default values when unneeded.

The fields tid and cpu_registers are just placeholders. There will 
eventually be a lot more in there. I would at least prefer not to 
allocate head and tail pseudonodes with all that stuff in them.

While I could see that unions would work I wanted to write the 
list-handling code as a separate module that would do list maintenance 
on various types of node (as long as they all began with the list's 
pointers). Unfortunately that seemed to require me to pass typedefs into 
the list handling code so that the pointers therein could be correctly 
typed. That approach did not seem reasonable so I am going to have a 
rethink on how best to do this.

James

[toc] | [prev] | [next] | [standalone]


#62071

FromJames Kuyper <jameskuyper@verizon.net>
Date2015-05-15 17:11 -0400
Message-ID<55566111.1000501@verizon.net>
In reply to#62065
On 05/15/2015 04:32 PM, James Harris wrote:
> "James Kuyper" <jameskuyper@verizon.net> wrote in message 
> news:mj3s2a$m42$1@dont-email.me...
>> On 05/14/2015 03:04 AM, James Harris wrote:
>>> "James Kuyper" <jameskuyper@verizon.net> wrote in message
>>> news:mivtsj$8bt$1@dont-email.me...
>>>> On 05/13/2015 06:31 AM, James Harris wrote:
> 
> ...
> 
>>> I have been using
>>>
>>>   http://port70.net/~nsz/c/c89/c89-draft.html
>>
>> Is there a reason you are concentrating on a standard that is already 
>> 16
>> years out-of-date, and a draft version of that standard? It seems
>> counterproductive. Versions of both the C99 and C2011 standards that 
>> re
>> almost identical to the official standard are available for free.
> 
> Yes, there is. I want the code to work on a number of compilers so am 
> writing for the lowest common denominator.

Then why are you using a draft standard? The draft differs from the
final standard in a number of ways (I don't know the details), and those
differences are NOT part of the "lowest common denominator".

...
> The fields tid and cpu_registers are just placeholders. There will 
> eventually be a lot more in there. I would at least prefer not to 
> allocate head and tail pseudonodes with all that stuff in them.

Then you'll have to use some other method of dealing with this. Relying
on the "common initial sequence" rule won't cut it.

> While I could see that unions would work I wanted to write the 
> list-handling code as a separate module that would do list maintenance 
> on various types of node (as long as they all began with the list's 
> pointers). Unfortunately that seemed to require me to pass typedefs into 
> the list handling code so that the pointers therein could be correctly 
> typed. That approach did not seem reasonable so I am going to have a 
> rethink on how best to do this.

The way to do that is to create a common struct which is the first
member of each of the individual node types. That common struct should
contain node pointers that point, not to the actual node objects, but to
the common struct within the other node objects. As the first element of
each node type, a pointer to the common struct can, when necessary, be
converted to a pointer to the node object itself - but the list
maintenance routines will never have need to do so. In addition to the
node pointers, the common struct should contain something that can be
used to determine which type the containing node is.
If there's a small number of different node types, just define an
enumerated type with one value for each node type, and switch(ptr->type)
in various places in your code.
If you're planning to make it more flexible, I'd be inspired by the
example of C++ to represent the type of each node by a pointer to a
table of functions that are specific to that node type (vtbl pointer);
one of those functions would return the size of the node, others would
implement copy, assignment, and cloning routines. Those are the only
things which the list management routines themselves need. The
pointed-at functions are will assume that they are being called for
objects of the appropriate type, and will perform the appropriate
conversions.

[toc] | [prev] | [next] | [standalone]


#62081

FromKeith Thompson <kst-u@mib.org>
Date2015-05-15 15:27 -0700
Message-ID<87zj55a330.fsf@kst-u.example.com>
In reply to#62071
James Kuyper <jameskuyper@verizon.net> writes:
[...]
>>> On 05/14/2015 03:04 AM, James Harris wrote:
[...]
>>>> I have been using
>>>>
>>>>   http://port70.net/~nsz/c/c89/c89-draft.html
[...]
> Then why are you using a draft standard? The draft differs from the
> final standard in a number of ways (I don't know the details), and those
> differences are NOT part of the "lowest common denominator".
[...]

The original C89 or C90 standard might not be easily available.  I don't
see it at webstore.ansi.org.

I have a PDF copy of the ISO C90 standard (I think I paid $18 for it),
but it's a very poor copy (no index, and the PDF was scanned from a
paper copy).

If you can find a copy of Schildt's "The Annotated ANSI C Standard"
(which I think is out of print), it includes the text of the C90
standard on the left-hand pages, though one page is missing.

WARNING: The right-hand pages consist of Schildt's annotations, which
are literally worse than useless.  Every copy of this book should be
accompanied by a printout of "The Annotated Annotated C Standard" by
Clive Feather, <http://www.lysator.liu.se/c/schildt.html>.

A pre-C89 draft might be good enough for most purposes.  Another
alternative is the N1256 C99 draft, which includes a foreword
summarizing the changes from C90 to C99.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[toc] | [prev] | [next] | [standalone]


#61949

FromBarry Schwarz <schwarzb@dqel.com>
Date2015-05-13 11:22 -0700
Message-ID<3b57la1qnukd4se2rbifcf2fqvhse9fkp4@4ax.com>
In reply to#61932
On Wed, 13 May 2015 11:31:46 +0100, "James Harris"
<james.harris.1@gmail.com> wrote:

>The basic query is: If two or more struct types begin with the same 
>types of members is it possible to treat a struct which has just the 
>common part as compatible with the others without the use of casts? 
>Specifics below.
>
>Before someone tells me just to cast the pointers ... I know/think that 
>can be done but was wondering if there is a more robust way that avoids 
>them by simply making the types sufficiently compatible that casts 
>become unnecessary.

Consider the problem of alignment.  If the first struct requires
alignment on a multiple of 4 (common if it contains int and pointer)
and the second requires alignment on a multiple of 8 (common if it
contains double), a pointer to the first that is not aligned on a
multiple of 8 cast as a pointer to the second causes undefined
behavior.

-- 
Remove del for email

[toc] | [prev] | [next] | [standalone]


#61963

Frompa@see.signature.invalid (Pierre Asselin)
Date2015-05-14 01:33 +0000
Message-ID<mj0u18$t0k$1@reader1.panix.com>
In reply to#61932
James Harris <james.harris.1@gmail.com> wrote:
> [ ... ]
> 2. Define a struct which has just the pointers and embed that at the 
> start of each of the other types as in

>   struct common {
>     struct common *next;
>     struct common *prev;
>   );

>   struct specific1 {
>     struct common c;
>     <rest of this node type>
>   };

> Again, I don't think this will work without casts.

I think it does work without a cast, but you have to name the
intermediate field, e.g. ptr->c.next, ptr->c.prev instead of just
ptr->next, ptr->prev.

-- 
pa at panix dot com

[toc] | [prev] | [next] | [standalone]


#61975

From"James Harris" <james.harris.1@gmail.com>
Date2015-05-14 08:15 +0100
Message-ID<mj1i0i$i2p$1@dont-email.me>
In reply to#61963
"Pierre Asselin" <pa@see.signature.invalid> wrote in message 
news:mj0u18$t0k$1@reader1.panix.com...
> James Harris <james.harris.1@gmail.com> wrote:
>> [ ... ]
>> 2. Define a struct which has just the pointers and embed that at the
>> start of each of the other types as in
>
>>   struct common {
>>     struct common *next;
>>     struct common *prev;
>>   );
>
>>   struct specific1 {
>>     struct common c;
>>     <rest of this node type>
>>   };
>
>> Again, I don't think this will work without casts.

> I think it does work without a cast, but you have to name the
> intermediate field, e.g. ptr->c.next, ptr->c.prev instead of just
> ptr->next, ptr->prev.


If I define the head and tail nodes as

  struct common head, tail;

and define a normal node, using the above name, as

  struct specific1 node;

Then it looks as though head.next will have the same type as 
node.c.prev, for example. Is that what you mean? It does seem doable 
though having to have a name for the common struct seems unnecessary. I 
wonder why C requires it.

James

[toc] | [prev] | [next] | [standalone]


#61982

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2015-05-14 11:22 +0100
Message-ID<87oaln4fxh.fsf@bsb.me.uk>
In reply to#61975
"James Harris" <james.harris.1@gmail.com> writes:

> "Pierre Asselin" <pa@see.signature.invalid> wrote in message 
> news:mj0u18$t0k$1@reader1.panix.com...
>> James Harris <james.harris.1@gmail.com> wrote:
>>> [ ... ]
>>> 2. Define a struct which has just the pointers and embed that at the
>>> start of each of the other types as in
>>
>>>   struct common {
>>>     struct common *next;
>>>     struct common *prev;
>>>   );
>>
>>>   struct specific1 {
>>>     struct common c;
>>>     <rest of this node type>
>>>   };
>>
>>> Again, I don't think this will work without casts.
>
>> I think it does work without a cast, but you have to name the
>> intermediate field, e.g. ptr->c.next, ptr->c.prev instead of just
>> ptr->next, ptr->prev.
>
>
> If I define the head and tail nodes as
>
>   struct common head, tail;
>
> and define a normal node, using the above name, as
>
>   struct specific1 node;
>
> Then it looks as though head.next will have the same type as 
> node.c.prev, for example.

You'd still need a cast to treat the struct common * you get as a
pointer to one of the specif types you might need to use (and you still
have potential alignment and aliasing issues).

If you put all the types into a union you can have the next and prev
pointers be to that union type which make for simple cast-free accesses
(though you do got a lot of member names -- macros can help with that).

<snip>
-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#61984

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2015-05-14 11:27 +0100
Message-ID<87fv6z4fpi.fsf@bsb.me.uk>
In reply to#61982
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
<snip>
> If you put all the types into a union you can have the next and prev
> pointers be to that union type which make for simple cast-free
> accesses

Which I see you are already doing.  That's the way to go in my opinion.

-- 
Ben.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c


csiph-web