In D, it's possible to allocate classes on the stack using scope
, i.e.
void foo()
{
scope example = new Bar();
}
Now, if class Foo
has class Bar
as a member, is there any way to store Bar
in-place inside Foo
and have it tear down with Foo
a la C++?
I had hoped that
import std.stdio;
class Foo {
this() { writeln("Foo"); }
~this() { writeln("~Foo"); }
scope Bar inside;
}
class Bar {
this() { writeln("Bar"); }
~this() { writeln("~Bar"); }
};
void main()
{
scope f = new Foo();
writeln("I'm a main!");
}
would yield something like
Bar
Foo
I'm a main!
~Bar
~Foo
Instead I only get
Foo
I'm a main!
~Foo
It seems like storing a class member as a garbage-collected reference instead of in-place is just needlessly complicating the heap layout for little benefit. And what is scope
doing in this case, if not specifying to hold Bar
in-place?
A JavaScript Object is a collection of Key-Value pairs, and nested objects are objects that have other objects inside them as their property. Nesting is a widely used practice in programming as it provides the code with more enhanced details.
14. Is it possible that an object of is passed to a function, and the function also have an object of same name? Explanation: There can't be more than one variable or object with the same name in same scope.
An object stores its state in fields (variables in some programming languages) and exposes its behavior through methods (functions in some programming languages). Methods operate on an object's internal state and serve as the primary mechanism for object-to-object communication.
In Java, we can create Objects in various ways: Using a new keyword. Using the newInstance () method of the Class class. Using the newInstance() method of the Constructor class.
Don't use scope classes. Those are effectively deprecated and remain only as legacy of old days. There is a Phobos helper that achieves similar effect. Your example re-written:
import std.stdio;
import std.typecons;
alias ScopedBar = typeof(scoped!Bar());
class Bar {
this() { writeln("Bar"); }
~this() { writeln("~Bar"); }
};
class Foo
{
this()
{
this.inside = scoped!Bar();
writeln("Foo");
}
~this() { writeln("~Foo"); }
ScopedBar inside;
}
void main()
{
writeln("Main before scope");
{
auto f = scoped!Foo();
}
writeln("Main after scope");
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With