Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #61932 > unrolled thread
| Started by | "James Harris" <james.harris.1@gmail.com> |
|---|---|
| First post | 2015-05-13 11:31 +0100 |
| Last post | 2015-05-14 11:27 +0100 |
| Articles | 14 — 8 participants |
Back to article view | Back to comp.lang.c
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
| From | "James Harris" <james.harris.1@gmail.com> |
|---|---|
| Date | 2015-05-13 11:31 +0100 |
| Subject | C 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]
| From | Johann Klammer <klammerj@NOSPAM.a1.net> |
|---|---|
| Date | 2015-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]
| From | James Kuyper <jameskuyper@verizon.net> |
|---|---|
| Date | 2015-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]
| From | Ian Collins <ian-news@hotmail.com> |
|---|---|
| Date | 2015-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]
| From | "James Harris" <james.harris.1@gmail.com> |
|---|---|
| Date | 2015-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]
| From | James Kuyper <jameskuyper@verizon.net> |
|---|---|
| Date | 2015-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]
| From | "James Harris" <james.harris.1@gmail.com> |
|---|---|
| Date | 2015-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]
| From | James Kuyper <jameskuyper@verizon.net> |
|---|---|
| Date | 2015-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]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-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]
| From | Barry Schwarz <schwarzb@dqel.com> |
|---|---|
| Date | 2015-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]
| From | pa@see.signature.invalid (Pierre Asselin) |
|---|---|
| Date | 2015-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]
| From | "James Harris" <james.harris.1@gmail.com> |
|---|---|
| Date | 2015-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]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2015-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]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2015-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