October 20, 2021
On Wed, Oct 20, 2021 at 05:28:30PM +0000, Guillaume Piolat via Digitalmars-d wrote: [...]
> One of the problem C++ has with objects-with-inheritance-as-value-type is "object slicing".
> 
> It is ugly in theory: https://stackoverflow.com/a/14461532
> 
> I guess it's one of the reason D doesn't have that, so a discussion about Object* needs to address that issue.

Yes, I believe the original reason D treats classes as inherently by-reference is precisely because of this. Consider:

	class A {
		int x;
		virtual int getValue() { return x; }
	}
	class B : A {
		int y;
		override int getValue() { return y; }
	}

	B b;
	A a = b; // allowed since B is a subtype of A.
	writeln(a.getValue()); // uh oh

Assume by-value semantics for the sake of argument.  So `a` gets a copy of part of B, because there is only enough space allocated on the stack to store an A but not a B. So `y` is missing.  Then what should a.getValue return?  Since it is virtual, it should call B.getValue, which accesses a non-existent member, or in more practical terms, it reads from the stack outside of the bounds of A.

All sorts of subtle (and nasty) problems crop up when you mix polymorphic class objects with by-value semantics.  That's why D's design makes more sense: if you want OO-style inheritance, let it be by-reference all the way. If you want by-value semantics, which is incompatible with OO-style polymorphism, use a struct.  Simple solution, avoids all the nastiness you have to deal with in C++.


T

-- 
Never step over a puddle, always step around it. Chances are that whatever made it is still dripping.
October 20, 2021

On Wednesday, 20 October 2021 at 17:27:45 UTC, jmh530 wrote:

>

What is the benefit of different pointer types?

If you have a function that takes a pointer but doesn't make any allocations or anything, then you would need to have two versions of it to work with each type. This introduces a similar issue as what led to inout. It might make some kind of generics with type erasure that can handle all of these cases more useful.

Correct, that is the drawback with different pointer types, that you might need two implementations, one for pointers and one for reference types. Normally generics takes care of that pretty well.

The benefit is that references/fat-pointers enables operations under the hood that raw pointers don't allow. For example increasing/decreasing a reference count. If everything in Phobos/Druntime would use references instead of raw pointers where applicable, the a recompile would have enabled a complete change of the GC algorithm. Reference types enables more versatility.

October 20, 2021
On Wednesday, 20 October 2021 at 17:28:30 UTC, Guillaume Piolat wrote:
> On Wednesday, 20 October 2021 at 11:41:32 UTC, Adam D Ruppe wrote:
>>
>> I don't really think it is worth the hassle but it is interesting in that it solves some old syntax worries.
>
> One of the problem C++ has with objects-with-inheritance-as-value-type is "object slicing".
>
> It is ugly in theory: https://stackoverflow.com/a/14461532

Never had any issues with this in real life, this is one of many things you should think about when modelling.

The real "problem" with C++ is that it both provides a lot of flexibility-for-responsible-designers, but also provides defaults. Since people don't write the default it is easier to forget to adjust the semantics of all member functions to the model.


October 20, 2021

On Wednesday, 20 October 2021 at 17:55:05 UTC, IGotD- wrote:

>

Correct, that is the drawback with different pointer types, that you might need two implementations, one for pointers and one for reference types. Normally generics takes care of that pretty well.

The benefit is that references/fat-pointers enables operations under the hood that raw pointers don't allow. For example increasing/decreasing a reference count. If everything in Phobos/Druntime would use references instead of raw pointers where applicable, the a recompile would have enabled a complete change of the GC algorithm. Reference types enables more versatility.

The more I think of it, this solution is becoming more and more attractive.

https://forum.dlang.org/post/bsmgpfdlylvpuojpkdgz@forum.dlang.org

I know that a reference type will never make it into D. However, the solution proposed offers an escape hatch without changing the core language. Managed memory is the norm and unmanaged memory will likely be used sparsely in most programs. There will be an unnecessary performance hit when using unmanaged memory (which is still managed but operations ignored at runtime) but it can be overridden (for example parameter passing) by a keyword for those very optimized situations which is a non breaking change.

October 20, 2021

On Wednesday, 20 October 2021 at 17:55:05 UTC, IGotD- wrote:

> >

[snip]

Correct, that is the drawback with different pointer types, that you might need two implementations, one for pointers and one for reference types. Normally generics takes care of that pretty well.

The benefit is that references/fat-pointers enables operations under the hood that raw pointers don't allow. For example increasing/decreasing a reference count. If everything in Phobos/Druntime would use references instead of raw pointers where applicable, the a recompile would have enabled a complete change of the GC algorithm. Reference types enables more versatility.

My limited knowledge of Managed C++ is that it only allowed the fat pointers with the GC. Would it be possible to have it customizable so that for instance you could have T^ work with a garbage collector or switch it out for reference counting?

October 20, 2021

On Wednesday, 20 October 2021 at 18:19:55 UTC, jmh530 wrote:

>

My limited knowledge of Managed C++ is that it only allowed the fat pointers with the GC. Would it be possible to have it customizable so that for instance you could have T^ work with a garbage collector or switch it out for reference counting?

Yes, the key is to have a distinctive type for managed pointers. When you have that you can implement that pointer as you want. Nim has a distinctive type for managed memory and can change GC type by recompiling.

October 20, 2021

On Wednesday, 20 October 2021 at 09:47:54 UTC, SealabJaster wrote:

>

If you could make any changes to D, what would they look like?

I want some non-ridiculous way to use +=, -=, *=, etc with property functions. Whether that's with @property or otherwise. And I want https://issues.dlang.org/show_bug.cgi?id=21321 (Class with unimplemented interface method compiles, links, then segfaults, if inherited through abstract base class) fixed.

October 20, 2021

Like many people here I tried to design my own language, but I noticed that I can't get (logically) puristic concepts into an adequate form. I grew suspicious it's because the problem domain itself is impure, so puristic concepts can't be adequate, and an adequate language should elegantly incorporate impurity instead of evading it at all wasteful costs. I also take this approach in program design, somehow a line is easy to see that the component shouldn't be more pure than this.

October 20, 2021
On 10/20/21 11:02 AM, Ola Fosheim Grøstad wrote:
> On Wednesday, 20 October 2021 at 17:28:30 UTC, Guillaume Piolat wrote:

>> One of the problem C++ has with objects-with-inheritance-as-value-type
>> is "object slicing".
>>
>> It is ugly in theory: https://stackoverflow.com/a/14461532
>
> Never had any issues with this in real life,

Me neither because I was following convention in C++: "Pass inherited types by reference because there is slicing." That rule renders C++ classes (and structs) of inheritance hierarchies "reference types". I like D's (and Java's and C#'s) approach here.

Ali


October 20, 2021
On Wednesday, 20 October 2021 at 15:58:49 UTC, Adam D Ruppe wrote:
> On Wednesday, 20 October 2021 at 15:53:53 UTC, H. S. Teoh wrote:
>> Object* *is* the pointer.  There's nothing going on behind the scenes.
>
> well tho does obj++; move the pointer or call the operator on the class?
>
> I don't think this syntax is ideal (I kinda prefer the old `Object ref` proposal), but just it is something interesting to think about.

FWIW the 'non-nullable & explicitly nullable pointers' proposal resolves this.  Because the point of such pointers is that they always point at exactly one object (or at nothing), there is no point in indexing or adding anything to them etc., so such overloaded operators can be forwarded.  (As x.y -> (*x).y, so x.opUnary!"++" -> (*x).upUnary!"++".)

However I just realised unary * can be overloaded.  Obviously *x should be a dereference.  But as that is a fairly fringe use case (and the point of the exercise was to ignore compatibility anyway), I think it can be safely steamrolled.