Stack-based object instantiation in D
Asked Answered
P

3

6

I'm learning D, and am confused by an error I'm getting.

Consider the following:

module helloworld;

import std.stdio;
import std.perf;

ptrdiff_t main( string[] args )
{
     auto t = new PerformanceCounter;    //From managed heap
     //PerformanceCounter t;             //On the stack

     t.start();
     writeln( "Hello, ", size_t.sizeof * 8, "-bit world!" );
     t.stop();

     writeln( "Elapsed time: ", t.microseconds, " \xb5s." );

     return 0;
} //main()

Yields a perfectly respectable:

Hello, 32-bit world!
Elapsed time: 218 µs.

Now consider what happens when I attempt to initialize PerformanceCounter on the stack instead of using the managed heap:

 //auto t = new PerformanceCounter;  //From managed heap
 PerformanceCounter t;               //On the stack

Yields:

--- killed by signal 10

I'm stumped. Any thoughts as to why this breaks? (DMD 2.049 on Mac OS X 10.6.4). Thanks in advance for helping a n00b.

Plausible answered 23/10, 2010 at 22:2 Comment(0)
B
5

You seem to be mixing up C++ classes with D classes.

D classes are always passed by reference (unlike, say, C++ classes), and PerformanceCounter t does not allocate the class on the stack, merely a pointer to it.

This means that t is set to null because, well, null is the default initializer for pointers - hence the error.

EDIT: You can think of D Foo class as a C++'s Foo*.

If you want this to be allocated on the heap, you could try using structs instead - they can also have methods, just like classes. They do not, however, have inheritance.

Batavia answered 23/10, 2010 at 22:7 Comment(6)
Thank you for the clue!! :) (That makes sense and also answers why the object dereference/member operator (->) isn't required.Plausible
Well, the -> operator wouldn't be required anyways - for example, in C, the compiler (if smart enough) can actually warn you that you're using the wrong operator. In a similar manner, D's pointers (say, Foo*) do not need ->, but work with the dot: Foo* foo = <whatever>; foo.bar = 5;Granville
Sorry, I was referring to not needing it for the dynamically allocated object, not the stack-based one.Plausible
If you know C# or Java, it might be more useful to think of D class instances as the same as C#'s and Javas.Ebullience
Nothing is implicitly passed by reference in D. If you want pass by reference, you have to use the ref keyword, always. The key point is that class types use reference semantics. You can either pass references by value (Foo param) or reference variables by reference (ref Foo param). Passing a reference and passing by reference are not the same thing at all.Sacristan
You can think of D Foo class as a C++'s Foo& reference, because that's what it actually is.Saldana
D
3

The most obvious answer is to use a struct. If you're using a library that you don't have control over or something and the heap allocations are a performance problem, you can use the std.typecons.scoped functionality to unsafely allocate a class instance on the stack. The instance is still passed by reference and if its lifetime exceeds the lifetime of the current stack frame, undefined behavior will result. The scope keyword as per anoncow's answer will work, but is scheduled for deprecation in D2.

Donnetta answered 23/10, 2010 at 23:42 Comment(3)
Oh no! I'd just fallen in love with scope, too! I'm using D2, so I'll go read up on why, this has been deprecated, sadly. Thank you for the struct suggestion--C++'s stack allocation mechanism suffers from the same gotcha as well.Plausible
I believe that it has been deprecated essentially because it's unsafe. Having a class on the stack runs the risk of keeping a reference to it at after the function returns and having that reference no longer be valid. If it's on the heap, it won't be garbage collected until after all references to it are gone, so you won't have that problem.Wald
@anoncow: IIRC, scope is replaced with library solution. Smth like Scoped!(YourType) varHemi
P
1

Thanks, Tim.

Thanks to your answer, I was able to find the following at http://www.digitalmars.com/d/2.0/memory.html:


Allocating Class Instances On The Stack

Class instances are normally allocated on the garbage collected heap. However, if they: are allocated as local symbols in a function are allocated using new use new with no arguments (constructor arguments are allowed) have the scope storage class then they are allocated on the stack. This is more efficient than doing an allocate/free cycle on the instance. But be careful that any reference to the object does not survive the return of the function.

class C { ... }

scope c = new C();  // c is allocated on the stack
scope c2 = new C(5);    // allocated on stack
scope c3 = new(5) C();  // allocated by a custom allocator

If the class has a destructor, then that destructor is guaranteed to be run when the class object goes out of scope, even if the scope is exited via an exception.


My code now reads

scope t = new PerformanceCounter();  //On the stack 

This (allegedly) allocates on the stack and runs fine. :)

Thanks again!

Plausible answered 23/10, 2010 at 22:30 Comment(1)
As dsimcha points out in his post, however, scope is scheduled for deprecation, so it's not a good long term solution.Wald

© 2022 - 2024 — McMap. All rights reserved.