Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

CONTAINING_RECORD macro

1 view
Skip to first unread message

mdli...@yahoo.co.in

unread,
Mar 12, 2006, 5:56:34 AM3/12/06
to
Hi All

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! ]

Bob Hairgrove

unread,
Mar 12, 2006, 10:06:55 AM3/12/06
to
On 12 Mar 2006 05:56:34 -0500, mdli...@yahoo.co.in wrote:

>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

Alberto Ganesh Barbati

unread,
Mar 12, 2006, 12:07:26 PM3/12/06
to
mdli...@yahoo.co.in ha scritto:

> 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

Francis Glassborow

unread,
Mar 13, 2006, 5:27:54 AM3/13/06
to
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.


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

Greg Herlihy

unread,
Mar 13, 2006, 10:35:31 AM3/13/06
to
Alberto Ganesh Barbati wrote:
> mdli...@yahoo.co.in ha scritto:
>
> > 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.

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

Bob Hairgrove

unread,
Mar 13, 2006, 11:58:43 AM3/13/06
to
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)

--
Bob Hairgrove
NoSpam...@Home.com

Greg Herlihy

unread,
Mar 14, 2006, 3:46:04 AM3/14/06
to
Bob Hairgrove wrote:
> 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)

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

James Hopkin

unread,
Mar 14, 2006, 10:34:24 AM3/14/06
to

Greg Herlihy wrote:
>
> 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.
>

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

Francis Glassborow

unread,
Mar 14, 2006, 8:41:18 PM3/14/06
to
In article <245b12d3t88oa7eop...@4ax.com>, Bob Hairgrove
<inv...@bigfoot.com> writes
>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.

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

kanze

unread,
Mar 15, 2006, 8:10:59 AM3/15/06
to
Francis Glassborow wrote:
> In article <245b12d3t88oa7eop...@4ax.com>, Bob
> Hairgrove <inv...@bigfoot.com> writes

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

0 new messages