this() not executing code on structs

Denis Koroskin 2korden at gmail.com
Wed Oct 21 12:47:46 PDT 2009


On Wed, 21 Oct 2009 20:15:16 +0400, Andrei Alexandrescu  
<SeeWebsiteForEmail at erdani.org> wrote:

> Today, structs can't write their own this(). There aren't very solid  
> reasons for that except that it makes language implementation more  
> difficult.
>
> I wonder how much of a problem that could be in practice. I realized  
> today that the "Counted" example - a classic C++ primer example  
> featuring a struct that counts its own instances - cannot be implemented  
> in D.
>
> In C++ the counted example looks like this:
>
> struct Counted {
>     static unsigned count;
>     unsigned myCount;
>     Counted() { myCount = count++; }
>     Counted(const Counted& rhs) { myCount = count++; }
>     Counted& operator=(const Counted& rhs) {
>        // no writing to myCount
>        return *this;
>     }
>     ~Counted() {
>        --count;
>     }
> }
>
> In D there's no chance to write Counted because you can always create  
> Counted objects without executing any code.
>
> struct Counted {
>     static uint count;
>     uint myCount;
>     this() { myCount = count++; }       // ERROR
>     this(this) { myCount = count++; }
>     ref Counted opAssign(Counted rhs) {
>        // no writing to myCount
>        return this;
>     }
>     ~this() {
>        --count;
>     }
> }
>
> This being a toy example, I wonder whether there are much more serious  
> examples that would be impossible to implement within D.
>
>
> Andrei

I agree it's a very annoying limitation, but I believe there is a  
rationale behind it.

Imagine a class that aggregates a struct:

struct Foo { ... }

class Bar
{
    Foo foo;
    this() {}
    // ...
}

The class object construction is now consists of two steps:

1) memcpy the Bar.classinfo.init
2) call __ctor()

Unlike C++, there is no stage at which class members are being  
initialized, and it simplifies things quite a lot.

Think about the following: what happens if structs will be allowed default  
ctors? How to avoid double initialization?

You may end up with design very similar to C++ in this case (including an  
initialization list).

Some problems could be solved with an enforced explicit initialization of  
each member. That's the only way I see now that would avoid double  
initialization of a struct in presence of default and non-default ctors:

struct Scoped(T) // I like this name a lot more than InPlace, and  
especially InSitu
{
     this(Args...)(Args args)
     {
         T obj = cast(T)data.ptr;
         obj.__ctor(args);
     }

     ubyte[T.classinfo.init.length] data = T.classinfo.init; // I believe  
this should work at compile-time, since classinfo is immutable
}

class Foo
{
     this() {}
     this(int i) {}
}

class Bar
{
     Scoped!(Foo) foo;
     this()
     {
          // foo is already constructed by now (default ctor is called)  
which is cool
          // but what if I need to initialize it with some other ctor?
          foo = Scoped!(Foo)(42); // double initialization
     }

     int doesntNeedToBeInitializedExplicitlyInACtor = 17;
}

Enforcing explicit initialization of each member (unless its value is set  
at the declaration, as in the example above) is gracefully solving this  
issue.

Also think about exception safety: what if an exception is thrown in a  
class ctor - should the dtors be invoked on initialized members or not? If  
yes, in what order? D doesn't enforce initialization order, so it's a bit  
tricky to determine what members are already initialized and need to be  
destroyed (with a dtor call) efficiently. C++ handles issues like this  
very well IMO. I believe D should also have a sound solution to this  
problem.



More information about the Digitalmars-d mailing list