Topic: Permit virtual function returns to be any type


Author: DeadMG <wolfeinstein@gmail.com>
Date: Mon, 7 Jan 2013 06:38:24 -0800 (PST)
Raw View
------=_Part_267_9399455.1357569504443
Content-Type: text/plain; charset=ISO-8859-1

Often, we see classes which return objects of other classes from their
virtual functions in mirrored hierarchies, such as factories. This is fine-
for a while.

struct Base {
    virtual ~Base() {}
};
struct Derived : Base {
    virtual ~Derived() {}
};
struct BaseFactory {
    virtual Base* Create() = 0;
};
struct DerivedFactory : BaseFactory {
    Derived* Create() { return new Derived(); }
};

But, of course, a responsible C++ user will observe that the Create method
is, in fact, horrendously unsafe through it's use of manual memory
management. The problem is that this can't be fixed by changing the Create
method to use an appropriate smart pointer, as virtual function returns are
strictly limited to only covariant return types. I propose that it should
be changed to be any return type which can be implicitly converted to the
original return type. This should permit the use of smart pointers in this
situation- amongst others.

--




------=_Part_267_9399455.1357569504443
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

Often, we see classes which return objects of other classes from their virt=
ual functions in mirrored hierarchies, such as factories. This is fine- for=
 a while.<div><br></div><div>struct Base {</div><div>&nbsp; &nbsp; virtual =
~Base() {}<br>};</div><div>struct Derived : Base {<br>&nbsp; &nbsp; virtual=
 ~Derived() {}<br>};</div><div>struct BaseFactory {<br>&nbsp; &nbsp; virtua=
l Base* Create() =3D 0;</div><div>};</div><div>struct DerivedFactory : Base=
Factory {<br>&nbsp; &nbsp; Derived* Create() { return new Derived(); }</div=
><div>};</div><div><br></div><div>But, of course, a responsible C++ user wi=
ll observe that the Create method is, in fact, horrendously unsafe through =
it's use of manual memory management. The problem is that this can't be fix=
ed by changing the Create method to use an appropriate smart pointer, as vi=
rtual function returns are strictly limited to only covariant return types.=
 I propose that it should be changed to be any return type which can be imp=
licitly converted to the original return type. This should permit the use o=
f smart pointers in this situation- amongst others.</div>

<p></p>

-- <br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />

------=_Part_267_9399455.1357569504443--

.


Author: Ville Voutilainen <ville.voutilainen@gmail.com>
Date: Mon, 7 Jan 2013 21:05:08 +0200
Raw View
On 7 January 2013 16:38, DeadMG <wolfeinstein@gmail.com> wrote:
> Often, we see classes which return objects of other classes from their
> virtual functions in mirrored hierarchies, such as factories. This is fine-
> for a while.
> struct Base {
>     virtual ~Base() {}
> };
> struct Derived : Base {
>     virtual ~Derived() {}
> };
> struct BaseFactory {
>     virtual Base* Create() = 0;
> };
> struct DerivedFactory : BaseFactory {
>     Derived* Create() { return new Derived(); }
> };
> But, of course, a responsible C++ user will observe that the Create method
> is, in fact, horrendously unsafe through it's use of manual memory
> management. The problem is that this can't be fixed by changing the Create
> method to use an appropriate smart pointer, as virtual function returns are
> strictly limited to only covariant return types. I propose that it should be
> changed to be any return type which can be implicitly converted to the
> original return type. This should permit the use of smart pointers in this
> situation- amongst others.

The problem I see with this proposal is that it would seem to increase
the cost of (some)
virtual calls, since a c++ implementation would then be required to
perform a user-specified
conversion at runtime, and there may be ABI implications, too.

--




.


Author: DeadMG <wolfeinstein@gmail.com>
Date: Mon, 7 Jan 2013 11:06:37 -0800 (PST)
Raw View
------=_Part_866_20649033.1357585597504
Content-Type: text/plain; charset=ISO-8859-1

The only cost increase would be the cost of the conversion the user
requested- hardly an undue penalty.

As for ABI, I don't think it should have any implications there. All the
existing code still behaves identically to how it does today.

--




------=_Part_866_20649033.1357585597504
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

The only cost increase would be the cost of the conversion the user request=
ed- hardly an undue penalty.<div><br></div><div>As for ABI, I don't think i=
t should have any implications there. All the existing code still behaves i=
dentically to how it does today.</div>

<p></p>

-- <br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />

------=_Part_866_20649033.1357585597504--

.


Author: Ville Voutilainen <ville.voutilainen@gmail.com>
Date: Mon, 7 Jan 2013 21:10:19 +0200
Raw View
On 7 January 2013 21:06, DeadMG <wolfeinstein@gmail.com> wrote:
> The only cost increase would be the cost of the conversion the user
> requested- hardly an undue penalty.

Well, perhaps it would be a good idea to have an implementation and measure
whether the penalty is undue or not.

> As for ABI, I don't think it should have any implications there. All the
> existing code still behaves identically to how it does today.

I don't see how you can add support for a runtime operation which may
or may not need
to invoke a user-specified conversion without any ABI changes.

--




.


Author: DeadMG <wolfeinstein@gmail.com>
Date: Mon, 7 Jan 2013 11:13:58 -0800 (PST)
Raw View
------=_Part_144_8671456.1357586038918
Content-Type: text/plain; charset=ISO-8859-1


>
> Well, perhaps it would be a good idea to have an implementation and
> measure
> whether the penalty is undue or not.


What, you want to have a user-defined conversion without paying for it?

Well, in a common implementation with vtables, then changing the body of
the function pointed to by the vtable won't change the signature or calling
convention or any such thing. Because that's all you'd be doing- changing
the body of some compiler-generated functions. Or to be more accurate,
requiring the compiler to generate some functions that are now illegal.
That's all.

--




------=_Part_144_8671456.1357586038918
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

<blockquote class=3D"gmail_quote" style=3D"margin: 0px 0px 0px 0.8ex; borde=
r-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style=
: solid; padding-left: 1ex;">Well, perhaps it would be a good idea to have =
an implementation and measure&nbsp;<br>whether the penalty is undue or not.=
&nbsp;</blockquote><div><br></div><div>What, you want to have a user-define=
d conversion without paying for it?&nbsp;</div><div><br></div>Well, in a co=
mmon implementation with vtables, then changing the body of the function po=
inted to by the vtable won't change the signature or calling convention or =
any such thing. Because that's all you'd be doing- changing the body of som=
e compiler-generated functions. Or to be more accurate, requiring the compi=
ler to generate some functions that are now illegal. That's all.

<p></p>

-- <br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />

------=_Part_144_8671456.1357586038918--

.


Author: Felipe Magno de Almeida <felipe.m.almeida@gmail.com>
Date: Mon, 7 Jan 2013 17:16:42 -0200
Raw View
On Mon, Jan 7, 2013 at 5:13 PM, DeadMG <wolfeinstein@gmail.com> wrote:
>> Well, perhaps it would be a good idea to have an implementation and
>> measure
>> whether the penalty is undue or not.
>
>
> What, you want to have a user-defined conversion without paying for it?
>
> Well, in a common implementation with vtables, then changing the body of the
> function pointed to by the vtable won't change the signature or calling
> convention or any such thing. Because that's all you'd be doing- changing
> the body of some compiler-generated functions. Or to be more accurate,
> requiring the compiler to generate some functions that are now illegal.
> That's all.

Wouldn't you need to have various functions for each possible conversion
in the hierarchy?

> --

Regards,
--
Felipe Magno de Almeida

--




.


Author: Ville Voutilainen <ville.voutilainen@gmail.com>
Date: Mon, 7 Jan 2013 21:17:22 +0200
Raw View
On 7 January 2013 21:13, DeadMG <wolfeinstein@gmail.com> wrote:
>> Well, perhaps it would be a good idea to have an implementation and
>> measure
>> whether the penalty is undue or not.
> What, you want to have a user-defined conversion without paying for it?

With the current rules, I can control where and when that cost gets
incurred - and that's
when I perform a downcast from a Base* or a smart_ptr<Base>.

> Well, in a common implementation with vtables, then changing the body of the
> function pointed to by the vtable won't change the signature or calling
> convention or any such thing. Because that's all you'd be doing- changing
> the body of some compiler-generated functions. Or to be more accurate,
> requiring the compiler to generate some functions that are now illegal.
> That's all.

I don't quite see how it's that simple. The call site, which is not
statically determined,
performs the upcast if the call was from an entry point where the
return type is Base*
instead of Derived*. The function body just returns a Derived*, and
the caller converts
if necessary. I don't see how it's different with this change, the
function body would
return what it's declared to return and the call site must decide
whether to do a conversion
or not, and then do the conversion if need be.

--




.


Author: DeadMG <wolfeinstein@gmail.com>
Date: Mon, 7 Jan 2013 11:20:15 -0800 (PST)
Raw View
------=_Part_52_4797474.1357586415975
Content-Type: text/plain; charset=ISO-8859-1

You wouldn't need any more functions than you do now if you return a raw
pointer. You need a small thunk to fix up "this", call the derived
function, and fix down the return argument. All I'm proposing is that "fix
down" can be more flexible than before. It would not require changing ABIs
or generating more functions than now or changing the implementation of
inheritance.

It's just the same as before, except where the compiler previously
generated code to convert a pointer from Derived* to Base*, it might now
generate code to perform some user-defined implicit conversion.

--




------=_Part_52_4797474.1357586415975
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

You wouldn't need any more functions than you do now if you return a raw po=
inter. You need a small thunk to fix up "this", call the derived function, =
and fix down the return argument. All I'm proposing is that "fix down" can =
be more flexible than before. It would not require changing ABIs or generat=
ing more functions than now or changing the implementation of inheritance.<=
div><br></div><div>It's just the same as before, except where the compiler =
previously generated code to convert a pointer from Derived* to Base*, it m=
ight now generate code to perform some user-defined implicit conversion.</d=
iv>

<p></p>

-- <br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />

------=_Part_52_4797474.1357586415975--

.


Author: Nevin Liber <nevin@eviloverlord.com>
Date: Mon, 7 Jan 2013 13:46:44 -0600
Raw View
--0015174bddc62b183d04d2b81998
Content-Type: text/plain; charset=ISO-8859-1

On 7 January 2013 13:20, DeadMG <wolfeinstein@gmail.com> wrote:

> You wouldn't need any more functions than you do now if you return a raw
> pointer. You need a small thunk to fix up "this", call the derived
> function, and fix down the return argument. All I'm proposing is that "fix
> down" can be more flexible than before.


Is this purely an extension, or is there a cost (code/space/speed) to a
hierarchy that only has the C++11 covariant return types?

Also, what happens in the case:

struct RA {};
struct RB { operator RA() const; };
struct RC { operator RB() const; };

struct VA { virtual RA clone() const; };
struct VB : VA { virtual RB clone() const; };
struct VC : VA { virtual RC clone() const; };

int main() { RA ra(VC().clone()); }

Is the expectation that the object stored in ra is the result of:

static_cast<RA>(static_cast<RB>(VC().VC::clone()))

It's just the same as before, except where the compiler previously
> generated code to convert a pointer from Derived* to Base*, it might now
> generate code to perform some user-defined implicit conversion.
>

In the cases where that cost is free now (on typical implementations), is
it still free?
--
 Nevin ":-)" Liber  <mailto:nevin@eviloverlord.com>  (847) 691-1404

--




--0015174bddc62b183d04d2b81998
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

On 7 January 2013 13:20, DeadMG <span dir=3D"ltr">&lt;<a href=3D"mailto:wol=
feinstein@gmail.com" target=3D"_blank">wolfeinstein@gmail.com</a>&gt;</span=
> wrote:<br><div class=3D"gmail_quote"><blockquote class=3D"gmail_quote" st=
yle=3D"margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

You wouldn&#39;t need any more functions than you do now if you return a ra=
w pointer. You need a small thunk to fix up &quot;this&quot;, call the deri=
ved function, and fix down the return argument. All I&#39;m proposing is th=
at &quot;fix down&quot; can be more flexible than before.</blockquote>

<div><br>Is this purely an extension, or is there a cost (code/space/speed)=
 to a hierarchy that only has the C++11 covariant return types?<br><br>Also=
, what happens in the case:<br><br>struct RA {};<br>struct RB { operator RA=
() const; };<br>

struct RC { operator RB() const; };<br><br>struct VA { virtual RA clone() c=
onst; };<br>struct VB : VA { virtual RB clone() const; };<br>struct VC : VA=
 { virtual RC clone() const; };<br><br>int main() { RA ra(VC().clone()); }<=
br>

<br>Is the expectation that the object stored in ra is the result of:<br><b=
r>static_cast&lt;RA&gt;(static_cast&lt;RB&gt;(VC().VC::clone()))<br><br></d=
iv><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left=
:1px #ccc solid;padding-left:1ex">

<div>It&#39;s just the same as before, except where the compiler previously=
 generated code to convert a pointer from Derived* to Base*, it might now g=
enerate code to perform some user-defined implicit conversion.<br></div>

</blockquote></div><br clear=3D"all">In the cases where that cost is free n=
ow (on typical implementations), is it still free?<br>-- <br>=A0Nevin &quot=
;:-)&quot; Liber=A0 &lt;mailto:<a href=3D"mailto:nevin@eviloverlord.com" ta=
rget=3D"_blank">nevin@eviloverlord.com</a>&gt;=A0 (847) 691-1404

<p></p>

-- <br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />

--0015174bddc62b183d04d2b81998--

.


Author: DeadMG <wolfeinstein@gmail.com>
Date: Mon, 7 Jan 2013 11:58:46 -0800 (PST)
Raw View
------=_Part_1416_4678299.1357588726025
Content-Type: text/plain; charset=ISO-8859-1

Yes. If you don't request any conversion, then none has to be performed.

You might expect a call to a user-defined conversion function (which is
presumably used in at least one other place) to consume slightly more code
than a pointer change (which is presumably just one subtraction). But for
one, that doesn't violate "Pay for what you use", because you only pay the
cost if you use it, and for two, the cost is no more than using that
implicit conversion in some other place- for example, in the body of the
derived function itself, which is what's typically done now.

For your proposed case, then VC::clone() is invoked, and then overload
resolution kicks in from there. I'm not sure that in this specific
instance, that can actually compile, because it would involve two
user-defined implicit conversions and I think the Standard allows only one.
However, you do raise an interesting point, which is that the compiler may
end up invoking more than one implicit conversion (for example if clone()
were called on an instance of VC which was referred to by a pointer of
static type VA), which is new.

--




------=_Part_1416_4678299.1357588726025
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

Yes. If you don't request any conversion, then none has to be performed.<di=
v><br></div><div>You might expect a call to a user-defined conversion funct=
ion (which is presumably used in at least one other place) to consume sligh=
tly more code than a pointer change (which is presumably just one subtracti=
on). But for one, that doesn't violate "Pay for what you use", because you =
only pay the cost if you use it, and for two, the cost is no more than usin=
g that implicit conversion in some other place- for example, in the body of=
 the derived function itself, which is what's typically done now.</div><div=
><br></div><div>For your proposed case, then VC::clone() is invoked, and th=
en overload resolution kicks in from there. I'm not sure that in this speci=
fic instance, that can actually compile, because it would involve two user-=
defined implicit conversions and I think the Standard allows only one. Howe=
ver, you do raise an interesting point, which is that the compiler may end =
up invoking more than one implicit conversion (for example if clone() were =
called on an instance of VC which was referred to by a pointer of static ty=
pe VA), which is new.</div>

<p></p>

-- <br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />

------=_Part_1416_4678299.1357588726025--

.


Author: Christof Meerwald <cmeerw@cmeerw.org>
Date: Mon, 7 Jan 2013 21:42:45 +0100
Raw View
On Mon, Jan 07, 2013 at 09:17:22PM +0200, Ville Voutilainen wrote:
> I don't quite see how it's that simple. The call site, which is not
> statically determined,
> performs the upcast if the call was from an entry point where the
> return type is Base*
> instead of Derived*. The function body just returns a Derived*, and
> the caller converts
> if necessary. I don't see how it's different with this change, the
> function body would
> return what it's declared to return and the call site must decide
> whether to do a conversion
> or not, and then do the conversion if need be.

Consider the following C++11 example:

struct A
{
  virtual void g();
};

struct B
{
  virtual B *f();
};

struct C : A, B
{
  virtual C *f()
  {
    return this;
  }
};

void bar(B *b)
{
  B *ptr = b->f();
}

void foo()
{
  C c;
  bar(&c);
}

When using gcc, the vtable entry for f actually points to a compiler
generated thunk that performs the return type conversion from C* to
B*. And I guess the proposal is to allow compiler generated thunks to
perform user-defined conversions (and not just derived-to-base
conversions).


Christof

--

http://cmeerw.org                              sip:cmeerw at cmeerw.org
mailto:cmeerw at cmeerw.org                   xmpp:cmeerw at cmeerw.org

--




.


Author: Ville Voutilainen <ville.voutilainen@gmail.com>
Date: Mon, 7 Jan 2013 22:50:43 +0200
Raw View
On 7 January 2013 22:42, Christof Meerwald <cmeerw@cmeerw.org> wrote:
> When using gcc, the vtable entry for f actually points to a compiler
> generated thunk that performs the return type conversion from C* to
> B*. And I guess the proposal is to allow compiler generated thunks to
> perform user-defined conversions (and not just derived-to-base
> conversions).

Thank you. That would indicate that there's no additional performance problem.

I like the semantics of the proposal, they certainly seem useful. If
there weren't any
implementation concerns (and I'm not claiming there are, at least for
gcc, according
to your explanation), I would wholeheartedly want this change. So the
remaining question
is, why are the current rules restricted to covariant types?

--




.


Author: DeadMG <wolfeinstein@gmail.com>
Date: Mon, 7 Jan 2013 13:19:35 -0800 (PST)
Raw View
------=_Part_578_3614393.1357593575850
Content-Type: text/plain; charset=ISO-8859-1

Presumably, the original authors did not want to permit most implicit
conversions, did want to permit covariance, but did not foresee the
problems with smart pointers. Ultimately, something like

struct Base {
    virtual int f() = 0;
};
struct Derived : Base {
    virtual float f();
};

probably isn't the smartest move in the world. But as the price of
permitting strongly-typed smart pointer returns, it's probably worth it.

--




------=_Part_578_3614393.1357593575850
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

Presumably, the original authors did not want to permit most implicit conve=
rsions, did want to permit covariance, but did not foresee the problems wit=
h smart pointers. Ultimately, something like<div><br></div><div>struct Base=
 {</div><div>&nbsp; &nbsp; virtual int f() =3D 0;</div><div>};</div><div>st=
ruct Derived : Base {<br>&nbsp; &nbsp; virtual float f();</div><div>};</div=
><div><br></div><div>probably isn't the smartest move in the world. But as =
the price of permitting strongly-typed smart pointer returns, it's probably=
 worth it.</div>

<p></p>

-- <br />
&nbsp;<br />
&nbsp;<br />
&nbsp;<br />

------=_Part_578_3614393.1357593575850--

.