Thread overview
Re: Difference between stack-allocated class and struct
May 02, 2011
Jonathan M Davis
May 02, 2011
Mariusz Gliwiński
May 02, 2011
Piotr Szturmaj
May 02, 2011
Jonathan M Davis
May 03, 2011
Sean Cavanaugh
May 02, 2011
> What are the differences between class instantiated by scope and struct
> itself?
> Two, that comes to my mind are:
> - vtable existance (yep, struct with inheritation - that's what i like)
> - lol, i just forgot while writing this e-mail

First off, I would point out that scoped classes are going away (though std.typecons.scoped serves as a replacement).

Now, structs are value types and are meant to be on the stack. When you assign one to another, a copy is made. If the original is altered, it doesn't affect the copy, and if the original goes away, the copy is unaffected.

Classes are reference types and are meant to be on the heap. When you assign one to another, you're merely copying the reference. Both refer to the same object. No copy is made. So, when you alter one, you also alter the other. When one goes away, it doesn't affect the other, but if the underlying object went away, then they would both be screwed.

Normally, class objects don't go away until the garbage collector collects them, but if you use scope (or std.typecons.scoped), then the object is on the stack instead of the heap, and as soon as that scope goes away, so does the object, so if you have references to it elsewhere, they're invalid, but the program won't know that, and it'll try and use them anyway. It can lead to serious bugs. Scoped classes are inherently unsafe, which is why they're going away.

It's like when you return an address to a local variable:

int* foo()
{
    int a = 7;

    return &a;
}

The pointer will be invalid, and you're going to have bugs. Now, the compiler's going to catch such a simple case as this, but what if you did something like this?

int* foo()
{
    int a = 7;

    return bar(&a);
}

int* bar(int* b)
{
    return b;
}

The compiler can't catch that. The same goes if you were to use a scoped class instead of an int. In general, scoped classes are a _bad_ idea and should not be used unless profiling has shown that they speed up critical code and you're _certain_ that you're using them in a manner which won't cause a reference to the class to escape the function and outlive the function call.

As for everything else structs are structs and classes are classes. All of the normal differences apply. That includes the fact that structs have no inheritance and no vtable while classes do have inheritance and do have a vtable. However, you pretty much lose polymorphism if you use a scoped class. You're guaranteeing that the class that you're using is _exactly_ that type and not a subclass of that type. This is because the object is put on the stack inside the function and the compiler must know its _exact_ size. It's exactly like what you get with classes on the stack in C++. Putting them on the stack loses any of the advantages of polymorhism and risks slicing ( http://en.wikipedia.org/wiki/Object_slicing ) when you assign a subclass object to a base class object.

So, if you're using a scoped class, you're _not_ getting the benefits of polymorphism. What you're doing is saying that you know that you have an object of a particular type - that _exact_ type - which you know is not going to need to exist once you exit that scope, and you want to increase the efficiency of that code, so you make it so that the object is created on the stack instead of the heap. And in so doing, you _must_ make sure that no reference to that object escapes - which often isn't easy if you have to pass that object to any other functions, and even if you verify that it's safe now, it might not be later.

Structs and classes are inherently different. Structs are value types and classes are reference types. All you're doing with a scoped class is forcing the object to be put on the stack instead of the heap, which doesn't really change anything for the class except for the fact that it must be of that _exact_ type (so no polymorphism), and you have to make sure that no references to it escape, or you could have serious bugs.

- Jonathan M Davis
May 02, 2011
Firstly, thanks for comprehensive answer and I'd like to excuse for my stupid mistakes, which are caused by learning a lot and not actually programming.

On date 2011-05-02 23:03, Jonathan M Davis wrote:
> Classes are reference types and are meant to be on the heap.
Yeah, value vs reference semantics - that was the thing i forgot...
> So, if you're using a scoped class, you're _not_ getting the benefits of
> polymorphism.
I completely didn't think about, that stack size needs to be known already at time of compiling. You just probably saved me a lot of problems.


So, scoped classes can't be used for filling gap in struct inheritance.

I'll clarify myself:
All i would need is extending - without polymorphism.
Containment, can be solution for fields which doesn't annoys so much (although image in auto-generated documentation, just like it's with subclassing, would be nice).
Unfortunately, the worse case is about methods, which have to be manually forwarded to contained struct.

So, does someone sees any nice solution for method forwarding as described? Should i make use of some mixin's?

Thanks,
Mariusz Gliwiński
May 02, 2011
> Firstly, thanks for comprehensive answer and I'd like to excuse for my stupid mistakes, which are caused by learning a lot and not actually programming.
> 
> On date 2011-05-02 23:03, Jonathan M Davis wrote:
> > Classes are reference types and are meant to be on the heap.
> 
> Yeah, value vs reference semantics - that was the thing i forgot...
> 
> > So, if you're using a scoped class, you're _not_ getting the benefits of polymorphism.
> 
> I completely didn't think about, that stack size needs to be known
> already at time of compiling. You just probably saved me a lot of problems.
> 
> 
> So, scoped classes can't be used for filling gap in struct inheritance.
> 
> I'll clarify myself:
> All i would need is extending - without polymorphism.
> Containment, can be solution for fields which doesn't annoys so much
> (although image in auto-generated documentation, just like it's with
> subclassing, would be nice).
> Unfortunately, the worse case is about methods, which have to be
> manually forwarded to contained struct.
> 
> So, does someone sees any nice solution for method forwarding as described? Should i make use of some mixin's?

alias this is supposed to be one of the better solutions for dealing with the sort of problem that you're looking at. Unfortunately, it's highly buggy at the moment, so it's questionable as to whether it would work. And even if it works, you can currently only have one alias this per type, so if you need more than that, you'd need another solution (eventually, alias this should work just fine, and you should be able to have multiple of them per type, but not yet).

At the moment, you pretty much need to do manual forwarding. Now, with the clever use of compile-time reflection via __traits and/or std.traits along with template mixins or string mixins, you should be able to get the compiler to generate all of the forwarded functions for you, but you'd still have to write the code for generating the mixins, which could be somewhat tricky. If you get it right though, then it would generate all of the appropriate functions regardless of whether any are added to or removed from the type that you're forwarding function calls to.

- Jonathan M Davis
May 02, 2011
Mariusz Gliwiński wrote:
> I'll clarify myself:
> All i would need is extending - without polymorphism.
> Containment, can be solution for fields which doesn't annoys so much
> (although image in auto-generated documentation, just like it's with
> subclassing, would be nice).
> Unfortunately, the worse case is about methods, which have to be
> manually forwarded to contained struct.
>
> So, does someone sees any nice solution for method forwarding as
> described? Should i make use of some mixin's?

You can use 'alias this':

http://www.digitalmars.com/d/2.0/class.html#AliasThis
May 03, 2011
Here is my prototype COM compile-time reflection based wrapper mixin (which I have abandoned in favor of alias this since it covers 95% of my use cases even though it isn't perfectly safe).  I am new at D so you have been warned, though this part of the language seems pretty straightforward enough.  It is possible the track down the argument names but takes some rather intricate parsing to do correctly (and the example someone linked me in another thread of mine chocked on const types due to parsing bugs).


http://snipt.org/xsu


The wrapped interface also needs to be a mixin so it can be created in the correct module, with visibility to all the types passed to the interface's methods.


So something like the following is going to fail miserably unless ComPtr is also made into a mixin and instantiated in the correct module.


struct ComPtr(T)
{
public:
    T m_Pointer;
    mixin(WrapComInterface!(T)("m_Pointer")
};