<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On 2016–01–09, at 11:35 AM, Hubert Tong <<a href="mailto:hubert.reinterpretcast@gmail.com" class="">hubert.reinterpretcast@gmail.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">Yes,<span class="Apple-converted-space"> </span></span><span style="font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; font-family: monospace, monospace;" class="">std::launder</span><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class=""><span class="Apple-converted-space"> </span>requires a static type; however, it does not limit the ability of<span class="Apple-converted-space"> </span></span><span style="font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; font-family: monospace, monospace;" class="">memcpy</span><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class=""><span class="Apple-converted-space"> </span>to operate without knowing the type of the object being copied. The<span class="Apple-converted-space"> </span></span><span style="font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; font-family: monospace, monospace;" class="">std::launder</span><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class=""><span class="Apple-converted-space"> </span>call is involved<span class="Apple-converted-space"> </span></span><i style="font-family: Helvetica; font-size: 12px; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class="">after</i><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class=""><span class="Apple-converted-space"> </span>the completion of<span class="Apple-converted-space"> </span></span><span style="font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; font-family: monospace, monospace;" class="">memcpy</span><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class=""><span class="Apple-converted-space"> </span>to access the object that the<span class="Apple-converted-space"> </span></span><span style="font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; font-family: monospace, monospace;" class="">memcpy</span><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">initialized.</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""></div></blockquote></div><br class=""><div class="">Right, but in type erasure, the static type must be determined by inspecting the blob somehow. (Stashing a discriminating value elsewhere is one solution, but it’s more common and often more efficient to use an abstract base class or a discriminator inside a union.)</div><div class=""><br class=""></div><div class="">My library would launder the <font face="Courier" class="">erasure_base</font> subobject to retrieve its dispatch table, but then it’d be stuck. Dispatching to a derived class would lead back to UB.</div><div class=""><br class=""></div><div class=""><div class="">One workaround could be to launder the same address repeatedly as the type becomes better resolved. For example, the call wrapper could launder a base class address, then perform an indirect call, then the callee could launder again to the derived class. For the common case of virtual dynamic dispatch, this sounds like it would incur UB before first line of the callee. My library doesn’t use <font face="Courier" class="">virtual</font>, but similar ones do. If only complete objects can be laundered, devirtualization could kick in… or launder could refuse to handle an abstract class at all. The workaround would also imply an excessive number of derived-type <font face="Courier" class="">launder</font> calls, which could compromise optimization by suggesting that bitwise manipulations are occurring when none are. Reloading the dispatch table pointer costs cycles.</div><div class=""><br class=""></div><div class="">Perhaps a second style of laundering could implement a compromise. First, <span style="font-family: Courier;" class="">auto &header = *launder(header_ptr)</span> gets a fully-formed <font face="Courier" class="">header</font> object from a blob, and then <font face="Courier" class="">auto &whole = launder_extend<whole_type>(header)</font> revises the object identity to make <font face="Courier" class="">header</font> a subobject sharing its address with another already-fully-formed object of type <font face="Courier" class="">whole</font>. (For example, <font face="Courier" class="">header</font> could be a base, a <font face="Courier" class="">union</font> member, or an initial <font face="Courier" class="">struct</font> member.) The <span style="font-family: Courier;" class="">launder_extend</span> function differs in that it acts only if its argument was believed to be a complete object (i.e. fresh from <font face="Courier" class="">launder</font>), and it only launders the remainder of the new complete object. To solve the <font face="Courier" class="">virtual</font> issue, do not let <font face="Courier" class="">launder</font> imply that its result is most-derived. Perhaps, let virtual dispatch implicitly do <font face="Courier" class="">launder_extend</font>.</div><div class=""><br class=""></div><div class="">This scheme leaves <font face="Courier" class="">launder</font> open-ended so a polymorphic object or union can be used, yet still laundered further. A simple implementation can opt to treat <font face="Courier" class="">launder_extend</font> the same as <font face="Courier" class="">launder</font>.</div><div class=""><br class=""></div><div class="">Example:</div><div class=""><font face="Courier" class="">struct discriminator { int value; };</font></div><div class=""><font face="Courier" class="">struct foo { discriminator d; int i; };</font></div><div class=""><font face="Courier" class="">struct bar { discriminator d; float f; };</font></div><div class=""><font face="Courier" class="">union foobar { foo a; bar b; };</font></div><div class=""><font face="Courier" class="">struct baz : discriminator { double x; };</font></div><div class=""><font face="Courier" class="">struct bad : discriminator { virtual ~ bad(); };</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class="">void unpack( discriminator * p ) {</font></div><div class=""><font face="Courier" class=""> std::launder( p ); // OK: now we can access p.</font></div><div class=""><font face="Courier" class=""> int disc = p->value;</font></div><div class=""><font face="Courier" class=""> if ( disc == 0 ) {</font></div><div class=""><font face="Courier" class=""> auto & f = std::launder_extend< foo >( * p ); // OK: now we can access a foo.</font></div><div class=""><font face="Courier" class=""> int q = p->value; // Load may be elided. Value is already in disc, equal to zero.</font></div><div class=""><font face="Courier" class=""> auto & fb = std::launder_extend< foobar >( * f ); // OK: a further extension to a super-object.</font></div><div class=""><font face="Courier" class=""> auto & f2 = std::launder_extend< foo >( * p ); // OK, no-op: p was already extended, becoming a subobject.</font></div><div class=""><font face="Courier" class=""> auto & b = std::launder_extend< bar >( * p ); // UB: there’s already a different object there.</font></div><div class=""><font face="Courier" class=""> auto & i = std::launder_extend< int >( * p ); // Library precondition violation: invalid object extension.</font></div><div class=""><font face="Courier" class=""> } else if ( disc == 1 ) {</font></div><div class=""><font face="Courier" class=""> baz & z = std::launder_extend< baz >( * p ); // OK, but implementation-dependent in theory.</font></div><div class=""><font face="Courier" class=""> } else if ( disc == 2 ) {</font></div><div class=""><font face="Courier" class=""> </font><span style="font-family: Courier;" class="">// Library precondition violation: no discriminator subobject shares an address with class bad.</span></div><div class=""><font face="Courier" class=""> bad & x = std::launder_extend< bad >( * p );</font></div><div class=""><font face="Courier" class=""> }</font></div><div class=""><font face="Courier" class="">}</font></div><div class=""><br class=""></div></div></body></html>