public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
* Re: vtable on stack
@ 2010-02-02 13:27 Frank Winter
  2010-02-02 15:04 ` Axel Freyn
  0 siblings, 1 reply; 13+ messages in thread
From: Frank Winter @ 2010-02-02 13:27 UTC (permalink / raw)
  To: axel-freyn; +Cc: gcc-help

Hi Axel!

Thank you!

Using references is a good idea. Indeed, B::f() gets called.

I agree that making the object const, is quite a drawback and having a 
temporary object living parallel is that good either.

--
Frank

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: vtable on stack
  2010-02-02 13:27 vtable on stack Frank Winter
@ 2010-02-02 15:04 ` Axel Freyn
  2010-02-02 18:13   ` Patrick Horgan
  0 siblings, 1 reply; 13+ messages in thread
From: Axel Freyn @ 2010-02-02 15:04 UTC (permalink / raw)
  To: gcc-help

Hi Frank,
> Using references is a good idea. Indeed, B::f() gets called.
>
> I agree that making the object const, is quite a drawback and having a  
> temporary object living parallel is that good either.
There is also another possibilty you could maybe think about: If you
create an additional structure, which "caches" the pointer to A or B and
returns references to it, you can have this new structure living on the
stack - and do with it whatever you want (e.g. you can insert it into a
std::vector without problems):
 
struct Cache{
  Cache( A* _a):a(_a){}
  A *a;
  A& operator()(){ return *a;}
};


int main() {
    A* x = new B();
    x->f();         // Class B

    Cache y = new B();
    y().f();          // Class A
}

this prints also "Class B".
However, as always there are some drawbacks...
 - my example will introduce memory-leaks, as the pointers are never
   deleted -> add a destructor to "Cache"
 - if you want to copy "y", you have to be careful as to what you are
   doing (shall the object pointed to be "a" also be copied - or shall
   both "Cache"'s use the same A? If you copy it: Which
   copy-constructor - the one of A or the one of B? If not: then each
   Cache has to count, how often the object A is used - in order to be
   able to write a working destructor...

Maybe the most powerful way would be:
 - add a virtual function "A* make_copy()" to struct A, which returns a
   copy of A
 - overload it in struct B
 - overload the Copy-Constructor and the assignement-operator for struct
   Cache, such that they use "make_copy" (this guarantees that
   "Cache y = new B();  Cache y1 = y;"  and "Cache y = new A(); Cache y1
    = y" are treated differently.
 - Add a destructor to Cache, which deletes the pointer
 - If - in addition - you also add "const A& operator()()const", you can
   also use "Cache" when it is a constant.

With that, you would simulate quite well a "virtual class on the stack"
- you can do almost everything with it. The only drawback I see is the
necessity to explicitely write "y()" instead of "y" (or some arbitrary
other memberfunction like "*y" or "y.get()"), which returns the
reference to A*.


Axel

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: vtable on stack
  2010-02-02 15:04 ` Axel Freyn
@ 2010-02-02 18:13   ` Patrick Horgan
  2010-02-02 20:39     ` Axel Freyn
  0 siblings, 1 reply; 13+ messages in thread
From: Patrick Horgan @ 2010-02-02 18:13 UTC (permalink / raw)
  To: gcc-help

Axel Freyn wrote:
> There is also another possibilty you could maybe think about: If you
> create an additional structure, which "caches" the pointer to A or B and
> returns references to it, you can have this new structure living on the
> stack - and do with it whatever you want (e.g. you can insert it into a
> std::vector without problems):
>  
> struct Cache{
>   Cache( A* _a):a(_a){}
>   A *a;
>   A& operator()(){ return *a;}
> };
>   
I like this whole thing, but wouldn't operator* be more natural to 
return the reference?

Patrick

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: vtable on stack
  2010-02-02 18:13   ` Patrick Horgan
@ 2010-02-02 20:39     ` Axel Freyn
  2010-02-02 21:35       ` Patrick Horgan
  0 siblings, 1 reply; 13+ messages in thread
From: Axel Freyn @ 2010-02-02 20:39 UTC (permalink / raw)
  To: gcc-help

Hi Patrick,
On Tue, Feb 02, 2010 at 09:56:19AM -0800, Patrick Horgan wrote:
>> There is also another possibilty you could maybe think about: If you
>> create an additional structure, which "caches" the pointer to A or B and
>> returns references to it, you can have this new structure living on the
>> stack - and do with it whatever you want (e.g. you can insert it into a
>> std::vector without problems):
>>  struct Cache{
>>   Cache( A* _a):a(_a){}
>>   A *a;
>>   A& operator()(){ return *a;}
>> };
>>   
> I like this whole thing, but wouldn't operator* be more natural to  
> return the reference?
Even if we are probably quite off-topic now: Yes, you are absolutely
right. I only used () as, when I first (already a few years ago) wrote a
similar class (a bit more complex) which I wanted to use for fast
matrix-vector calculations, I realized a problem with "*": it can lead
to unresolved overloads when you write something like
class GeneralCache { ... };
class Cache : public GeneralCache { ... };
... operator * (const GeneralCache &, const GeneralCache & );
Cache a, b, c;
c = a * b;
Now is "*" dereferencing b, or is it multiplying a and b? 
I don't remember the precise definitions I used - but at least g++
claimed it would have been an unresolvable overloading (And my reading
and partial understanding of the C++-standard did confirm this). Of
course, by adding parenthesis or temporaries it would be possible to
solve the problem, but I switched to operator() ...


Axel

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: vtable on stack
  2010-02-02 20:39     ` Axel Freyn
@ 2010-02-02 21:35       ` Patrick Horgan
  0 siblings, 0 replies; 13+ messages in thread
From: Patrick Horgan @ 2010-02-02 21:35 UTC (permalink / raw)
  To: gcc-help

Axel Freyn wrote:
> Hi Patrick,
> On Tue, Feb 02, 2010 at 09:56:19AM -0800, Patrick Horgan wrote:
>   
>>> There is also another possibilty you could maybe think about: If you
>>> create an additional structure, which "caches" the pointer to A or B and
>>> returns references to it, you can have this new structure living on the
>>> stack - and do with it whatever you want (e.g. you can insert it into a
>>> std::vector without problems):
>>>  struct Cache{
>>>   Cache( A* _a):a(_a){}
>>>   A *a;
>>>   A& operator()(){ return *a;}
>>> };
>>>   
>>>       
>> I like this whole thing, but wouldn't operator* be more natural to  
>> return the reference?
>>     
> Even if we are probably quite off-topic now: Yes, you are absolutely
> right. I only used () as, when I first (already a few years ago) wrote a
> similar class (a bit more complex) which I wanted to use for fast
> matrix-vector calculations, I realized a problem with "*": it can lead
> to unresolved overloads when you write something like
> class GeneralCache { ... };
> class Cache : public GeneralCache { ... };
> ... operator * (const GeneralCache &, const GeneralCache & );
>   
Yes, I see this, but this is the nature of the beast.  Even without user 
defined types you can get exactly this same problem and sometimes need 
parenthesis to make clear what you mean.  When you override operator() 
people expect the meaning to be function or constructor.

> Cache a, b, c;
> c = a * b;
> Now is "*" dereferencing b, or is it multiplying a and b? 
>   
Frustrating when you try to come up with a good example off the top of 
your head and you can't think of one, huh?  It always happens to me 
too.  Of course this would only make sense for the binary operator*, the 
unary would one just be a syntax error, and current gcc would use the 
binary one without warnings.

Patrick

^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: vtable on stack
  2010-02-02 13:47       ` Frank Winter
@ 2010-02-02 13:53         ` John (Eljay) Love-Jensen
  0 siblings, 0 replies; 13+ messages in thread
From: John (Eljay) Love-Jensen @ 2010-02-02 13:53 UTC (permalink / raw)
  To: Frank Winter, Kevin P. Fleming; +Cc: gcc-help

Hi Frank,

> You mentioned a vector of references. As far as I [know] this is not possible.

You are correct, a std::vector of C++ references is not possible.

You would have to use a std::vector of pointers, or a std::vector of smart pointers.  (Sometimes smart pointers are called references, just to muddy the water.)

For smart pointers, I recommend Boost's smart pointers:
http://www.boost.org/doc/libs/1_41_0/libs/smart_ptr/smart_ptr.htm

Sincerely,
--Eljay

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: vtable on stack
  2010-02-02 13:40     ` Kevin P. Fleming
@ 2010-02-02 13:47       ` Frank Winter
  2010-02-02 13:53         ` John (Eljay) Love-Jensen
  0 siblings, 1 reply; 13+ messages in thread
From: Frank Winter @ 2010-02-02 13:47 UTC (permalink / raw)
  To: Kevin P. Fleming; +Cc: John (Eljay) Love-Jensen, gcc-help

Hi Kevin!

>
> That's not quite correct here; you are not inserting a B into the vector
> at all. In the second call to push_back(), a temporary B object is

Thank you for pointing that out!

You mentioned a vector of references. As far as I now this is not 
possible.

vector<A&> vec;
or
A& vec[N];

Correct me if I am wrong. But I believe this is illegal C++ code. The 
compiler would have to transform a reference into a pointer.

--
Frank

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: vtable on stack
  2010-02-02 12:45   ` Frank Winter
  2010-02-02 13:11     ` Florian Weimer
@ 2010-02-02 13:40     ` Kevin P. Fleming
  2010-02-02 13:47       ` Frank Winter
  1 sibling, 1 reply; 13+ messages in thread
From: Kevin P. Fleming @ 2010-02-02 13:40 UTC (permalink / raw)
  To: Frank Winter; +Cc: John (Eljay) Love-Jensen, gcc-help

Frank Winter wrote:

> Nobody would want to write this. But somebody might want to use objects
> with virtual functions and define hers object on the stack, like:
> 
> vector<A> ve;
> ve.push_back(A());
> ve.push_back(B());
> ve.push_back(A());
> 
> iterate over it and call ::f(). Then always A::f() gets called, because
> its a vector of A. But a B is also an A. Thatfor I can insert it into
> the vector.

That's not quite correct here; you are not inserting a B into the vector
at all. In the second call to push_back(), a temporary B object is
instantiated on the stack, and then A's copy constructor is called to
create an A from it in the space allocated inside the vector (then the
temporary B object is destroyed). The vector does *not* contain a B
object at any time. As has previously been mentioned, the only way to
use polymorphism with objects in containers is to use pointer-to-object
or reference-to-object semantics; if you use value semantics, then the
objects will be sliced to the type that the container is defined to hold.

-- 
Kevin P. Fleming
Digium, Inc. | Director of Software Technologies
445 Jan Davis Drive NW - Huntsville, AL 35806 - USA
skype: kpfleming | jabber: kpfleming@digium.com
Check us out at www.digium.com & www.asterisk.org

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: vtable on stack
  2010-02-02 12:45   ` Frank Winter
@ 2010-02-02 13:11     ` Florian Weimer
  2010-02-02 13:40     ` Kevin P. Fleming
  1 sibling, 0 replies; 13+ messages in thread
From: Florian Weimer @ 2010-02-02 13:11 UTC (permalink / raw)
  To: Frank Winter; +Cc: John (Eljay) Love-Jensen, gcc-help

* Frank Winter:

> I think, its not possible to make use of the vtable with objects
> living on the stack.

This is not true.  C++'s brand of polymorphism does not interact well
with copying (due to slicing).  Your examples stress this point.

Put differently, you want to create an object of a type which is not
known at compile-time.  This is not directly possible in C++.
Workarounds generally use the heap because unknown types have unknown
sizes, and objects of unknown size don't mix well with stack
allocation.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: vtable on stack
  2010-02-02 12:41 ` John (Eljay) Love-Jensen
  2010-02-02 12:41   ` Axel Freyn
@ 2010-02-02 12:45   ` Frank Winter
  2010-02-02 13:11     ` Florian Weimer
  2010-02-02 13:40     ` Kevin P. Fleming
  1 sibling, 2 replies; 13+ messages in thread
From: Frank Winter @ 2010-02-02 12:45 UTC (permalink / raw)
  To: John (Eljay) Love-Jensen; +Cc: gcc-help

Dear Eljay,

thank you for your reply!

>
> To fix your example, do this:
>   B y = B();

You are right. This creates a B on the stack and B::f() gets called.
Nevertheless, here virtual functions are not involved. The vtable is not 
used when calling y.f()

My example created an A on the stack, and assigned a B to it, without 
turning the A into a B. Right!

But, I wanted to use virtual functions with class objects purley living 
on the stack _without_ doing some sort of casting:

B y = B();
dynamic_cast<A*>(&y)->f(); // B::f() called
(*(A*)(&y)).f();           // B::f() called

Nobody would want to write this. But somebody might want to use 
objects with virtual functions and define hers object on the stack, like:

vector<A> ve;
ve.push_back(A());
ve.push_back(B());
ve.push_back(A());

iterate over it and call ::f(). Then always A::f() gets called, because 
its a vector of A. But a B is also an A. Thatfor I can insert it into the 
vector.

This example can only be realized with

vector<A*>.

I think, its not possible to make use of the vtable with objects 
living on the stack.

--
Frank


^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: vtable on stack
  2010-02-02 12:11 Frank Winter
@ 2010-02-02 12:41 ` John (Eljay) Love-Jensen
  2010-02-02 12:41   ` Axel Freyn
  2010-02-02 12:45   ` Frank Winter
  0 siblings, 2 replies; 13+ messages in thread
From: John (Eljay) Love-Jensen @ 2010-02-02 12:41 UTC (permalink / raw)
  To: Frank Winter, gcc-help

Hi Frank,

> Do virtual functions work with class objects created on the stack?

Yes.

> Consider the example below.

I think you may be misunderstanding this line:
   A y = B();

That makes an A on the stack, since y is an A.  y does not become a B from the assignment.  Rather B is sliced to an A.

So that A y = B(); is not analogous to:
   A* x = new B();

To fix your example, do this:
   B y = B();

Sincerely,
--Eljay

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: vtable on stack
  2010-02-02 12:41 ` John (Eljay) Love-Jensen
@ 2010-02-02 12:41   ` Axel Freyn
  2010-02-02 12:45   ` Frank Winter
  1 sibling, 0 replies; 13+ messages in thread
From: Axel Freyn @ 2010-02-02 12:41 UTC (permalink / raw)
  To: gcc-help

Hi Frank,
On Tue, Feb 02, 2010 at 04:06:56AM -0800, John (Eljay) Love-Jensen wrote:
> Hi Frank,
> 
> > Do virtual functions work with class objects created on the stack?
> 
> Yes.
> 
> > Consider the example below.
> 
> I think you may be misunderstanding this line:
>    A y = B();
> 
> That makes an A on the stack, since y is an A.  y does not become a B
> from the assignment.  Rather B is sliced to an A.
> 
> So that A y = B(); is not analogous to:
>    A* x = new B();
> 
> To fix your example, do this:
>    B y = B();

Another possibility would be to use references and write:
  const A &y = B();
(the "const" is necessary, as you can't create a non-const reference to
a temporary object (that's a limitation I really don't like - sometimes
it would be nice to have:-)). Of course, the drawback is that y is
constant, so you have to define the member functions as:
 virtual void f() const { cout << "Class A" << endl; }
and
 void f() const { cout << "Class B" << endl; }

Then, y.f() outputs "Class B".

A third way is to use an additional variable, where you store the
B-object (so it's no temporary, and you can create a non-constant
reference to it), and then create a reference of type A to it:
B y1 = B();
A & y = y1;

Then, y refers to an object of type B and "y.f()" outputs "Class B".

I think, it depends on your situation what is the "best" solution.

Axel

^ permalink raw reply	[flat|nested] 13+ messages in thread

* vtable on stack
@ 2010-02-02 12:11 Frank Winter
  2010-02-02 12:41 ` John (Eljay) Love-Jensen
  0 siblings, 1 reply; 13+ messages in thread
From: Frank Winter @ 2010-02-02 12:11 UTC (permalink / raw)
  To: gcc-help

Do virtual functions work with class objects created on the stack?
Consider the example below.

First the class object is created on the heap and the f() call binding
is done via vtable as supposed.

Second, the class lives on the stack. B is created, assignment operator 
to A called. But the f() call prints "class A". Maybe here the vtable is 
not copied..?

I used structs here to avoid con/destructor calls.

How would one use virtual functions with objects living on the stack. Is 
it possible at all?

--
Frank



#include <iostream>
using namespace std;

struct A {
    virtual void f() { cout << "Class A" << endl; }
};

struct B: A {
    void f() { cout << "Class B" << endl; }
};


int main() {
   A* x = new B();
   x->f();         // Class B

   A y = B();
   y.f();          // Class A
}



^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2010-02-02 20:41 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-02-02 13:27 vtable on stack Frank Winter
2010-02-02 15:04 ` Axel Freyn
2010-02-02 18:13   ` Patrick Horgan
2010-02-02 20:39     ` Axel Freyn
2010-02-02 21:35       ` Patrick Horgan
  -- strict thread matches above, loose matches on Subject: below --
2010-02-02 12:11 Frank Winter
2010-02-02 12:41 ` John (Eljay) Love-Jensen
2010-02-02 12:41   ` Axel Freyn
2010-02-02 12:45   ` Frank Winter
2010-02-02 13:11     ` Florian Weimer
2010-02-02 13:40     ` Kevin P. Fleming
2010-02-02 13:47       ` Frank Winter
2010-02-02 13:53         ` John (Eljay) Love-Jensen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).