August 28, 2002
Let me start by saying that the struct solution would work for me.  I can't resist the urge to ramble on, however.

Just for comment:

1. Since structs don't participate in inheritance, D already has variable types that cannot participate in Stream solutions or solutions that leverage the Object interface.  Which suggests that arguments against non-convertible auto/scoped/counted classes can also be made against the structs that have been there since day one...

2. Presence of a destructor will tend to make people wonder why there isn't a constructor.  I know that the answer has to do with reducing complexity, but the question will continue to come up.

3. This kinda pushes an implementation detail up into user cognitive space. Prior to this suggestion, the major benefits of structs were alignment control and lightweight allocation (stack).  Now the second benefit (stack) turns into two benefits - lightweight allocation and scoping/d.o.f.  This feels slightly wrong, somehow.

4. I know that the impetus for using struct this way would be to minimize the marginal added complexity of d.o.f. behavior.  However, looking at the end result, I kinda feel the same way I do when I look at the 'static' keyword in C++ and have to stop and wonder if it is referring to a static thing or to a file-scope-restricted thing.  Or when I look at the 'const' keyword and have to stop and figure out if it is referring to the thing being constant, or claiming that it is legal for the thing to be called on a constant instance (For C++ programmers that may not have seen the usage I am talking about: 'const' after a member function declaration means that it is legal for a user to call that member function if they have a constant instance of that class.  Functions without this decoration cannot be called on a constant instance because the function might change the state of the class, which would violate the const-ness of the instance.  Of course, const functions can also change the state of the instance, but they're not *supposed* to...)  (Sorry for the tangential rambling)


Having said all of that, I have been thinking about how to use RAII.v2.  Since all classes derive (directly or indirectly) from Object, you could make:

struct Scoped
{
Object obj;
~this() {delete obj;}
}

and put that somewhere in Phobos.  Then, to scope something, I can just:

void MyFunc()
{
Scoped s;
s.obj = new MyClass;
// proceed willy-nilly
} // s.obj gets deleted here.

Since the scoping struct doesn't need any interface of the scoped object other than Object, this polymorphism works, and can be done once and for all inside the standard library.

I would like some form of initializer.  It would be great if we could use the "static initializer" syntax from struct.html for non-static structs:

void MyFunc()
{
Scoped s = {obj:new MyClass};
// proceed willy-nilly
} // s.obj gets deleted here.

Actually, there are nicer (or at least easier to type/read) syntaxes for this, but this would be consistent with existing language.

How bad would it be to allow initializer-only constructors for structs?  In C++, a constructor can use an initializer list to store incoming parameters in data members during creation.  Since that would be the only use for a struct constructor anyway, initializer-only constructors should be sufficient for anything anyone needs in a struct (more complicated stuff gets done in a class, like it should).  This would make the syntax something more like:

struct Scoped
{
Object obj;
this(Object o) : obj(o); // or pick a better syntax, this is "like" C++
~this() {delete obj;}
}

void MyFunc()
{
Scoped s(new MyClass);
// proceed willy-nilly
} // s.obj gets deleted here.

I don't know if limiting the nature of the constructor helps any or not.  An initializer-based constructor may cause just as many problems as a full-fledged constructor.

Mac


August 28, 2002
"Mac Reiter" <Mac_member@pathlink.com> wrote in message news:akj7dl$nc$1@digitaldaemon.com...
> 1. Since structs don't participate in inheritance, D already has variable
types
> that cannot participate in Stream solutions or solutions that leverage the Object interface.  Which suggests that arguments against non-convertible auto/scoped/counted classes can also be made against the structs that have
been
> there since day one...

Yes, but with this scheme there are only 2 kinds of objects instead of 3.

> 2. Presence of a destructor will tend to make people wonder why there
isn't a
> constructor.  I know that the answer has to do with reducing complexity,
but the
> question will continue to come up.

You're right. We'll have to see how this goes.

> 3. This kinda pushes an implementation detail up into user cognitive
space.
> Prior to this suggestion, the major benefits of structs were alignment
control
> and lightweight allocation (stack).  Now the second benefit (stack) turns
into
> two benefits - lightweight allocation and scoping/d.o.f.  This feels
slightly
> wrong, somehow.

It seems natural to me <g>.

> 4. I know that the impetus for using struct this way would be to minimize
the
> marginal added complexity of d.o.f. behavior.  However, looking at the end result, I kinda feel the same way I do when I look at the 'static' keyword
in
> C++ and have to stop and wonder if it is referring to a static thing or to
a
> file-scope-restricted thing.  Or when I look at the 'const' keyword and
have to
> stop and figure out if it is referring to the thing being constant, or
claiming
> that it is legal for the thing to be called on a constant instance

Structs were already scoped, they just didn't have destructors.

> Having said all of that, I have been thinking about how to use RAII.v2.
Since
> all classes derive (directly or indirectly) from Object, you could make:
>
> struct Scoped
> {
> Object obj;
> ~this() {delete obj;}
> }
>
> and put that somewhere in Phobos.  Then, to scope something, I can just:
>
> void MyFunc()
> {
> Scoped s;
> s.obj = new MyClass;
> // proceed willy-nilly
> } // s.obj gets deleted here.
>
> Since the scoping struct doesn't need any interface of the scoped object
other
> than Object, this polymorphism works, and can be done once and for all
inside
> the standard library.

Yes, although Scoped can be made a template.


> I would like some form of initializer.  It would be great if we could use
the
> "static initializer" syntax from struct.html for non-static structs:
>
> void MyFunc()
> {
> Scoped s = {obj:new MyClass};
> // proceed willy-nilly
> } // s.obj gets deleted here.
>
> Actually, there are nicer (or at least easier to type/read) syntaxes for
this,
> but this would be consistent with existing language.

Yes.

> How bad would it be to allow initializer-only constructors for structs?
In C++,
> a constructor can use an initializer list to store incoming parameters in
data
> members during creation.  Since that would be the only use for a struct constructor anyway, initializer-only constructors should be sufficient for anything anyone needs in a struct (more complicated stuff gets done in a
class,
> like it should).  This would make the syntax something more like:
>
> struct Scoped
> {
> Object obj;
> this(Object o) : obj(o); // or pick a better syntax, this is "like" C++
> ~this() {delete obj;}
> }
>
> void MyFunc()
> {
> Scoped s(new MyClass);
> // proceed willy-nilly
> } // s.obj gets deleted here.
>
> I don't know if limiting the nature of the constructor helps any or not.
An
> initializer-based constructor may cause just as many problems as a
full-fledged
> constructor.

Initializer lists are a bug in C++ <g>.


August 28, 2002
Walter wrote:

> "Mac Reiter" <Mac_member@pathlink.com> wrote in message news:akj7dl$nc$1@digitaldaemon.com...
> > 1. Since structs don't participate in inheritance, D already has variable
> types
> > that cannot participate in Stream solutions or solutions that leverage the Object interface.  Which suggests that arguments against non-convertible auto/scoped/counted classes can also be made against the structs that have
> been
> > there since day one...
>
> Yes, but with this scheme there are only 2 kinds of objects instead of 3.

I think that that is an argument FOR the other style.  As I see it, there are
(at least) three types of usage:
* struct (pure data map)
* garbage collected class (arbitrary lifespan)
* raii class (d.o.f.)

Those 3 should be well distinguished.

--
The Villagers are Online! villagersonline.com

.[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
.[ (a version.of(English).(precise.more)) is(possible) ]
?[ you want.to(help(develop(it))) ]


August 28, 2002
Hi,

"Walter" <walter@digitalmars.com> wrote in message news:akjh94$k4g$1@digitaldaemon.com...
>> 2. Presence of a destructor will tend to make people wonder why there isn't a constructor.
> You're right. We'll have to see how this goes.

Constructors are nice, but not really that important. They do not offer functionality, that could not be realized using regular methods. Destructors, on the other hand, are a gift to man-kind. Deterministic destructors makes them even more so. Concentrating on destructors first seems very right to me.

I have a thought about constructors, though:

A new object is often created like this:

    SomeClass a = new SomeClass(args);

You wrote yourself sometime ago, that it was clumsy. I agree. How about:

   SomeClass a(args);

meaning that if an argument list was present, it was to be instantiated, and the constructor called. The argument list could be omitted like today like this:

   SomeClass a; // initialized to null

The destinction between null initialization and initialization using a constructor without arguments would be:

   SomeClass a; // null
   SomeClass a(); // allocates new object and initializes it using this()

The existing syntax would still have to be supported in cases like this:

   SomeClass a = new DerivedClass(args);


Regards,
Martin M. Pedersen








August 28, 2002
Hi,

"Martin M. Pedersen" <mmp@www.moeller-pedersen.dk> wrote in message news:akjir7$ns6$1@digitaldaemon.com...
>    SomeClass a(); // allocates new object and initializes it using this()

I might add, that this might conflict with prototypes as we know them from C. However, prototypes belongs at global scope, object instantiation do not. So, there does not need to be a conflict.

Regards,
Martin M. Pedersen



August 28, 2002
"Russ Lewis" <spamhole-2001-07-16@deming-os.org> wrote in message news:3D6D4905.4BAF1C5C@deming-os.org...
> I think that that is an argument FOR the other style.  As I see it, there
are
> (at least) three types of usage:
> * struct (pure data map)
> * garbage collected class (arbitrary lifespan)
> * raii class (d.o.f.)
>
> Those 3 should be well distinguished.

They are:
1) struct
2) class
3) struct with destructor

The distinction between 1 and 3 should be comfortable for anyone used to destructors in C++.


August 28, 2002
"Martin M. Pedersen" <mmp@www.moeller-pedersen.dk> wrote in message news:akjir7$ns6$1@digitaldaemon.com...
> "Walter" <walter@digitalmars.com> wrote in message news:akjh94$k4g$1@digitaldaemon.com...
> >> 2. Presence of a destructor will tend to make people wonder why there isn't a constructor.
> > You're right. We'll have to see how this goes.
> Constructors are nice, but not really that important. They do not offer functionality, that could not be realized using regular methods.

I agree. Also, constructors for stack objects have wretched parsing ambiguity problems. (Is it a declaration? Is it a function call? Is it a bird? Is it a plane? <g>)

> Destructors, on the other hand, are a gift to man-kind. Deterministic destructors makes them even more so. Concentrating on destructors first seems very right to me.

Yes.

> I have a thought about constructors, though:
> A new object is often created like this:
>     SomeClass a = new SomeClass(args);
> You wrote yourself sometime ago, that it was clumsy. I agree. How about:
>    SomeClass a(args);
> meaning that if an argument list was present, it was to be instantiated,
and
> the constructor called. The argument list could be omitted like today like
> this:
>    SomeClass a; // initialized to null
> The destinction between null initialization and initialization using a
> constructor without arguments would be:
>    SomeClass a; // null
>    SomeClass a(); // allocates new object and initializes it using this()
> The existing syntax would still have to be supported in cases like this:
>    SomeClass a = new DerivedClass(args);


That's the "is it a function declaration or a declaration with constructor".


August 29, 2002
"Martin M. Pedersen" <mmp@www.moeller-pedersen.dk> wrote in message news:akjj57$odk$1@digitaldaemon.com...
> "Martin M. Pedersen" <mmp@www.moeller-pedersen.dk> wrote in message news:akjir7$ns6$1@digitaldaemon.com...
> >    SomeClass a(); // allocates new object and initializes it using
this()
>
> I might add, that this might conflict with prototypes as we know them from C. However, prototypes belongs at global scope, object instantiation do
not.
> So, there does not need to be a conflict.

You're right, but it would be better if the syntax for constructors was unique without relying on scope context. In C++, as things got added to the language, fairly simple ambiguity problems just got worse and worse. It's so bad now that even if the compiler parses it correctly, it is unable to generate a reasonable error message for any goofs. Syntactical redundancy is the key to being able to generate good error diagnostics.


August 29, 2002
Walter wrote:

> They are:
> 1) struct
> 2) class
> 3) struct with destructor
>
> The distinction between 1 and 3 should be comfortable for anyone used to destructors in C++.

I hear you, but (from my perspective), the key difference between a struct and a class is not the stack/heap question, but the active/inactive and binary layout issues.

As I think of them, a struct is a primarily a binary layout mechanism - its purpose is to give a portable, definable, C-interoperable design for the data. That it can be laid out on the stack is very much secondary in that view. Since it is primarily a binary layout mechanism, it is an "inactive" mechanism - it doesn't contain any virtualization or implicit code (constructors and destructors are implicit code, since they run without us explicitly calling them).  Member functions of structs, IMHO, are just (good) syntax sugar - they simply serve to encapsulate common operations on the structure.

A class, on the other hand, is fundamentally an active mechanism.  It has implicit code (constructors, destructors, operator overloading, etc.) and does a lot of virtualization.  It explicitly does NOT allow you to define binary layout.

In this view, an active property of the object (raii) fits far better with a
class than a struct.

--
The Villagers are Online! villagersonline.com

.[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
.[ (a version.of(English).(precise.more)) is(possible) ]
?[ you want.to(help(develop(it))) ]


August 29, 2002
"Russ Lewis" <spamhole-2001-07-16@deming-os.org> wrote in message news:3D6E5614.E9ACCEE9@deming-os.org...
> Walter wrote:
>
> > They are:
> > 1) struct
> > 2) class
> > 3) struct with destructor
> >
> > The distinction between 1 and 3 should be comfortable for anyone used to destructors in C++.
>
> I hear you, but (from my perspective), the key difference between a struct
and
> a class is not the stack/heap question, but the active/inactive and binary layout issues.
>
> As I think of them, a struct is a primarily a binary layout mechanism -
its
> purpose is to give a portable, definable, C-interoperable design for the
data.
> That it can be laid out on the stack is very much secondary in that view. Since it is primarily a binary layout mechanism, it is an "inactive"
mechanism
> - it doesn't contain any virtualization or implicit code (constructors and destructors are implicit code, since they run without us explicitly
calling
> them).  Member functions of structs, IMHO, are just (good) syntax sugar -
they
> simply serve to encapsulate common operations on the structure.
>
> A class, on the other hand, is fundamentally an active mechanism.  It has implicit code (constructors, destructors, operator overloading, etc.) and
does
> a lot of virtualization.  It explicitly does NOT allow you to define
binary
> layout.
>
> In this view, an active property of the object (raii) fits far better with
a
> class than a struct.

A struct also provides a mechanism for lightweight objects intended to be on the stack. (The absense of these is a large burden in Java.) Providing a destructor for raii is a natural extension of that.