I was reading an article
"http://msdn.microsoft.com/msdnmag/issues/1000/Winsock/default.aspx"
where I encountered a macro called CONTAINING_RECORD. A purpose of this
macro is to return a poniter to the extended structure.
After the pointer to the OVERLAPPED structure is returned, the
CONTAINING_RECORD macro can be used to obtain a pointer to the extended
structure.
OVERLAPPED *Overlap;
OVERLAPPEDPLUS *OverlapPlus;
typedef struct _OVERLAPPEDPLUS {
SOCKET s, sclient;
OVERLAPPED ol;
int OpCode;
WSABUF wbuf;
DWORD dwBytes, dwFlags;
// other useful information
} OVERLAPPEDPLUS;
OverlapPlus = CONTAINING_RECORD(Overlap, OVERLAPPEDPLUS, ol);
The defination of macro is as follows :
#define CONTAINING_RECORD(address, type, field)
((type *)((PCHAR)(address) - (ULONG_PTR)(&((type *)0)->field)))
Could anyone help me out to understand the operations being performed
in macro.
Regards
Dinesh
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
>The defination of macro is as follows :
>#define CONTAINING_RECORD(address, type, field)
>((type *)((PCHAR)(address) - (ULONG_PTR)(&((type *)0)->field)))
>
>Could anyone help me out to understand the operations being performed
>in macro.
There should be a backslash after the first line. It looks like this
macro calculates the offset of "field" within the structure "type"
given the address, i.e. a pointer to whatever "type" is.
However, this part seems to be wrong:
&((type *)0)->field
I believe that the C++ standard forbids casting 0 to a pointer type.
It might work in C, however.
--
Bob Hairgrove
NoSpam...@Home.com
> OVERLAPPED *Overlap;
> OVERLAPPEDPLUS *OverlapPlus;
>
> typedef struct _OVERLAPPEDPLUS {
> SOCKET s, sclient;
> OVERLAPPED ol;
> int OpCode;
> WSABUF wbuf;
> DWORD dwBytes, dwFlags;
> // other useful information
> } OVERLAPPEDPLUS;
>
> OverlapPlus = CONTAINING_RECORD(Overlap, OVERLAPPEDPLUS, ol);
>
> The defination of macro is as follows :
> #define CONTAINING_RECORD(address, type, field)
> ((type *)((PCHAR)(address) - (ULONG_PTR)(&((type *)0)->field)))
>
> Could anyone help me out to understand the operations being performed
> in macro.
How sad. This is typical C code, not C++, so you are posting in the
wrong newsgroup. And even in C it's non-portable (=bad) code because it
invokes undefined behavior. In C++ you have such a nice way to achieve
the same result without those tricks: just derive OVERLAPPEDPLUS from
OVERLAPPED and use a static_cast<>! Even in C you could avoid obscure
tricks by simply making ol the first member in OVERLAPPEDPLUS.
But you asked about the macro... I am reluctant to explain such a
horrible beast, but... well, the last part is computing the offset of
member ol inside an OVERLAPPEDPLUS struct. The macro then decrements the
given pointer (that is expected to point to an OVERLAPPED struct) by
that amount of bytes in order to obtain a pointer to the containing
struct (of type OVERLAPPEDPLUS). A slightly better way of writing the
same macro is the following:
#define CONTAINING_RECORD(address, type, field) \
((type *)((PCHAR)(address) - offsetof(type, field)))
because offsetof() is the portable (=good) way to compute the offset.
The macro still invokes undefined behavior, but it's better than before.
Just my opinion,
Ganesh
Why do you think that? Now whether you are then allowed to use such a
pointer to access field (of a non-existent) object is quite another
matter.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
Since the purpose of this macro is to obtain a pointer to an object
from the address of (any) one of its data members, I don't really see
how deriving OVERLAPPEDPLUS from OVERLAPPED would get us anywhere. At
least not as a general solution: since a class may not be able to
inherit from each of its data member classes. Especially those data
members, like ints and longs, which do not have class types at all.
Moreoever, I don't think that the macro has license to rearrange the
declared order of a struct's members to suit its tastes. Instead we
have to allow the possibility that a struct may be defined in a library
or by the OS and therefore be unalterable. And in any event such a
rearrangment would only work once - so it too would not provide a
general solution for re-implementing this macro.
>...
> A slightly better way of writing the
> same macro is the following:
>
> #define CONTAINING_RECORD(address, type, field) \
> ((type *)((PCHAR)(address) - offsetof(type, field)))
>
> because offsetof() is the portable (=good) way to compute the offset.
> The macro still invokes undefined behavior, but it's better than before.
I think that this suggested change is more than a "slight" improvement.
Granted there is still a lot of distasteful casting and pointer
arithmatic going on, but where is the undefined behavior? Assuming that
PCHAR is "const char *", this revised version looks perfectly portable
to me. And trading undefined for defined behavior is, in my view, quite
a big improvement by any measure.
Greg
>In article <f228121j14sq2lrcf...@4ax.com>, Bob Hairgrove
><inv...@bigfoot.com> writes
>>However, this part seems to be wrong:
>>
>> &((type *)0)->field
>>
>>I believe that the C++ standard forbids casting 0 to a pointer type.
>>It might work in C, however.
>
>Why do you think that? Now whether you are then allowed to use such a
>pointer to access field (of a non-existent) object is quite another
>matter.
Because the following is not allowed by the standard:
#define NULL ((void*)0)
--
Bob Hairgrove
NoSpam...@Home.com
C++ does not allow NULL to be defined as a void pointer for reasons of
type-safety - and not because the expression (void*) 0 is itself
illegal. In fact the null pointer constant can be converted to a null
pointer of any type with a reinterpret_cast (and, by extension, with
cast notation). See §5.2.10/9.
Greg
static_cast will do. It's an implicit conversion, but writing this
reply made me realise that boost::implicit_cast doesn't work in this
case.
James
But that is a quite different thing. In C++ NULL must not be macro
defined as (void *)0 because in C++ unlike C, there are no implicit
conversions from void* to other types of pointer.
However there is no 'ban' on using any pointer cast you like to apply to
0, you just have to be careful of the context.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
> >On 13 Mar 2006 05:27:54 -0500, Francis Glassborow
> ><fra...@robinton.demon.co.uk> wrote:
> >>In article <f228121j14sq2lrcf...@4ax.com>, Bob
> >>Hairgrove <inv...@bigfoot.com> writes
> >>>However, this part seems to be wrong:
> >>> &((type *)0)->field
> >>>I believe that the C++ standard forbids casting 0 to a
> >>>pointer type. It might work in C, however.
> >>Why do you think that? Now whether you are then allowed to
> >>use such a pointer to access field (of a non-existent)
> >>object is quite another matter.
> >Because the following is not allowed by the standard:
> >#define NULL ((void*)0)
> But that is a quite different thing. In C++ NULL must not be
> macro defined as (void *)0 because in C++ unlike C, there are
> no implicit conversions from void* to other types of pointer.
That's not the reason. There's no implicit conversions from int
to pointers either.
Part of the reason is that pre-standard C didn't allow NULL to
be defined like this either. Beyond that, I really don't know;
neither 0 nor ((void*)0) are really coherent, and the only real
solution is in the nullptr proposal.
> However there is no 'ban' on using any pointer cast you like
> to apply to 0, you just have to be careful of the context.
Especially because the semantics of casting a literal 0 are not
the same as casting an arbitrary (non-const) literal expression
of type int.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34