February 09, 2009
Rainer Deyke wrote:
> Example 3 (C++):
> 
> class person {
> public:
>   person(person const& org) {
>     assert(typeid(org) == typeid(*this));
>   }
>   virtual ~person() {}
> };
> 
> class avatar : public person, public deity {
> };
> 
> 'person' and 'avatar' are value types that can be used polymorphically.
>  In any context 'person&' may be either a reference to an instance of
> class 'person' or a polymorphic reference to an instance of any class
> that inherits from 'person'.  Unfortunately the C++ type system does not
> distinguish between these two uses.  It is an error (checked at runtime)
> to construct a new 'person' from a polymorphic reference, but any other
> use of 'person' as a value type or a polymorphic type is valid.  No
> slicing problem exists except through user error.

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.

Andrei
February 09, 2009
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)
  {
  }
};

Fix 2:

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


-- 
Rainer Deyke - rainerd@eldwood.com
February 09, 2009
Weed wrote:
> Christopher Wright пишет:
>> Weed wrote:
>>> Christopher Wright пишет:
>>>> Weed wrote:
>>>>> (Has started here: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81359)
>>>>>
>>>>>
>>>>>
>>>>> To me still does not give rest performance of classes (in comparison
>>>>> with C++ or with D structs)
>>>> On my system, your struct example averaged 0.36 seconds, and your class example averaged 0.38 seconds.
>>>>
>>>> Your benchmark is flawed in three ways:
>>>> 1. You're timing allocations in the class example. Use opAddAssign to
>>>> avoid it.
>>> Earlier I proved that it is impossible. For example here in such
>>> expression:
>>> ==============
>>> space_ship_1.calculatePathTo("Moon").getCheckpoint(3).getCoords;
>>>
>>> In this example we create a temporary class "path", create temporary
>>> class "checkpoint" and we take coords of checkpoint of this path. It is
>>> not expedient to us to store all this path and checkpoint because it is
>>> vary.
>>> ==============
>> In that example, you can use structs instead of classes. Your response to that is that structs do not participate in polymorphism.
>>
>> There was a suggestion elsewhere like this:
>> interface IPathResolver
>> {
>>     Checkpoint getCheckpoint(Path* path, int i);
>> }
>>
>> struct Path
>> {
>>     char[] path;
>>     // any other info you need....
>>     IPathResolver resolver;
>>     Checkpoint getCheckpoint(int value)
>>     {
>>         return resolver.getCheckpoint(this, value);
>>     }
>> }
>>
>> This way, you only allocate once for each type you need, you have polymorphism, and you can put stuff on the stack for quick access.
> 
> And if I need some different such combinations? For each it is necessary to write such 8-10 lines? This is terrible!

I'm sure you can do something with templates. :)
February 09, 2009
Weed Wrote:

> The code on C++ is also approximately in 6 times faster a code with classes on D. (I do not give an example on C++ because classes on C++ work just as structures in D.)

> class C {
>     int i;
>     real[5] unused; // to prevent returning this object in registers
> 
>     C opAdd( C src ) {
>         auto ret = new C;
>         ret.i = i + src.i;
>         return ret;
>     }
> }

Well, D class code here is not equivalent to C++ class code. D code has more features, namely, it's polymorphic: C.opAdd is able to work with classes, derived from C, while corresponding C++ code is unable to do so. If you can't use polymorphism, why do you use classes?
February 09, 2009
Kagamin пишет:
> Weed Wrote:
> 
>> The code on C++ is also approximately in 6 times faster a code with classes on D. (I do not give an example on C++ because classes on C++ work just as structures in D.)
> 
>> class C {
>>     int i;
>>     real[5] unused; // to prevent returning this object in registers
>>
>>     C opAdd( C src ) {
>>         auto ret = new C;
>>         ret.i = i + src.i;
>>         return ret;
>>     }
>> }
> 
> Well, D class code here is not equivalent to C++ class code. D code has more features, namely, it's polymorphic: C.opAdd is able to work with classes, derived from C, while corresponding C++ code is unable to do so. If you can't use polymorphism, why do you use classes?

There opAdd is only for demonstration. In real life it may be virtual
method getSomething()
February 09, 2009
Kagamin пишет:
> Weed Wrote:
> 
>> The code on C++ is also approximately in 6 times faster a code with classes on D. (I do not give an example on C++ because classes on C++ work just as structures in D.)
> 
>> class C {
>>     int i;
>>     real[5] unused; // to prevent returning this object in registers
>>
>>     C opAdd( C src ) {
>>         auto ret = new C;
>>         ret.i = i + src.i;
>>         return ret;
>>     }
>> }
> 
> Well, D class code here is not equivalent to C++ class code. D code has more features, namely, it's polymorphic: C.opAdd is able to work with classes, derived from C, while corresponding C++ code is unable to do so.

It is really true?
February 09, 2009
Weed Wrote:

> > Well, D class code here is not equivalent to C++ class code. D code has more features, namely, it's polymorphic: C.opAdd is able to work with classes, derived from C, while corresponding C++ code is unable to do so.
> 
> It is really true?

Of course.

> There opAdd is only for demonstration.

If your example has nothing in common with real-world tasks and needs, it's useless.
February 09, 2009
Kagamin пишет:
> Weed Wrote:
> 
>>> Well, D class code here is not equivalent to C++ class code. D code has more features, namely, it's polymorphic: C.opAdd is able to work with classes, derived from C, while corresponding C++ code is unable to do so.
>> It is really true?
> 
> Of course.
> 

 But I do not understand in what here problem. In C++ is impossible to
do overloading with "virtual" keyword?
Probably I badly know C ++, sorry.
February 09, 2009
On 2009-02-08 23:43:13 -0500, Weed <resume755@mail.ru> said:

> Michel Fortin пишет:
>> On 2009-02-08 09:30:08 -0500, Weed <resume755@mail.ru> said:
>> 
>>> Let's assume, polymorphism is necessary to these objects
>> 
>> Polymorphism doesn't work very well while passing objects by value, even
>> in C++. This is called the slicing problem.
>> <http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c>
>> 
>> 
>> I think D does a good job at avoiding that problem.
>> 
> 
> By performance loss
> :(

No. By forbiding the cases that leads to slicing, like returning a polymorphic object by value.

Returning something by-value always cause slicing in C++. Try this C++ code:

	#include <iostream>

	class A {
	public:
		virtual void hello() { std::cout << "hello from A" << std::endl; }
	};
	class B : public A {
	public:
		virtual void hello() { std::cout << "hello from B" << std::endl; }
	};

	A test() {
		B b;
		b.hello(); // prints "hello from B"
		return b;
	}
	int main() {
		test().hello(); // prints "hello from A"
		A a = test();
		a.hello();      // prints "hello from A"
	}

Here, returning B by value "slices" the object, transforming it into a A. There is absolutely no polymorphic behaviour when returning by value. It's as if virtual functions weren't virtual at all: your B object is transformed to a A when it is returned.

To preserve polymorphism, you need to return a pointer or a reference, but then test() can't allocate the object on the stack.

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

February 09, 2009
Michel Fortin пишет:
> On 2009-02-08 23:43:13 -0500, Weed <resume755@mail.ru> said:
> 
>> Michel Fortin пишет:
>>> On 2009-02-08 09:30:08 -0500, Weed <resume755@mail.ru> said:
>>>
>>>> Let's assume, polymorphism is necessary to these objects
>>>
>>> Polymorphism doesn't work very well while passing objects by value, even
>>> in C++. This is called the slicing problem.
>>> <http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c>
>>>
>>>
>>>
>>> I think D does a good job at avoiding that problem.
>>>
>>
>> By performance loss
>> :(
> 
> 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