Topic: Preliminary proposal: "downcasting" from a pointer


Author: "'Walt Karas' via ISO C++ Standard - Future Proposals" <std-proposals@isocpp.org>
Date: Tue, 2 Oct 2018 19:34:23 -0700 (PDT)
Raw View
------=_Part_2124_1628042150.1538534063365
Content-Type: multipart/alternative;
 boundary="----=_Part_2125_321801296.1538534063366"

------=_Part_2125_321801296.1538534063366
Content-Type: text/plain; charset="UTF-8"


That is, casting from a pointer to data member to a pointer to the
containing class instance.  Here is an implementation with macros, likely
to have high de facto portability.  Putting this capability in the base
language in some way would be more type-safe and more aesthetic.

#include <cstddef>
#include <utility>

// If FIELD_PTR is a pointer to a field specified by FIELD_SPEC in an
instance of a class named CLASS_NAME, then
// FIELD_TO_CLASS_PTR(CLASS_NAME, FIELD_SPEC, FIELD_PTR) returns a pointer
to the instance of CLASS_NAME.
//
// Example:
//
// struct S
// {
//   int m, n;
//   struct { int a, b, c; } ss;
//   int q;
// } s;
//
// assert(FIELD_TO_CLASS_PTR(S, ss.b, &(s.ss.b)) == &s);
//
#define FIELD_TO_CLASS_PTR(CLASS_NAME, FIELD_SPEC, FIELD_PTR)
                                     \
  ([](decltype(std::declval<CLASS_NAME>().FIELD_SPEC) *fldPtr) ->
CLASS_NAME * {                                \
    return reinterpret_cast<CLASS_NAME *>(reinterpret_cast<char *>(fldPtr)
- offsetof(CLASS_NAME, FIELD_SPEC)); \
  }(FIELD_PTR))

// Version for const, volatile, const volatile pointers.
//
// Example:
//
// struct S
// {
//   int m, n;
//   struct { int a, b, c; } ss;
//   int q;
// } s;
//
// const S *sp = &s;
//
// assert(CV_FIELD_TO_CLASS_PTR(const, S, ss.b, &(sp->ss.b)) == sp);
//
#define CV_FIELD_TO_CLASS_PTR(CVSPEC, CLASS_NAME, FIELD_SPEC, FIELD_PTR)
                                                  \
  ([](CVSPEC decltype(std::declval<CLASS_NAME>().FIELD_SPEC) *fldPtr) ->
CVSPEC CLASS_NAME * {                                \
    return reinterpret_cast<CVSPEC CLASS_NAME *>(reinterpret_cast<CVSPEC
char *>(fldPtr) - offsetof(CLASS_NAME, FIELD_SPEC)); \
  }(FIELD_PTR))

/*
NOTE: this is generalizing the technique of static_cast'ing from a base
class pointer to a derived class pointer.  It
allows this technique with class fields as well as base classes.  But it's
easy to make a mistake that the compiler
would not catch unfortunately, must be used very carefully.  The lambda
function is used so that at least the compiler
can check that the type of the pointer and the type of the class field
match.
*/

This technique is not strictly necessary, but I have found many cases where
I felt using it made the code clearer and more concise.

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/fa582cd9-7a72-4151-821f-d1c095303bf3%40isocpp.org.

------=_Part_2125_321801296.1538534063366
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div><br></div><div>That is, casting from a pointer to dat=
a member to a pointer to the containing class instance.=C2=A0 Here is an im=
plementation with macros, likely to have high de facto portability.=C2=A0 P=
utting this capability in the base language in some way would be more type-=
safe and more aesthetic.</div><br><div><div class=3D"prettyprint" style=3D"=
background-color: rgb(250, 250, 250); border-color: rgb(187, 187, 187); bor=
der-style: solid; border-width: 1px; overflow-wrap: break-word;"><code clas=
s=3D"prettyprint"><div class=3D"subprettyprint"><font color=3D"#660066"><di=
v class=3D"subprettyprint"></div><div class=3D"subprettyprint"><div class=
=3D"subprettyprint">#include &lt;cstddef&gt;</div><div class=3D"subprettypr=
int">#include &lt;utility&gt;<br><br></div><div class=3D"subprettyprint">//=
 If FIELD_PTR is a pointer to a field specified by FIELD_SPEC in an instanc=
e of a class named CLASS_NAME, then</div><div class=3D"subprettyprint">// F=
IELD_TO_CLASS_PTR(CLASS_NAME, FIELD_SPEC, FIELD_PTR) returns a pointer to t=
he instance of CLASS_NAME.</div><div class=3D"subprettyprint">//</div><div =
class=3D"subprettyprint">// Example:</div><div class=3D"subprettyprint">//<=
/div><div class=3D"subprettyprint">// struct S</div><div class=3D"subpretty=
print">// {</div><div class=3D"subprettyprint">//=C2=A0 =C2=A0int m, n;</di=
v><div class=3D"subprettyprint">//=C2=A0 =C2=A0struct { int a, b, c; } ss;<=
/div><div class=3D"subprettyprint">//=C2=A0 =C2=A0int q;</div><div class=3D=
"subprettyprint">// } s;</div></div><div class=3D"subprettyprint">//</div><=
div class=3D"subprettyprint">// assert(FIELD_TO_CLASS_PTR(S, ss.b, &amp;(s.=
ss.b)) =3D=3D &amp;s);</div><div class=3D"subprettyprint">//</div><div clas=
s=3D"subprettyprint">#define FIELD_TO_CLASS_PTR(CLASS_NAME, FIELD_SPEC, FIE=
LD_PTR)=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0\</div><div class=3D"subprettyprin=
t">=C2=A0 ([](decltype(std::declval&lt;CLASS_NAME&gt;().FIELD_SPEC) *fldPtr=
) -&gt; CLASS_NAME * {=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 \</div><div cla=
ss=3D"subprettyprint">=C2=A0 =C2=A0 return reinterpret_cast&lt;CLASS_NAME *=
&gt;(reinterpret_cast&lt;char *&gt;(fldPtr) - offsetof(CLASS_NAME, FIELD_SP=
EC)); \</div><div class=3D"subprettyprint">=C2=A0 }(FIELD_PTR))</div><div c=
lass=3D"subprettyprint"><br>// Version for const, volatile, const volatile =
pointers.</div><div class=3D"subprettyprint">//</div><div class=3D"subprett=
yprint">// Example:</div><div class=3D"subprettyprint">//</div><div class=
=3D"subprettyprint">// struct S</div><div class=3D"subprettyprint">// {</di=
v><div class=3D"subprettyprint">//=C2=A0 =C2=A0int m, n;</div><div class=3D=
"subprettyprint">//=C2=A0 =C2=A0struct { int a, b, c; } ss;</div><div class=
=3D"subprettyprint">//=C2=A0 =C2=A0int q;</div><div class=3D"subprettyprint=
">// } s;</div><div class=3D"subprettyprint">//</div><div class=3D"subprett=
yprint">// const S *sp =3D &amp;s;</div><div class=3D"subprettyprint">//</d=
iv><div class=3D"subprettyprint">// assert(CV_FIELD_TO_CLASS_PTR(const, S, =
ss.b, &amp;(sp-&gt;ss.b)) =3D=3D sp);</div><div class=3D"subprettyprint">//=
</div><div class=3D"subprettyprint">#define CV_FIELD_TO_CLASS_PTR(CVSPEC, C=
LASS_NAME, FIELD_SPEC, FIELD_PTR)=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 \=
</div><div class=3D"subprettyprint">=C2=A0 ([](CVSPEC decltype(std::declval=
&lt;CLASS_NAME&gt;().FIELD_SPEC) *fldPtr) -&gt; CVSPEC CLASS_NAME * {=C2=A0=
 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 \</div><div class=3D"subprettyprint">=C2=A0=
 =C2=A0 return reinterpret_cast&lt;CVSPEC CLASS_NAME *&gt;(reinterpret_cast=
&lt;CVSPEC char *&gt;(fldPtr) - offsetof(CLASS_NAME, FIELD_SPEC)); \</div><=
div class=3D"subprettyprint">=C2=A0 }(FIELD_PTR))<br><br></div><div class=
=3D"subprettyprint">/*</div><div class=3D"subprettyprint">NOTE: this is gen=
eralizing the technique of static_cast&#39;ing from a base class pointer to=
 a derived class pointer.=C2=A0 It</div><div class=3D"subprettyprint">allow=
s this technique with class fields as well as base classes.=C2=A0 But it&#3=
9;s easy to make a mistake that the compiler</div><div class=3D"subprettypr=
int">would not catch unfortunately, must be used very carefully.=C2=A0 The =
lambda function is used so that at least the compiler</div><div class=3D"su=
bprettyprint">can check that the type of the pointer and the type of the cl=
ass field match.</div><div class=3D"subprettyprint">*/</div></font></div></=
code></div><br>This technique is not strictly necessary, but I have found m=
any cases where I felt using it made the code clearer and more concise.</di=
v></div>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/fa582cd9-7a72-4151-821f-d1c095303bf3%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/fa582cd9-7a72-4151-821f-d1c095303bf3=
%40isocpp.org</a>.<br />

------=_Part_2125_321801296.1538534063366--

------=_Part_2124_1628042150.1538534063365--

.