www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Classes or stucts :: Newbie

reply David Currie <curriedr iinet.net.au> writes:
I am new to D (like many have done C++ , Java ).

Can a class be instantiated on the stack ?

eg

class C
{
   private  int _I1;
   private  int _I2;

   public:

   this(int pI) // constructor
   {
     _I1 = pI;
     _I2 = pI + 1;
   }

// ...  other methods etc
}

void f()  // just a function
{

   C myC(3);  // C++ syntax BUT is there a d equivalent

}

It appears that D ASSUMES myC is really a myC*(in C++)

and therefore requires

C myC = new C(3);
// but this ALWAYS requires calling the memory allocator
// this is what Java does (forces your Class instance onto the Heap)

Is there any way in D to instantiate a stack object ?

Will a struct do?

Does a struct have a constructor (as opposed to an opcall?)

I would be very grateful for a response.

David Currie
Dec 20 2010
next sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Mon, 20 Dec 2010 18:00:31 +0300, David Currie <curriedr iinet.net.au>  
wrote:

 I am new to D (like many have done C++ , Java ).

 Can a class be instantiated on the stack ?

 eg

 class C
 {
    private  int _I1;
    private  int _I2;

    public:

    this(int pI) // constructor
    {
      _I1 = pI;
      _I2 = pI + 1;
    }

 // ...  other methods etc
 }

 void f()  // just a function
 {

    C myC(3);  // C++ syntax BUT is there a d equivalent

 }

 It appears that D ASSUMES myC is really a myC*(in C++)

 and therefore requires

 C myC = new C(3);
 // but this ALWAYS requires calling the memory allocator
 // this is what Java does (forces your Class instance onto the Heap)

 Is there any way in D to instantiate a stack object ?

 Will a struct do?

 Does a struct have a constructor (as opposed to an opcall?)

 I would be very grateful for a response.

 David Currie
Try the following: scope c = new C(3); Unfortunately last I've heard it's going to be deprecated in favor of a library solution: InSitu!(C) c = InSitu!(C)(3); // IIRC not implemented yet
Dec 19 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Denis Koroskin:

 Unfortunately last I've heard it's going to be deprecated in favor of a  
 library solution:
 
 InSitu!(C) c = InSitu!(C)(3); // IIRC not implemented yet
It's named scoped, see about its problems: http://d.puremagic.com/issues/show_bug.cgi?id=5115 Bye, bearophile
Dec 19 2010
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Mon, 20 Dec 2010 18:00:31 +0300, David Currie <curriedr iinet.net.au>  
wrote:

 I am new to D (like many have done C++ , Java ).

 Can a class be instantiated on the stack ?

 eg

 class C
 {
    private  int _I1;
    private  int _I2;

    public:

    this(int pI) // constructor
    {
      _I1 = pI;
      _I2 = pI + 1;
    }

 // ...  other methods etc
 }

 void f()  // just a function
 {

    C myC(3);  // C++ syntax BUT is there a d equivalent

 }

 It appears that D ASSUMES myC is really a myC*(in C++)

 and therefore requires

 C myC = new C(3);
 // but this ALWAYS requires calling the memory allocator
 // this is what Java does (forces your Class instance onto the Heap)

 Is there any way in D to instantiate a stack object ?

 Will a struct do?

 Does a struct have a constructor (as opposed to an opcall?)

 I would be very grateful for a response.

 David Currie
To your second question, yes, structs would do: struct C { this(int i) { ... } } C c = C(3);
Dec 19 2010
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
David Currie:

 I am new to D (like many have done C++ , Java ).
Welcome to D :-) What language do you refer to, D1 or D2? The answers here are about the latest versions of D2.
 Can a class be instantiated on the stack ?
There was a way built in the language to do that (using "scope", it works still but it's deprecated), but now you have to use the std library. See the std.conv.emplace, but keep in mind it has issues (more than the issues of "scope"!) like I think not calling the object destructor.
 It appears that D ASSUMES myC is really a myC*(in C++)
Right, in D class instances are always managed by reference, even when you allocate them on the stack using the emplace trick.
 // but this ALWAYS requires calling the memory allocator
 // this is what Java does (forces your Class instance onto the Heap)
A difference is that the Oracle Java Garbage Collector has a Eden memory for the newly allocated objects that's much faster than the current D GC :-)
 Will a struct do?
Sometimes a struct is enough. D structs are managed by value or by pointer, but they don't support inheritance. In D structs and classes are different (like the memory layout of a class instance is decided by the compiler, while struct fields are in memory as you want them, with the alignment you desire) and they are used for different purposes.
 Does a struct have a constructor (as opposed to an opcall?)
In D2 the struct constructor is this() as for classes. Bye, bearophile
Dec 19 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
 See the std.conv.emplace, 
Sorry, see std.typecons.scoped and its problems: http://d.puremagic.com/issues/show_bug.cgi?id=5115 Bye, bearophile
Dec 19 2010
parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 12/19/10, bearophile <bearophileHUGS lycos.com> wrote:
 See the std.conv.emplace,
Sorry, see std.typecons.scoped and its problems: http://d.puremagic.com/issues/show_bug.cgi?id=5115 Bye, bearophile
Is this another bug?: import std.stdio; import std.typecons; class A { ~this() { writeln("dtor"); } } void main() { { A a1 = scoped!A(); // doesn't call dtor after the scope auto a2 = scoped!A(); // calls dtor after the scope } }
Dec 19 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Andrej Mitrovic:

 Is this another bug?:
I don't exactly know what's going on, but I have added a modified version of your code to the issue 5115. Bye, bearophile
Dec 19 2010
prev sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
There's also scoped() in std.typecons, but I think this will still
allocate on the heap. Not sure..

On 12/19/10, bearophile <bearophileHUGS lycos.com> wrote:
 David Currie:

 I am new to D (like many have done C++ , Java ).
Welcome to D :-) What language do you refer to, D1 or D2? The answers here are about the latest versions of D2.
 Can a class be instantiated on the stack ?
There was a way built in the language to do that (using "scope", it works still but it's deprecated), but now you have to use the std library. See the std.conv.emplace, but keep in mind it has issues (more than the issues of "scope"!) like I think not calling the object destructor.
 It appears that D ASSUMES myC is really a myC*(in C++)
Right, in D class instances are always managed by reference, even when you allocate them on the stack using the emplace trick.
 // but this ALWAYS requires calling the memory allocator
 // this is what Java does (forces your Class instance onto the Heap)
A difference is that the Oracle Java Garbage Collector has a Eden memory for the newly allocated objects that's much faster than the current D GC :-)
 Will a struct do?
Sometimes a struct is enough. D structs are managed by value or by pointer, but they don't support inheritance. In D structs and classes are different (like the memory layout of a class instance is decided by the compiler, while struct fields are in memory as you want them, with the alignment you desire) and they are used for different purposes.
 Does a struct have a constructor (as opposed to an opcall?)
In D2 the struct constructor is this() as for classes. Bye, bearophile
Dec 19 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Andrej Mitrovic:

 There's also scoped() in std.typecons, but I think this will still
 allocate on the heap. Not sure..
It allocates on the stack. Bye, bearophile
Dec 19 2010
prev sibling next sibling parent Joost 't Hart <Joost.t.Hart planet.nl> writes:
On 12/20/2010 04:00 PM, David Currie wrote:

 I am new to D (like many have done C++ , Java ).
Me too. Let's see what we can figure out together :-)
 Can a class be instantiated on the stack ?

 eg

 class C
 {
 private int _I1;
 private int _I2;

 public:

 this(int pI) // constructor
 {
 _I1 = pI;
 _I2 = pI + 1;
 }

 // ... other methods etc
 }

 void f() // just a function
 {

 C myC(3); // C++ syntax BUT is there a d equivalent

 }

 It appears that D ASSUMES myC is really a myC*(in C++)
It is not so much a *, but reference semantics (like T& param in C++).
 and therefore requires

 C myC = new C(3);
 // but this ALWAYS requires calling the memory allocator
 // this is what Java does (forces your Class instance onto the Heap)

 Is there any way in D to instantiate a stack object ?
Not a class object. Only values are on the stack. The reference semantics requires a "living" entity to refer to.
 Will a struct do?
Yes, if you so wish, but new also works. Structs have value semantics.
 Does a struct have a constructor (as opposed to an opcall?)
You can define one, but you cannot redefine the default this() constructor. Because structs have value semantics, D wants to make sure that T.init member values are slotted in by default. Note that T.init for a class type is the null reference.
 I would be very grateful for a response.
Hope this helps a bit. Cheers, Joost.
 David Currie
Dec 19 2010
prev sibling next sibling parent Nick Voronin <elfy.nv gmail.com> writes:
On Mon, 20 Dec 2010 07:00:31 -0800
David Currie <curriedr iinet.net.au> wrote:

 Can a class be instantiated on the stack ?
Yes, check std.conv.emplace http://www.digitalmars.com/d/2.0/phobos/std_conv.html#emplace and alloca() I don't know details about interaction of such objects with GC though. Also there is scarcely documented storage class scope, which for class objects means that they will be destroyed upon scope exit, but also currently results in that object itself will be placed on the stack, no memory in heap is allocated.
    C myC(3);  // C++ syntax BUT is there a d equivalent
 It appears that D ASSUMES myC is really a myC*(in C++)
Not exactly. All class objects have reference semantic, so when you declare variable of type C you really declare a reference. The reference is what you get from new, pass as parameters, etc.
 Will a struct do?
structs on the contrary have value semantic, so when you declare struct variable you get the memory for struct allocated on stack or in data segment. When you pass or return structs -- you return or pass value, that is a copy of data. You may be also interested in the way dynamic arrays are handled, as arrays are value type, yet they hold a pointer to memory. This means that passing them as parameter or returning them does copy pointer and length, but actual data is stil shared until you do something which will force D to relocate it. All kind of not obvious implications here.
 Does a struct have a constructor (as opposed to an opcall?)
Yes, and more. http://www.digitalmars.com/d/2.0/struct.html -- Nick Voronin <elfy.nv gmail.com>
Dec 19 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday 20 December 2010 07:00:31 David Currie wrote:
 I am new to D (like many have done C++ , Java ).
 
 Can a class be instantiated on the stack ?
 
 eg
 
 class C
 {
    private  int _I1;
    private  int _I2;
 
    public:
 
    this(int pI) // constructor
    {
      _I1 = pI;
      _I2 = pI + 1;
    }
 
 // ...  other methods etc
 }
 
 void f()  // just a function
 {
 
    C myC(3);  // C++ syntax BUT is there a d equivalent
 
 }
 
 It appears that D ASSUMES myC is really a myC*(in C++)
 
 and therefore requires
 
 C myC = new C(3);
 // but this ALWAYS requires calling the memory allocator
 // this is what Java does (forces your Class instance onto the Heap)
 
 Is there any way in D to instantiate a stack object ?
 
 Will a struct do?
Structs are value types. Therefore, they go on the stack unless you have a pointer to them. struct Foo {} Foo a; //on stack Foo* b = new Foo(); //on heap Classes are reference types. The are therefore always on the heap. It's like what you get in Java. class Bar {} Bar a; //On the heap. Structs do not have inheritance (and thus don't have polymorphism), but they can be used for RAII. Assigning one to another means copying it (or moving it if the compiler determines that it can). Because struct member functions are not virtual, they often can be inlined. Classes do have inheritance (and thus do have polymorphism), but they can't use RAII. Assigning one to another means assigning a reference. Both references now refer to the same object. You'll have to use a clone method of some kind to get an actualy, deep copy. Because class member functions are almost always virtual, it's much rarer that they can be inlined. The language did have scoped classes, which put the class itself on the stack instead of the heap: class Bar {} scope Bar a; //On the stack. But it's inherently unsafe, so it's being removed from the language. There will be a library solution to do it, but again, it's unsafe. If you were to pass a scaped class to any other function, you risk the reference escaping, and then you'll end up with a reference to an object that doesn't exist once the function that declared it exits. Classes are meant to be on the heap. structs are meant to be on the stack but can be on the heap. Generally-speaking, if you use a class if you need a reference type or need polymorphism. Otherwise, you use a struct (reasons for using one or the other can, of course, get more complicated that that, but that's the core of it). Allowing classes on the like C++ does allows for problems like sheering and disallows polymorphism. D opted to split the concept into two different types of types: classes and structs. The language which is closest to D with regards to structs and classes that I'm aware of is C#, though I believe that D structs are definitely more powerful than C# structs. In D, you just don't use classes as often as you'd do in Java or C++, because structs in D do a lot of what classes do in those languages. For any user-defined type, you need to decide whether a struct or a class is more appropriate for what you're trying to do. The approach has definite benefits, but it does take some getting used to.
 Does a struct have a constructor (as opposed to an opcall?)
structs can have constructs just like classes - they just can't have default constructors. The reason for this is that all types in D have an init property that variables of that type are initialized to when they are default initialized. For integral types, it's 0; for bool, it's false; for references and pointers it's null; etc. For structs, it's what the member variables are directly initialized to. init precludes having an arbitrary constructor, because init must be determinable at compile time, can't have exceptions being thrown, etc. We may get some sort of limited default constructor for structs at some point, but it's not at all straightforward to have one, so we don't. The solution then, if you need one, is to use a static opCall() function, and then do Foo a = Foo(); //calls static opCall(). instead of Foo a; //Just uses Foo.init. So, structs _do_ have constructors, just not default constructors. - Jonathan M Davis
Dec 19 2010
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 There will be a library solution to do it, but again, it's unsafe.
It can be safer if the compiler gives some help. For me it's one of the important unfinished parts of D. Bye, bearophile
Dec 19 2010
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 19 December 2010 14:26:19 bearophile wrote:
 Jonathan M Davis:
 There will be a library solution to do it, but again, it's unsafe.
It can be safer if the compiler gives some help. For me it's one of the important unfinished parts of D.
Whereas, I would argue that it's completely unnecessary. structs and classes serve different purposes. There is no need for scoped classes. They may perodically be useful, but on the whole, they're completely unnecessary. The compiler can help, but it can't fix the problem any more that it can guarantee that a pointer to a local variable doesn't escape once you've passed it to another function. In _some_ circumstances, it can catch escaping pointers and references, but in the general case, it can't. If we have library solutions for people who want to play with fire, that's fine. But scoped classes is just not one of those things that the language really needs. They complicate things unnecessarily for minimal benefit. - Jonathan M Davis
Dec 19 2010
next sibling parent reply Nick Voronin <elfy.nv gmail.com> writes:
On Sun, 19 Dec 2010 14:38:17 -0800
Jonathan M Davis <jmdavisProg gmx.com> wrote:

 On Sunday 19 December 2010 14:26:19 bearophile wrote:
 Jonathan M Davis:
 There will be a library solution to do it, but again, it's unsafe.
It can be safer if the compiler gives some help. For me it's one of the important unfinished parts of D.
Whereas, I would argue that it's completely unnecessary. structs and classes serve different purposes. There is no need for scoped classes. They may perodically be useful, but on the whole, they're completely unnecessary.
How do you define a need for scoped classes? I see two aspects there: first is having destructor called at known point rather than arbitrarily, second is performance. It looks perfectly reasonable for me to want to have first. Do you know why things which makes program act more deterministic are shunned from D? Does it really add so much to safety that it is worth reducing programmer's control? Second is irrelevant to types, if I want to speed up some code and can do it by placing things on stack rather than in heap -- what does it matter if it is class or struct or integer? We have alloca() anyway, so removing modifier won't save me from myself, just push me to more C-like code. Which kind of defeats the purpose of using D.
 The compiler can help, but it can't fix the problem any more that it can 
 guarantee that a pointer to a local variable doesn't escape once you've passed 
 it to another function.
Absolutely. Yet we won't have library solution for pointers instead of language support (hopefully)? :) I think it all goes against "being practical" as an objective of the language. Safety is important but you don't achieve safety by means of making unsafe thing unconvenient and inefficient. If there is emplace() then there is no reason not to have scope storage class. At least looking from user's POV. I don't know how hard it is on the compiler.
 In _some_ circumstances, it can catch escaping pointers 
 and references, but in the general case, it can't.
In _general_ case there is no safety in D. With all low-level capabilities one can always defeat compiler. Removing intermediate-level safer (yet unsafe) capabilities arguabily gains nothing but frustration. I'm all for encouraging good practices, but this is different. -- Nick Voronin <elfy.nv gmail.com>
Dec 19 2010
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 12/20/10, Nick Voronin <elfy.nv gmail.com> wrote:
 I see two aspects there: first is having destructor called at known point
 rather than arbitrarily, second is performance.
There's still an alternative for the first part, scope(exit): import std.stdio; class A { ~this() { writeln("A.dtor"); } void test() { writeln("test"); } } void main() { A a = new A(); scope(exit) { clear(a); } a.test(); }
 test
 A.dtor
 A.dtor
Dec 19 2010
prev sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 19 December 2010 16:50:34 Nick Voronin wrote:
 On Sun, 19 Dec 2010 14:38:17 -0800
 
 Jonathan M Davis <jmdavisProg gmx.com> wrote:
 On Sunday 19 December 2010 14:26:19 bearophile wrote:
 Jonathan M Davis:
 There will be a library solution to do it, but again, it's unsafe.
It can be safer if the compiler gives some help. For me it's one of the important unfinished parts of D.
Whereas, I would argue that it's completely unnecessary. structs and classes serve different purposes. There is no need for scoped classes. They may perodically be useful, but on the whole, they're completely unnecessary.
How do you define a need for scoped classes? I see two aspects there: first is having destructor called at known point rather than arbitrarily, second is performance. It looks perfectly reasonable for me to want to have first. Do you know why things which makes program act more deterministic are shunned from D? Does it really add so much to safety that it is worth reducing programmer's control?
Then use a struct. That's one of the reasons that they exist. And if you _really_ want to make sure that a class' destructor gets run, you can always use clear() (which is arguably an abomination in its own right). And if you use that in combination with scope(exit), then you get a class which is guaranteed to be destroyed when the function exits, and (assuming that clear() is fully implemented and actually zeroes out the vtable - which I don't think that it does yet), you won't be using garbage memory (though obviously it will still go boom - likely due to a segfault). Part of the whole point of separating classes and structs is to separate the issue of where they go - on the stack or on the heap. So, putting classes on the stack kind of negates the whole point of having both structs and classes in the first place.
 In _general_ case there is no safety in D. With all low-level capabilities
 one can always defeat compiler. Removing intermediate-level safer (yet
 unsafe) capabilities arguabily gains nothing but frustration. I'm all for
 encouraging good practices, but this is different.
In the general case when using SafeD, there D _is_ safe. scoped classes are definitely not in SafeD. structs already are on the stack. If you want something on the stack, then use structs. And if you _really_ want a class on the stack for whatever reason (which I _really_ question), then there will be a library solution to the problem. By putting classes on the stack, you're pretty much ignoring the differences between structs and classes. - Jonathan M Davis
Dec 19 2010
next sibling parent reply Nick Voronin <elfy.nv gmail.com> writes:
On Sun, 19 Dec 2010 17:26:20 -0800
Jonathan M Davis <jmdavisProg gmx.com> wrote:

 On Sunday 19 December 2010 16:50:34 Nick Voronin wrote:
 On Sun, 19 Dec 2010 14:38:17 -0800
 
 Jonathan M Davis <jmdavisProg gmx.com> wrote:
 On Sunday 19 December 2010 14:26:19 bearophile wrote:
 Jonathan M Davis:
 There will be a library solution to do it, but again, it's unsafe.
It can be safer if the compiler gives some help. For me it's one of the important unfinished parts of D.
Whereas, I would argue that it's completely unnecessary. structs and classes serve different purposes. There is no need for scoped classes. They may perodically be useful, but on the whole, they're completely unnecessary.
How do you define a need for scoped classes? I see two aspects there: first is having destructor called at known point rather than arbitrarily, second is performance. It looks perfectly reasonable for me to want to have first. Do you know why things which makes program act more deterministic are shunned from D? Does it really add so much to safety that it is worth reducing programmer's control?
Then use a struct.
Here is where we diverge. Choosing struct vs class on criteria of their placement makes no sense to me. Difference in default placement hardly matters at all, you can perfectly put structs in heap or in static segment yet still maintain same properties. It's those properties which matter when I choose one or another, not where it will reside in particular part of program.
 That's one of the reasons that they exist. And if you 
 _really_ want to make sure that a class' destructor gets run, you can always
use 
 clear() (which is arguably an abomination in its own right).
This is what I don't understand. Fine. structs are meant for stack, class for heap. Conceptually, anyway. Still there will be ways to put class on stack and struct in heap. And there is(will be) clear(). Now I do see benefit of scope storage. It looks clean, it is supported by compiler meaning reasonable level of protection against misuse, it is extremely effective. What are benefits of _not having_ it? -- Nick Voronin <elfy.nv gmail.com>
Dec 19 2010
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 19 December 2010 18:13:54 Nick Voronin wrote:
 On Sun, 19 Dec 2010 17:26:20 -0800
 
 Jonathan M Davis <jmdavisProg gmx.com> wrote:
 On Sunday 19 December 2010 16:50:34 Nick Voronin wrote:
 On Sun, 19 Dec 2010 14:38:17 -0800
 
 Jonathan M Davis <jmdavisProg gmx.com> wrote:
 On Sunday 19 December 2010 14:26:19 bearophile wrote:
 Jonathan M Davis:
 There will be a library solution to do it, but again, it's
 unsafe.
It can be safer if the compiler gives some help. For me it's one of the important unfinished parts of D.
Whereas, I would argue that it's completely unnecessary. structs and classes serve different purposes. There is no need for scoped classes. They may perodically be useful, but on the whole, they're completely unnecessary.
How do you define a need for scoped classes? I see two aspects there: first is having destructor called at known point rather than arbitrarily, second is performance. It looks perfectly reasonable for me to want to have first. Do you know why things which makes program act more deterministic are shunned from D? Does it really add so much to safety that it is worth reducing programmer's control?
Then use a struct.
Here is where we diverge. Choosing struct vs class on criteria of their placement makes no sense to me. Difference in default placement hardly matters at all, you can perfectly put structs in heap or in static segment yet still maintain same properties. It's those properties which matter when I choose one or another, not where it will reside in particular part of program.
 That's one of the reasons that they exist. And if you
 _really_ want to make sure that a class' destructor gets run, you can
 always use clear() (which is arguably an abomination in its own right).
This is what I don't understand. Fine. structs are meant for stack, class for heap. Conceptually, anyway. Still there will be ways to put class on stack and struct in heap. And there is(will be) clear(). Now I do see benefit of scope storage. It looks clean, it is supported by compiler meaning reasonable level of protection against misuse, it is extremely effective. What are benefits of _not having_ it?
Except that the compiler _can't_ protect against misuse. That's one of the main reasons that it's going away as part of the language. Structs are intended to their lifetime be the same as their scope. The lifetime for classes, on the other hand, is essentially as long as the program is running (though the garbage collector can obviously collect them in cases where they aren't used anymore). By trying to force a reference type to have a scoped lifetime, you're going against how they work, and it's just asking for trouble. Yes, it _can_ be done, but no, it _can't_ be done safely. As I understand it, Andrei isn't even entirely happy that clear() exists. I believe that pretty much the only reason that it exists is because there are cases where classes hold resources and you want to make sure that they get released rather than waiting for the object to get destroyed at some indeterminate point in the future. But really, the garbage collected heap is supposed to be managed by the garbage collector. And with scope, you're trying to take something that's designed to live on the garbage collected heap and put it on the stack. You're taking something that is designed to be used in one type of storage and using it in another. It's going to cause problems. C++ has a number of issues pricely because it allows classes to live on both the stack and the heap. By separating types that live on the stack from those that live on the heap, you avoid those problems. And actually, the fact that structs can live in the heap in D can be problematic, as evidenced by the bugs relating to destructors for structs on the heap. Not only do their destructors not get run, it's questionable whether the language will _ever_ be such that the destructors of structs on the heap will be run. The result is that in many cases, structs should either be designed to be on the stack or on the heap. D was designed so that classes live on the heap, and that's the way it works. Putting them on the stack is going against how classes are intended to work, and it's asking for trouble. - Jonathan M Davis
Dec 19 2010
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Nick Voronin:

 Here is where we diverge. Choosing struct vs class on criteria of their
placement makes no sense to me. 
In D you use a class if you want inheritance or when you (often) need reference semantics, and you use a struct when you need a little value passed around by value or when you want a simple form of RAII or when you want to implement something manually (like using PIMPL), or when you want max performance (and you manage structs by pointer, you may even put a tag inside the stuct or the pointer and implement manually some kind of inheritance). With structs you have a literal syntax, postblits, in-place allocation, and you are free to use align() too. Bye, bearophile
Dec 20 2010
parent Nick Voronin <elfy.nv gmail.com> writes:
On Mon, 20 Dec 2010 05:43:08 -0500
bearophile <bearophileHUGS lycos.com> wrote:

 Nick Voronin:
 
 Here is where we diverge. Choosing struct vs class on criteria of their
placement makes no sense to me. 
In D you use a class if you want inheritance or when you (often) need reference semantics, and you use a struct when you need a little value passed around by value or when you want a simple form of RAII or when you want to implement something manually (like using PIMPL), or when you want max performance (and you manage structs by pointer, you may even put a tag inside the stuct or the pointer and implement manually some kind of inheritance). With structs you have a literal syntax, postblits, in-place allocation, and you are free to use align() too.
Well said. Plenty of differences there more important than stack/heap allocation. -- Nick Voronin <elfy.nv gmail.com>
Dec 20 2010
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 Whereas, I would argue that it's completely unnecessary.
Recently even the Oracle Java VM allocates some class instances on the stack, when Escape Analysis finds it's safe to do so. And D lacks the very efficient GC of the JVM, so for D it's even more important to use the stack for some classes. Think about the "pure" tag of D2. D1 lacks the "pure" annotation, but an advanced D1 compiler may add some logic able to tell if a function is pure, and use this info for performance purposes. The problem here is that if you change the function a little your function may stop being pure and the optimization becomes impossible. The "pure" annotation of D2 is a contract between the programmer and the compiler. In the same way, even if a D compiler gains an automatic escape analysis like Java and a much more efficient GC, a way to annotate a class as scoped is useful still, because like "pure" it's a contract between the compiler and the programmer, it enforces that a class instance doesn't escape a scope (if you try to escape it, the good compiler in most cases gives an error. While in this case the JVM just doesn't allocate the object on the stack), this also means guaranteed performance and deallocation determinism. Another thing to keep in account is that we aren't talking just about the stack, but generally about in-place allocation. So if you have a class instance A, one of its fields may be another class instance B, that's scoped. Here B is allocated on the heap, because B is contained inside A, that is (generally) on the heap. Yet B is allocated in-place. Having a single heap allocation instead of two when you create an instance A is a significant performance gain (and I think Java doesn't perform this optimization yet). A well designed type system is then able to make all this safe.
 The compiler can help, but it can't fix the problem any more that it can 
 guarantee that a pointer to a local variable doesn't escape once you've passed 
 it to another function. In _some_ circumstances, it can catch escaping
pointers 
 and references, but in the general case, it can't.
See also: http://en.wikipedia.org/wiki/Linear_types Stronger and more powerful type systems are possible (D has means to break any type system, but the programmer does this knowing what she or he is doing. And there are ways similar to safe to statically disallow the means you may use to break this part of the type system).
So, putting classes on the stack kind of negates the whole point of having both
structs and classes in the first place.<
This is false, the definition of D class instance doesn't specify where the instance memory is allocated.
In the general case when using SafeD, there D _is_ safe. scoped classes are
definitely not in SafeD.<
SafeD is not safe. It allows integer overflows, memory overflows, etc. SafeD just disallows a certain (important) kind of memory bugs. This is why some people have asked to call it "memory safe". If you take a look at coding standards that specify how to write high integrity software (MISRA-C, SPARK, Joint Strike Fighter Air Vehicle C++ Coding Standards, etc), you will see that they forbid ALL heap allocations, because stack allocations are more deterministic and more predictable, so they are safer. If you disallow recursion the max required size of the stack is even computable statically, avoiding stack overflows. --------------------- Nick Voronin:
Safety is important but you don't achieve safety by means of making unsafe
thing unconvenient and inefficient.<
I know that sounds a little weird, but in practice this is how some parts of D are designed :-) Yet, Walter has said many things that he doesn't like "safety by convention". So I presume there is some contradiction here :-) A language like Cyclone has tried to make low-level operations in a very C-language safe, and it succeeds in this, but Cyclone is both slow, fussy, not handy, a failure. So by design D doesn't try to make low-level operation safe, it just add safe(r) hi-level operations. You are allowed to use the low-level means still, but in this case you are fully on your own, like in C. For a lot of time I have not appreciated much this basic design decision, but so far it has worked well enough, far better than Cyclone. Bye, bearophile
Dec 19 2010
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 19 December 2010 18:33:56 bearophile wrote:
 Jonathan M Davis:
 Whereas, I would argue that it's completely unnecessary.
Recently even the Oracle Java VM allocates some class instances on the stack, when Escape Analysis finds it's safe to do so. And D lacks the very efficient GC of the JVM, so for D it's even more important to use the stack for some classes. Think about the "pure" tag of D2. D1 lacks the "pure" annotation, but an advanced D1 compiler may add some logic able to tell if a function is pure, and use this info for performance purposes. The problem here is that if you change the function a little your function may stop being pure and the optimization becomes impossible. The "pure" annotation of D2 is a contract between the programmer and the compiler. In the same way, even if a D compiler gains an automatic escape analysis like Java and a much more efficient GC, a way to annotate a class as scoped is useful still, because like "pure" it's a contract between the compiler and the programmer, it enforces that a class instance doesn't escape a scope (if you try to escape it, the good compiler in most cases gives an error. While in this case the JVM just doesn't allocate the object on the stack), this also means guaranteed performance and deallocation determinism. Another thing to keep in account is that we aren't talking just about the stack, but generally about in-place allocation. So if you have a class instance A, one of its fields may be another class instance B, that's scoped. Here B is allocated on the heap, because B is contained inside A, that is (generally) on the heap. Yet B is allocated in-place. Having a single heap allocation instead of two when you create an instance A is a significant performance gain (and I think Java doesn't perform this optimization yet). A well designed type system is then able to make all this safe.
 The compiler can help, but it can't fix the problem any more that it can
 guarantee that a pointer to a local variable doesn't escape once you've
 passed it to another function. In _some_ circumstances, it can catch
 escaping pointers and references, but in the general case, it can't.
See also: http://en.wikipedia.org/wiki/Linear_types Stronger and more powerful type systems are possible (D has means to break any type system, but the programmer does this knowing what she or he is doing. And there are ways similar to safe to statically disallow the means you may use to break this part of the type system).
So, putting classes on the stack kind of negates the whole point of having
both structs and classes in the first place.<
This is false, the definition of D class instance doesn't specify where the instance memory is allocated.
In the general case when using SafeD, there D _is_ safe. scoped classes
are definitely not in SafeD.<
SafeD is not safe. It allows integer overflows, memory overflows, etc. SafeD just disallows a certain (important) kind of memory bugs. This is why some people have asked to call it "memory safe".
The whole point of "safe" when talking about safe in D is memory saftey. And scoped classes are an issue of memory safety. They are _not_ safe with regards to memory. If the compiler can determine that a particular class object can be put on the stack and optimize it that way. Fine, but it's pretty rare that it can do that - essentially only in cases where you don't pass it to _anything_ except for pure functions (including calls to member functions). And if the compiler can do that, then it there's no need for the programmer to use scope explicitly. And no, a compiler _can't_ do pure optimizations on its own, generally-speaking, because that would require looking not only at the body of the function that's being called but at the function bodies of any functions that it calls. D is not designed in a way that the compiler even necessarily has _access_ to a function's body when compiling, and you can't generally look at a function's body when doing optimizations when calling that function. So, _some_ pure optimizations could be done, but most couldn't. This is not the case with scoped classes, because purity already gives you the information that you need.
 If you take a look at coding standards that specify how to write high
 integrity software (MISRA-C, SPARK, Joint Strike Fighter Air Vehicle C++
 Coding Standards, etc), you will see that they forbid ALL heap
 allocations, because stack allocations are more deterministic and more
 predictable, so they are safer. If you disallow recursion the max required
 size of the stack is even computable statically, avoiding stack overflows.
 
 ---------------------
 
 Nick Voronin:
Safety is important but you don't achieve safety by means of making unsafe
thing unconvenient and inefficient.<
I know that sounds a little weird, but in practice this is how some parts of D are designed :-) Yet, Walter has said many things that he doesn't like "safety by convention". So I presume there is some contradiction here :-)
Safety by convention means that the language and the compiler do not enforce it in any way. When the language is designed to include it, then it's _not_ by convention. So, for instance, Go doesn't use its type system to protect you from threads stomping on each other. Rather, the safety is done by convention - the programmer does it by programming in a particular way. In D, on the other hand, the type system enforces it. There's nothing contradictory about Walter's stance. He's for having safety built into the language as much as reasonably possible and against having it thrust upon the programmer to program in a particular way to avoid unsafe stuff. safe is a prime example of this (assuming that's implemented correctly, which isn't the case at the moment). It causes the compiler to enforce that only safe operations are used rather than relying on the programmer not to use unsafe operations. - Jonathan M Davis
Dec 19 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 So, putting classes on the stack kind of negates the whole point of having
 both structs and classes in the first place.
Where you put the instance is mostly a matter of implementation. This is why a smart JavaVM is able to perform escape analysis and choose where to allocate the class instance. Keep in mind that if you allocate a class on the stack or in-place inside another class, you don't turn it into a value, because beside the class instance you reserve space for its reference too (this reference may even be immutable, if you want).
 scoped classes are definitely not in SafeD.
Well implemented scoped classes are safe enough (compared to the other things). The compiler may perform escape analysis of all the aliases of a scoped object and statically raise an error if a reference escapes. This isn't 100% safe in a language that has all kind of casts and low-level features, but it's often safe enough, compared to other things. And those casts and low level features that can fool the escape analysis can be disabled statically (with something like safe), this makes scoped classes 100% safe, probably safer than heap allocations.
The whole point of "safe" when talking about safe in D is memory saftey.
I know, but some people (including me) think that "safe D" is a misleading name because it just means "memory safe D".
If the compiler can determine that a particular class object can be put on the
stack and optimize it that way. Fine, but it's pretty rare that it can do that
- essentially only in cases where you don't pass it to _anything_ except for
pure functions (including calls to member functions).
I don't agree that it's rare. If a function that allocates an object calls a function (or member function) that's present in the same compilation unit (this more or less means same module), then the compiler is able to continue the escape analysis and determine if the called function escapes the reference. If this doesn't happen, then the class instance is free to be scoped. This situation is common enough.
And if the compiler can do that, then it there's no need for the programmer to
use scope explicitly.<
I don't agree. An annotation like " scope" is a contract between the programmer and the compiler. It means that if the compiler sees a reference escape, then it stops the compilation.
And no, a compiler _can't_ do pure optimizations on its own,
generally-speaking, because that would require looking not only at the body of
the function that's being called but at the function bodies of any functions
that it calls. D is not designed in a way that the compiler even necessarily
has _access_ to a function's body when compiling, and you can't generally look
at a function's body when doing optimizations when calling that function. So,
_some_ pure optimizations could be done, but most couldn't. This is not the
case with scoped classes, because purity already gives you the information that
you need.<
Quite often a function calls another function in thee same compilation unit, in this case the analysis is possible. So you limit the optimizations to this common but limited case. And LDC compiler and in future GDC too, have link-time optimization, this means the compiler packs or sees the program code code in a single compilation unit. In this case it's able to perform a more complete analysis (including de-virtualization of some virtual functions).
Safety by convention means that the language and the compiler do not enforce it
in any way.<
This is not fully true. If the syntax of the unsafe thing is ugly and long, the programmer is discouraged to use it. This makes the unsafe thing more visible for the eyes of the programmer. Statistically this may reduce bug count.
There's nothing contradictory about Walter's stance. He's for having safety
built into the language as much as reasonably possible and against having it
thrust upon the programmer to program in a particular way to avoid unsafe
stuff.<
I think you have missed part of the context of my comments for Nick Voronin, he was trying to say something here:
Yet we won't have library solution for pointers instead of language support
(hopefully)? :) I think it all goes against "being practical" as an objective
of the language. Safety is important but you don't achieve safety by means of
making unsafe thing unconvenient and inefficient. If there is emplace() then
there is no reason not to have scope storage class. At least looking from
user's POV. I don't know how hard it is on the compiler.<
In _general_ case there is no safety in D. With all low-level capabilities one
can always defeat compiler. Removing intermediate-level safer (yet unsafe)
capabilities arguabily gains nothing but frustration. I'm all for encouraging
good practices, but this is different.<
In D the convention is to not use certain low-level means to do something (and safe statically forbids them, so it's not just a convention, I agree with you). But I am programming in D for some years and I have seen that some times (for performance or for other causes) you need to use the unsafe low-level features. In this case D leaves you on your own. You don't have something intermediate between the safe high level features and the unsafe C features. Nick and I feel the lack of this intermediate level of safety. Then I have explained to Nick about the failure of Cyclone. It shows that implementing that intermediate level is hard, and it may lead to a not so good language. This is probably why Walter has chosen to avoid doing it. Despite the failure of Cyclone I am not sure Walter is right, and there may be a way to implement this intermediate level. I think the ATS language shows ways to do it, but it's a very hard to use language, "fifteen" times harder than D. So it's an open problem still. Bye, bearophile
Dec 20 2010
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, December 20, 2010 03:19:48 bearophile wrote:
 Jonathan M Davis:
 So, putting classes on the stack kind of negates the whole point of
 having both structs and classes in the first place.
Where you put the instance is mostly a matter of implementation. This is why a smart JavaVM is able to perform escape analysis and choose where to allocate the class instance. Keep in mind that if you allocate a class on the stack or in-place inside another class, you don't turn it into a value, because beside the class instance you reserve space for its reference too (this reference may even be immutable, if you want).
 scoped classes are definitely not in SafeD.
Well implemented scoped classes are safe enough (compared to the other things). The compiler may perform escape analysis of all the aliases of a scoped object and statically raise an error if a reference escapes. This isn't 100% safe in a language that has all kind of casts and low-level features, but it's often safe enough, compared to other things. And those casts and low level features that can fool the escape analysis can be disabled statically (with something like safe), this makes scoped classes 100% safe, probably safer than heap allocations.
The whole point of "safe" when talking about safe in D is memory saftey.
I know, but some people (including me) think that "safe D" is a misleading name because it just means "memory safe D".
Talking about SafeD meaning memory safety makes the meaning of safety clear. If you try and make the term safety encompass more than that, it takes very little for "safety" to become subjective. Regardless of whether it would be nice if SafeD gave types of safety other than memory safety, when D documentation and any of the main D devs talk about safety, it is memory safety which is being referred to. Trying to expand the meaning beyond that will just cause confusion regardless of whether the non-memory safety being discussed is desirable or not.
If the compiler can determine that a particular class object can be put on
the stack and optimize it that way. Fine, but it's pretty rare that it
can do that - essentially only in cases where you don't pass it to
_anything_ except for pure functions (including calls to member
functions).
I don't agree that it's rare. If a function that allocates an object calls a function (or member function) that's present in the same compilation unit (this more or less means same module), then the compiler is able to continue the escape analysis and determine if the called function escapes the reference. If this doesn't happen, then the class instance is free to be scoped. This situation is common enough.
And if the compiler can do that, then it there's no need for the
programmer to use scope explicitly.<
I don't agree. An annotation like " scope" is a contract between the programmer and the compiler. It means that if the compiler sees a reference escape, then it stops the compilation.
And no, a compiler _can't_ do pure optimizations on its own,
generally-speaking, because that would require looking not only at the
body of the function that's being called but at the function bodies of
any functions that it calls. D is not designed in a way that the compiler
even necessarily has _access_ to a function's body when compiling, and
you can't generally look at a function's body when doing optimizations
when calling that function. So, _some_ pure optimizations could be done,
but most couldn't. This is not the case with scoped classes, because
purity already gives you the information that you need.<
Quite often a function calls another function in thee same compilation unit, in this case the analysis is possible. So you limit the optimizations to this common but limited case. And LDC compiler and in future GDC too, have link-time optimization, this means the compiler packs or sees the program code code in a single compilation unit. In this case it's able to perform a more complete analysis (including de-virtualization of some virtual functions).
It's trivial to get a reference or pointer to escape and make undetectable to the compiler. Some escape analysis can be and is done, but all it takes is passing a pointer or a reference to another function and the compiler can't determine it anymore unless it has access to the called functions body, and perhaps the bodies of functions that that function calls. And if the compiler can't be 100% correct with escape analysis, then any feature that requires it is not safe. And as great as fancier optimizations such as link-time optimizations may be, the existence of dynamic libraries eliminates any and all guarantees that such optimizations would be able to make if they had all of the source to look at. So, you can't rely on them. They help, and they're great, but no feature can require them. They're optimizations only. - Jonathan M Davis
Dec 20 2010
parent bearophile <bearophileHUGS lycos.com> writes:
It seems I disagree with most things you say in this thread :-)

Jonathan M Davis:

Talking about SafeD meaning memory safety makes the meaning of safety clear. If
you try and make the term safety encompass more than that, it takes very little
for "safety" to become subjective. Regardless of whether it would be nice if
SafeD gave types of safety other than memory safety, when D documentation and
any of the main D devs talk about safety, it is memory safety which is being
referred to. Trying to expand the meaning beyond that will just cause confusion
regardless of whether the non-memory safety being discussed is desirable or
not.<
"SafeD is a misleading name. Names are important because they not just reflect how we see things, but they also shape how we see and how we will see things. If D will want to add "overflow safety" it will need to call those parts "UltraSafeD" :-) Safety in a language is not so much subjective, just a little. Try to take a look at languages where safety is taken seriously, like SPARK, or even Ada.
It's trivial to get a reference or pointer to escape and make undetectable to
the compiler. Some escape analysis can be and is done, but all it takes is
passing a pointer or a reference to another function and the compiler can't
determine it anymore unless it has access to the called functions body, and
perhaps the bodies of functions that that function calls.<
I think the type system can solve this problem (but where the programmer wants to fool the compiler or punch a hole in the type system on purpose), the not escaping nature of the reference has to become transitive, and the called function too needs the type annotation that ensure that the reference doesn't escape. Probably linear types are one way to do this, but there are more flexible ways to do it (to avoid template bloat this annotation may work as "auto const", so in the binary only one istance of the function will be present even if it is once called with a reference that can't escape and it is called by someone else that gives a free to escape reference). Bye, bearophile
Dec 21 2010
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On Sun, 19 Dec 2010 21:33:56 -0500
bearophile <bearophileHUGS lycos.com> wrote:

So, putting classes on the stack kind of negates the whole point of havi=
ng both structs and classes in the first place.< =20
=20
 This is false, the definition of D class instance doesn't specify where t=
he instance memory is allocated. For me, the important difference is that classes are referenced, while stru= cts are plain values. This is a semantic distinction of highest importance.= I would like structs to be subtype-able and to implement (runtime-type-bas= ed) polymorphism. Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Dec 20 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday 20 December 2010 01:19:31 spir wrote:
 On Sun, 19 Dec 2010 21:33:56 -0500
 
 bearophile <bearophileHUGS lycos.com> wrote:
So, putting classes on the stack kind of negates the whole point of
having both structs and classes in the first place.<
This is false, the definition of D class instance doesn't specify where the instance memory is allocated.
For me, the important difference is that classes are referenced, while structs are plain values. This is a semantic distinction of highest importance. I would like structs to be subtype-able and to implement (runtime-type-based) polymorphism.
Except that contradicts the facts that they're value types. You can't have a type which has polymorphism and is a value type. By its very nature, polymorphism requires you to deal with a reference. C++ allows you to put classes on the stack. It even allows you to assign a derived type to a base type where the variable being assigned to is on the stack. The result is shearing. The only part assigned is the base type portion, and the data which is part of the derived type is lost. That's because the variable _is_ the base type. A value type _is_ a particular type _exactly_ and _cannot_ be any other type. This is distinctly different from a reference of a base type which points to an object which is of a derived type. In that case, the variable is a reference of the base type, but the object referenced is in fact the derived type. The indirection allows you to use the derived type as if it were the base type. It allows you to use polymorphism. Without that indirection, you can't do that. So, you _could_ make structs have inheritance, but doing so would introduce shearing, which causes a number of problems. One of the main reasons that structs in D do _not_ have inheritance is to avoid shearing. - Jonathan M Davis
Dec 20 2010
prev sibling parent spir <denis.spir gmail.com> writes:
On Mon, 20 Dec 2010 01:29:13 -0800
Jonathan M Davis <jmdavisProg gmx.com> wrote:

 For me, the important difference is that classes are referenced, while
 structs are plain values. This is a semantic distinction of highest
 importance. I would like structs to be subtype-able and to implement
 (runtime-type-based) polymorphism. =20
=20 Except that contradicts the facts that they're value types. You can't hav=
e a=20
 type which has polymorphism and is a value type. By its very nature,=20
 polymorphism requires you to deal with a reference.
Can you expand on this? At least Oberon has value structs ("records") with inheritance and polyporp= hism; I guess the turbo Pascal OO model was of that kind, too (unsure) -- a= t least the version implemented in freepascal seems to work fine that way. = And probably loads of less known PLs provide such a feature. D structs could as well IIUC: I do not see the relation with instances beei= ng implicitely referenced. (Except that they must be passed by ref to "memb= er functions" they are the receiver of, but this is true for any kind of OO= , including present D structs.) (I guess we have very different notions of "reference", as shown by previou= s threads.) Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Dec 20 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 19 Dec 2010 17:38:17 -0500, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 On Sunday 19 December 2010 14:26:19 bearophile wrote:
 Jonathan M Davis:
 There will be a library solution to do it, but again, it's unsafe.
It can be safer if the compiler gives some help. For me it's one of the important unfinished parts of D.
Whereas, I would argue that it's completely unnecessary. structs and classes serve different purposes. There is no need for scoped classes. They may perodically be useful, but on the whole, they're completely unnecessary. The compiler can help, but it can't fix the problem any more that it can guarantee that a pointer to a local variable doesn't escape once you've passed it to another function. In _some_ circumstances, it can catch escaping pointers and references, but in the general case, it can't. If we have library solutions for people who want to play with fire, that's fine. But scoped classes is just not one of those things that the language really needs. They complicate things unnecessarily for minimal benefit.
I don't mind having a solution as long as there is a solution. The main need I see for scoped classes is for when you *know* as the programmer that the lifetime of a class or struct will not exceed the lifetime of a function, but you don't want to incur the penalty of allocating on the heap. Mostly this is because the functions you want to call take classes or interfaces. It's difficult to find an example with Phobos since there are not many classes. But with Tango, scoped classes are used everywhere. -Steve
Dec 20 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday 20 December 2010 01:52:58 spir wrote:
 On Mon, 20 Dec 2010 01:29:13 -0800
 
 Jonathan M Davis <jmdavisProg gmx.com> wrote:
 For me, the important difference is that classes are referenced, while
 structs are plain values. This is a semantic distinction of highest
 importance. I would like structs to be subtype-able and to implement
 (runtime-type-based) polymorphism.
Except that contradicts the facts that they're value types. You can't have a type which has polymorphism and is a value type. By its very nature, polymorphism requires you to deal with a reference.
Can you expand on this? At least Oberon has value structs ("records") with inheritance and polyporphism; I guess the turbo Pascal OO model was of that kind, too (unsure) -- at least the version implemented in freepascal seems to work fine that way. And probably loads of less known PLs provide such a feature. D structs could as well IIUC: I do not see the relation with instances beeing implicitely referenced. (Except that they must be passed by ref to "member functions" they are the receiver of, but this is true for any kind of OO, including present D structs.) (I guess we have very different notions of "reference", as shown by previous threads.)
Okay. This can get pretty complicated, so I'm likely to screw up on some of the details, but this should give you a basic idea of what's going on. In essentially any C-based language, when you declare an integer on the stack like so: int a = 2; you set aside a portion of the stack which is the exact size of an int (typically 32 bits, but that will depend on the language). If you declare a pointer, int* a; then you're setting aside a portion of the stack the size of a pointer (32 bits on a 32 bit machine and 64 bits on a 64 bit machine). That variable then holds an address - typically to somewhere on the heap, though it could be to an address on the stack somewhere. In the case of int*, the address pointed to will refer to a 32-bit block of memory which holds an int. If you have a struct or a class that you put on the stack. Say, class A { int a; float b; } then you're setting aside exactly as much space as that type requires to hold itself. At minimum, that will be the total size of its member variables (in this case an int and a float, so probably a total of 64 bits), but it often will include extra padding to align the variables along appropriate boundaries for the sake of efficiency, and depending on the language, it could have extra type information. If the class has a virtual table (which it will if it has virtual functions, which in most any language other than C++ would mean that it definitely has a virtual table), then that would be part of the space required for the class as well (virtual functions are polymorphic; when you call a virtual function, it calls the version of the function for the actual type that an object is rather than the pointer or reference that you're using to refer to the object; when a non-virtual function function is called, then the version of the function which the pointer or reference is is used; all class functions are virtual in D unless the compiler determines that they don't have to be and optimizes it out (typically because they're final); struct functions and stand- alone functions are never virtual). The exact memory layout of a type _must_ be known at compile time. The exact amount of space required is then known, so that the stack layout can be done appropriately. If you're dealing with a pointer, then the exact memory layout of the memory being pointed to needs to be known when that memory is initialized, but the pointer doesn't necessarily need to know it. This means that you can have a pointer of one type point to a variable of another type. Now, assuming that you're not subverting the type system (e.g. my casting int* to float*), you're dealing with inheritance. For instance, you have class B : A { bool c; } and a variable of type A*. That pointer could point to an object which is exactly of type A, or it could point to any subtype of A. B is derived from A, so the object could be a B. As long as the functions are virtual, you can have polymorphic functions by having the virtual table used to call the version of the function for the type that the object actually is rather than the type that the pointer is. References are essentially the same as pointers (though they may have some extra information with them, making them a bit bigger than a pointer would be in terms of the amount of space required on the stack). However, in the case of D, pointers are _not_ treated as polymorphic (regardless of whether a function is virtual or not), whereas references _are_ treated as polymorphic (why, I don't know - probably to simplify pointers). In C++ though, pointers are polymorphic. Now, if you have a variable of type A*, you could do something like this: B* b = new B(); A* a = b; A* takes up 32 or 64 bits in memory and holds the memory location on the heap where the B object is. Both pointers have the same value and point to the same object. The only difference is how the compiler treats each type (e.g. you can't call a B function on the a variable). Calling A functions on the a variable will call the B version if it has its own version and the function is virtual. However, what about this: B b; A a = b; The memory layout of b and a must be known at compile time. They're laid out precisely on the stack. b has the size of a B object. a has the size of an A object. a is _exactly_ an A. It cannot be a B. So, what you get is called sheering. The A portions of the variable are assigned (in this case, the int and the float), whereas the B portions aren't assigned. a is now exactly as it would have been had you created it with its member variables having the same values that b's member variables from its A portion had. This is almost certainly _not_ what you wanted. Now, because a is exactly an A, and b is exactly a B, when you go to call functions on them, it doesn't matter whether they're virtual or not. The type of the variable _is_ the type of the object. There is no polymorphism. You _need_ that level of indirection to get it. Now, you could conceivably have a language where all of its objects were actually pointers, but they were treated as value types. So, B b; A a = b; would actually be declaring B* b; A* a = b; underneath the hood, except that the assignment would do a deep copy and allocate the appropriate meemory rather than just copying the pointer like would happen in a language like C++ or D. Perhaps that's what Oberon does. I have no idea. I have never heard of the language before, let alone used it. However, that's _not_ how C++, D, C#, or Java works. If you declare B b; A a = b; then you are literally putting a B and an A on the stack, and assignments from a B to an A will cause sheering. D chose to avoid the sheering issue by making structs not have inheritance. This also means that they don't have a virtual table, which makes them more efficient. Classes have inheritance and a virtual table, but because they're on the heap, you don't get sheering and polymorphism works just fine. So, what it comes down to is that you can't have polymorphism for a stack object because you know _exactly_ what its type is, and you can't have inheritance for a stack object without risking sheering when assignments are made (unless you disallow assignments from one type of object to another unless they're the exact same type). So, you're never going to see inheritance for structs in D. It doesn't fit its memory model at all. What you get instead are templates, which can be used to generate the same code for different types. And that's as close as you're going to get for polymorphism for structs. - Jonathan M Davis
Dec 20 2010
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On Mon, 20 Dec 2010 03:11:49 -0800
Jonathan M Davis <jmdavisProg gmx.com> wrote:

 Now, you could conceivably have a language where all of its objects were=
=20
 actually pointers, but they were treated as value types. So,
=20
 B b;
 A a =3D b;
=20
 would actually be declaring
=20
 B* b;
 A* a =3D b;
=20
 underneath the hood, except that the assignment would do a deep copy and=
=20
 allocate the appropriate meemory rather than just copying the pointer lik=
e would=20
 happen in a language like C++ or D. Perhaps that's what Oberon does. I ha=
ve no=20
 idea. I have never heard of the language before, let alone used it.
I don't know how Oberon works. But I'm sure that its records are plain valu= es, _not_ "pointed" under the hood. And their methods all are virtual (they= have a virtual method table). I have no more details, sorry. Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Dec 20 2010
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, December 20, 2010 06:24:56 spir wrote:
 On Mon, 20 Dec 2010 03:11:49 -0800
 
 Jonathan M Davis <jmdavisProg gmx.com> wrote:
 Now, you could conceivably have a language where all of its objects were
 actually pointers, but they were treated as value types. So,
 
 B b;
 A a = b;
 
 would actually be declaring
 
 B* b;
 A* a = b;
 
 underneath the hood, except that the assignment would do a deep copy and
 allocate the appropriate meemory rather than just copying the pointer
 like would happen in a language like C++ or D. Perhaps that's what
 Oberon does. I have no idea. I have never heard of the language before,
 let alone used it.
I don't know how Oberon works. But I'm sure that its records are plain values, _not_ "pointed" under the hood. And their methods all are virtual (they have a virtual method table). I have no more details, sorry.
Well, given C's memory model - which D uses - you can't do that. Oberon could use a different memory model and have some other way of doing it, but it won't work for D, so you'll never see structs with polymorphic behavior in D. - Jonthan M Davis
Dec 20 2010
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 19 December 2010 13:23:42 Jonathan M Davis wrote:
 On Monday 20 December 2010 07:00:31 David Currie wrote:
 I am new to D (like many have done C++ , Java ).
 
 Can a class be instantiated on the stack ?
 
 eg
 
 class C
 {
 
    private  int _I1;
    private  int _I2;
    
    public:
    
    this(int pI) // constructor
    {
    
      _I1 = pI;
      _I2 = pI + 1;
    
    }
 
 // ...  other methods etc
 }
 
 void f()  // just a function
 {
 
    C myC(3);  // C++ syntax BUT is there a d equivalent
 
 }
 
 It appears that D ASSUMES myC is really a myC*(in C++)
 
 and therefore requires
 
 C myC = new C(3);
 // but this ALWAYS requires calling the memory allocator
 // this is what Java does (forces your Class instance onto the Heap)
 
 Is there any way in D to instantiate a stack object ?
 
 Will a struct do?
Structs are value types. Therefore, they go on the stack unless you have a pointer to them. struct Foo {} Foo a; //on stack Foo* b = new Foo(); //on heap Classes are reference types. The are therefore always on the heap. It's like what you get in Java. class Bar {} Bar a; //On the heap. Structs do not have inheritance (and thus don't have polymorphism), but they can be used for RAII. Assigning one to another means copying it (or moving it if the compiler determines that it can). Because struct member functions are not virtual, they often can be inlined. Classes do have inheritance (and thus do have polymorphism), but they can't use RAII. Assigning one to another means assigning a reference. Both references now refer to the same object. You'll have to use a clone method of some kind to get an actualy, deep copy. Because class member functions are almost always virtual, it's much rarer that they can be inlined. The language did have scoped classes, which put the class itself on the stack instead of the heap: class Bar {} scope Bar a; //On the stack. But it's inherently unsafe, so it's being removed from the language. There will be a library solution to do it, but again, it's unsafe. If you were to pass a scaped class to any other function, you risk the reference escaping, and then you'll end up with a reference to an object that doesn't exist once the function that declared it exits. Classes are meant to be on the heap. structs are meant to be on the stack but can be on the heap. Generally-speaking, if you use a class if you need a reference type or need polymorphism. Otherwise, you use a struct (reasons for using one or the other can, of course, get more complicated that that, but that's the core of it). Allowing classes on the like C++ does allows for problems like sheering and disallows polymorphism. D opted to split the concept into two different types of types: classes and structs. The language which is closest to D with regards to structs and classes that I'm aware of is C#, though I believe that D structs are definitely more powerful than C# structs. In D, you just don't use classes as often as you'd do in Java or C++, because structs in D do a lot of what classes do in those languages. For any user-defined type, you need to decide whether a struct or a class is more appropriate for what you're trying to do. The approach has definite benefits, but it does take some getting used to.
 Does a struct have a constructor (as opposed to an opcall?)
structs can have constructs just like classes - they just can't have default constructors. The reason for this is that all types in D have an init property that variables of that type are initialized to when they are default initialized. For integral types, it's 0; for bool, it's false; for references and pointers it's null; etc. For structs, it's what the member variables are directly initialized to. init precludes having an arbitrary constructor, because init must be determinable at compile time, can't have exceptions being thrown, etc. We may get some sort of limited default constructor for structs at some point, but it's not at all straightforward to have one, so we don't. The solution then, if you need one, is to use a static opCall() function, and then do Foo a = Foo(); //calls static opCall(). instead of Foo a; //Just uses Foo.init. So, structs _do_ have constructors, just not default constructors.
I should probably add that structs can have reference semantics as well if they have pointers or references internally, and they don't have a postblit constructor or, it doesn't do a deep copy (a postblit constructor - this(this) - being the constructor which runs after a struct has been memcpy-ed by the compiler, since the default copying of a struct uses memcpy). structs are generally value types, however, and classes are always reference types. - Jonathan M Davis
Dec 19 2010