If Bar
is a struct, you'll get what you want, without needing scope (however, structs cannot have default constructors, to the writeln("Bar")
won't be permitted). Unless you need interfaces and virtual functions, I think structs are generally the better tool to use in D since they are more flexible. You can very easily have a scope struct with deterministic destruction, no indirection, etc. If you want it to be a reference, you can always use a pointer to it or a pointer to a private internal implementation to force reference semantics.
It is possible to wrap a class in a struct, including in-place allocation:
import std.stdio;
// bar is first because of https://d.puremagic.com/issues/show_bug.cgi?id=12278
class Bar {
this() { writeln("Bar"); }
~this() { writeln("~Bar"); }
}
class Foo {
this() {
writeln("Foo");
// since there's no default ctor for structs, we force
// initialization right here
inside = InPlace!Bar.getDefault();
}
~this() { writeln("~Foo"); }
// InPlace instead of scope...
InPlace!Bar inside;
}
struct InPlace(T) {
// an in-place buffer for the class data...
private byte[__traits(classInstanceSize, T)] rawData;
@property T obj() { return cast(T) rawData.ptr; }
alias obj this; // DANGER: don't escape this reference!
@disable this(); // force initialization at the usage site
// get it default-constructed
static InPlace!T getDefault() {
InPlace!T t = void;
t.initializeObject();
t.obj.__ctor();
return t;
}
void initializeObject() {
assert(__traits(classInstanceSize, T) == 8);
assert(T.classinfo.init.length == 8);
assert(this.rawData.length == 8);
this.rawData[] = T.classinfo.init[];
}
// ctors with args
this(T...)(T t) {
initializeObject();
obj._ctor(t);
}
~this() {
.destroy(obj); // call the class destructor in the struct dtor
}
}
void main()
{
scope f = new Foo();
writeln("I'm a main!");
}
edit: std.typecons.scoped does this too, I wasn't sure it would work in the class member but Михаил Страшун's answer showed it indeed does. /edit
That will do the trick... but I recommend against it. You should just make Bar itself a struct if you can. The danger with this wrapper magic is if you escape the reference to the inner object you are liable to get random crashes if the outer one is destroyed first. It might be ok if it is private though, I guess, though there is still some danger there (private in D is module private, so another function in that module might still escape it, but still, if you are careful enough you can be ok with it.)
Running gives:
Foo
Bar
I'm a main!
~Foo
~Bar
BTW the scope thing you use in the OP is currently deprecated because it isn't implemented safely/completely. I don't think it even stack allocates right now, it is more like automatically inserting scope(exit) .destroy(foo);
right after the declaration.