February 09, 2009
Weed wrote:
> Christopher Wright пишет:
>> Weed wrote:
>>> And if I need some different such combinations? For each it is necessary
>>> to write such 8-10 lines? This is terrible!
>> You need to add those lines for every method you need virtual dispatch
>> with for your value type. It's an overhead of three lines per method,
>> two for the interface (declaration and member), and one extra line where
>> you create the struct. If you're reasonable, your struct constructor
>> will create a default instance.
>>
>> So, it's not that great an overhead.
>
> Do not be surprised that so many continue to write in C++! :)

huh?

there are still billions of people that continue smoking even though the problems that causes are well known.

people have bad habits - using C++ instead of proper languages (like D) is one of them. Unless a person wants to be cured, nothing can be done to cure him of such a strong addiction.

I hope you too will one day be cured of the illness that is C++.
February 09, 2009
Rainer Deyke wrote:
> Andrei Alexandrescu wrote:
>> The slicing problem exists in spades in this example, or better put its
>> converse (your code will fire asserts when it shouldn't). The reason is
>> rather subtle, so please try the code out to edify yourself.
> 
> You're referring to the automatically generated copy constructor in
> class 'avatar' which calls the copy constructor in class 'person',
> right?  That's a bug in my silly untested example code, but it's not the
> slicing problem.
> 
> Fix 1:
> 
> class person {
>   person(person const& org, bool allow_slicing = false) {
>     assert(allow_slicing || typeid(org) == typeid(*this));
>   }
> };
> 
> class avatar : public person, public deity {
>   avatar(avatar const& org, bool allow_slicing = false)
>     : person(org, true)
>   {
>   }
> };

So the problem exists since you are explicitly addressing it.

> Fix 2:
> 
> Trust the programmer to pay attention and remove the silly unnecessary
> assertion from class 'person'.
> 
> 

So the problem exists since you are trusting the programmer to avoid it.


Andrei
February 09, 2009
On Mon, 09 Feb 2009 15:53:59 +0300, Don <nospam@nospam.com> wrote:

> Michel Fortin wrote:
>> On 2009-02-09 07:00:56 -0500, Weed <resume755@mail.ru> said:
>>
>>>> No. By forbiding the cases that leads to slicing, like returning a
>>>> polymorphic object by value.
>>>
>>> Let's think, can there are other ways to solve problem?
>>>
>>> Here, for example my reasons:
>>> http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81958
>>  I'm
>>>
>> not sure I'm getting what you mean in that post.
>>  It seems like you want the ability to copy classes, and thus pass them by value, but only when there would be no type conversion.
>>  I'm not getting where this can be useful though: by forcing your classes to be of a specific type, with no derived types allowed, you're losing polymorphism. The only advantage you have is that your type can be derived from another so you can reuse some of its implementation. There are other ways to acheive that in D however (mixins come to mind).
>>
>
> The one case I could think of was the strategy pattern: no data is added (including, no virtual functions) -- the only thing that's added is a different implementation of an existing virtual function. In such a situation, the slicing problem cannot happen.

Michel has posted (2 posts up) an example of a class that happen to have no member fields at all (virtual methods only). And yet it still suffers from slicing.

> You can have an array of polymorphic types. But the language won't allow it.
>
> However, I've encountered this problem in C++ as well. Allowing stack-based classes is NOT sufficient to solve it. What you actually want is a struct with a vtable pointer stored IN the struct itself (rather than one instance of the vtable per struct).




February 09, 2009
Andrei Alexandrescu wrote:
> So the problem exists since you are trusting the programmer to avoid it.

The slicing problem exists in the sense that it is possible for a bad
programmer to accidentally slice an object.  However:
  - This is not a problem with the language, but an avoidable programmer
error in the language.
  - There are far more common programmer errors in C++ against which D
does not guard.  For example, dangling pointers to stack variables that
have gone out of scope.
  - D's response to this perceived problem is far too heavy-handed,
because it disallows useful correct code.
  - I would even say that the reference type classes in D lead to more
problems.  It is very easy for two objects in D to accidentally share
internal state, something that is impossible with value types.
  - Even if the slicing problem were a major issue in C++, it should be
possible to partially or totally fix it at the language level in D
without sacrificing inheritance for value types.


-- 
Rainer Deyke - rainerd@eldwood.com
February 10, 2009
On 2009-02-09 16:02:10 -0500, "Denis Koroskin" <2korden@gmail.com> said:

>> The one case I could think of was the strategy pattern: no data is added  (including, no virtual functions) -- the only thing that's added is a  different implementation of an existing virtual function. In such a  situation, the slicing problem cannot happen.
> 
> Michel has posted (2 posts up) an example of a class that happen to have no member fields at all (virtual methods only). And yet it still suffers from slicing.

Indeed. But that's in C++. It could certainly be possible to make that work differently in another language. I'm not sure it's worth it for D though.

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

February 11, 2009
Rainer Deyke wrote:
> Andrei Alexandrescu wrote:
>> So the problem exists since you are trusting the programmer to avoid it.
> 
> The slicing problem exists in the sense that it is possible for a bad
> programmer to accidentally slice an object.  However:
>   - This is not a problem with the language, but an avoidable programmer
> error in the language.

Yes. There are many examples of this: memory leaks, array bounds errors, invalid casts, that sort of thing. If you don't mind explicitly coding around these, stay with C or C++. You'll have these errors, but if you're careful and disciplined, it won't happen too often. It will, however, take time and attention to avoid these errors, and to debug them when you slip up.

In some cases, a language change can eliminate a class of bugs. Sometimes it's worthwhile. In my experience, the cost of forbidding polymorphic value types is pretty much zero.

>   - There are far more common programmer errors in C++ against which D
> does not guard.  For example, dangling pointers to stack variables that
> have gone out of scope.

Yep. Escape analysis is tricky, but I'd like to see it in D.

>   - D's response to this perceived problem is far too heavy-handed,
> because it disallows useful correct code.

Well, D is a systems language, so you can still get the same effect, but it's sufficiently difficult and ugly that it is likely not worthwhile. On the other hand, you can probably simplify it to a single template mixin (see the thread on struct interfaces). Given that it is not a common need (as far as I can tell) and is an inherently unsafe thing, I believe that it is reasonable.

>   - I would even say that the reference type classes in D lead to more
> problems.  It is very easy for two objects in D to accidentally share
> internal state, something that is impossible with value types.

struct A
{
	int* i;
}

A a, b;
int* i = new int;
a.i = i;
b.i = i;

Hey look, they share internal state!

What mechanism for sharing state is available to by-reference objects but not by-value objects?
February 12, 2009
Christopher Wright wrote:
>>   - I would even say that the reference type classes in D lead to more
>> problems.  It is very easy for two objects in D to accidentally share
>> internal state, something that is impossible with value types.
> 
> struct A
> {
>     int* i;
> }
> 
> A a, b;
> int* i = new int;
> a.i = i;
> b.i = i;
> 
> Hey look, they share internal state!

That's not accidental.  You did that on purpose.

> What mechanism for sharing state is available to by-reference objects but not by-value objects?

struct C {
  T t;
}

C c1;
C c2 = c1;

If T was a reference type, 'c1' and 'c2' now share state, and it's up to the programmer to write code to prevent this.  Moreover, the D language doesn't even give a hint as to whether T or a reference type or a value type.  If T is a template parameter, it could be both.

What I'd really like to see is a hybrid of struct and class that has the RAII and value semantics of a struct, but the polymorphism of classes.

polymorphic_struct A {
}

polymorphic_struct B : A {
}

{
  B b;
  A a = b; // Creates a true non-sliced copy of 'b'.
} // Destructors are called when the variables go out of scope.

At the implementation level, the objects could be placed on the heap unless the compiler determines that this is not necessary.  The goal is not performance, but consistent value semantics throughout the language.

The alternative is to work backwards and create a wrapper for classes to give them value semantics:

struct value_semantics(T) {
  this(T v) {
    this.value = v;
  }
  this(this) {
    this.value = this.value.dup;
  }
  ~this() {
    delete this.v;
  }
  T opDot() {
    return this;
  }
  T value;
}


-- 
Rainer Deyke - rainerd@eldwood.com
February 12, 2009
Rainer Deyke wrote:
>> What mechanism for sharing state is available to by-reference objects
>> but not by-value objects?
> 
> struct C {
>   T t;
> }
> 
> C c1;
> C c2 = c1;
> 
> If T was a reference type, 'c1' and 'c2' now share state, and it's up to
> the programmer to write code to prevent this.  Moreover, the D language
> doesn't even give a hint as to whether T or a reference type or a value
> type.  If T is a template parameter, it could be both.

Okay, reference types don't have value semantics. That's exactly what I would expect. Why is it a problem? Because there is no explicit builtin copying mechanism for classes? If there were such, would it be a problem if copying were not the default?
February 12, 2009
Rainer Deyke wrote:
> Christopher Wright wrote:
>> What mechanism for sharing state is available to by-reference objects
>> but not by-value objects?
> 
> struct C {
>   T t;
> }
> 
> C c1;
> C c2 = c1;
> 
> If T was a reference type, 'c1' and 'c2' now share state, and it's up to
> the programmer to write code to prevent this.

No, they don't. Each instance of C has its own copy of the 't' reference.


> Moreover, the D language
> doesn't even give a hint as to whether T or a reference type or a value
> type.

Like you can't write "typedef X* T;" in C...


> If T is a template parameter, it could be both.

That's what IsExpression and static assertions are for.


-- 
Tomasz Stachowiak
http://h3.team0xf.com/
h3/h3r3tic on #D freenode
February 12, 2009
Tom S wrote:
> Rainer Deyke wrote:
>> If T was a reference type, 'c1' and 'c2' now share state, and it's up to the programmer to write code to prevent this.
> 
> No, they don't. Each instance of C has its own copy of the 't' reference.

That's like saying object slicing is an intentional feature.  Given the intended semantics of 'C', it's a bug.

>> Moreover, the D language
>> doesn't even give a hint as to whether T or a reference type or a value
>> type.
> 
> Like you can't write "typedef X* T;" in C...

The C programmer who does that should be shot.  In C++ this idiom has some valid uses (iterators, template metaprogramming), but there remains an important distinction: the syntax for dealing with variables of type 'X*' is different than the syntax for dealing with variables of type 'X'.


-- 
Rainer Deyke - rainerd@eldwood.com