www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - D is about to take a wrong turn with interface identities

reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
The [spec on identity 
expressions](https://dlang.org/spec/expression.html#identity_expressions) was
recently [changed](https://github.com/dlang/dlang.org/pull/3726) to:
 For class / interface objects, identity is defined as the 
 object references being identical. Null class objects can be 
 compared with `is`. Note that inferface objects need not have 
 the same reference of the class they were cast from. To test 
 whether an `interface` shares a class instance with another 
 `interface` / `class` value, cast both operands to `Object` 
 before comparing with `is`.
My sense is that this behavior is surprising and therefore unacceptable. Even the route of simply casting to `void*` is unacceptable as it will surprise C++ users who know that `dynamic_cast<void*>`, given a pointer to a base-type subobject, returns a `void*` to the actual (most-derived) object; that way, two seemingly unrelated class-type objects can be compared for identity without knowing their most-derived type. D doesn’t need that, given that `Object` is a known base class for all classes, a cast to `Object` achieves the same. If anything, D’s `cast(void*)` should do the same as it does in C++, or be invalid. As was [pointed out to me](https://forum.dlang.org/post/gitvtdwiygpuvwduomfc forum.dlang.org), a type paint should be done as per `*cast(void**)(&obj)`. DRuntime or Phobos could provide this via a convenience function `reinterpretAsVoidPointer`. If interface identity cannot be done fast enough and correctly, and the language designers don’t want to provide a slow and correct solution as an operator, whatever their rationale may be, they should not provide an incorrect solution, but rather no solution at all, that is, make using the identity operator with one or two interface type values an error. The error message should tell people to use a `cast(Object)`; for template code, if a value is a class or interface, note that `cast(Object)` is a no-op on class values. None of this addresses the problem that the `cast(Object)` cannot work for `extern(C++)` classes, even if C++-RTTI was available, which it hopefully will in the future. A solution would be to do what C++ does: Make `cast(void*)` bypass a potential `opCast` (akin to how class-type assignment bypasses `opAssign`) to return a pointer to the most-derived object the reference refers to. Alternatively, a DRuntime function `dynamicCastVoidPointer` could provide this. Make interfaces easy to use correctly and hard to use incorrectly. – Scott Mayers
Nov 20 2023
next sibling parent reply Adam D Ruppe <destructionator gmail.com> writes:
On Monday, 20 November 2023 at 16:41:46 UTC, Quirin Schroll wrote:
 My sense is that this behavior is surprising and therefore 
 unacceptable.
It has been this way for twenty years. Has it bothered you before now?
Nov 20 2023
parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Monday, 20 November 2023 at 18:39:44 UTC, Adam D Ruppe wrote:
 On Monday, 20 November 2023 at 16:41:46 UTC, Quirin Schroll 
 wrote:
 My sense is that this behavior is surprising and therefore 
 unacceptable.
It has been this way for twenty years.
But it was a bug!
 Has it bothered you before now?
My personal experience with it doesn’t really matter here, but yes, I ran into this.
Nov 27 2023
prev sibling parent reply Kagamin <spam here.lot> writes:
It's not obvious why `cast(void*)` should cast to Object. Any 
application that requires accurate pointers would fail horribly.
Nov 27 2023
parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Monday, 27 November 2023 at 12:08:51 UTC, Kagamin wrote:
 It's not obvious why `cast(void*)` should cast to Object. Any 
 application that requires accurate pointers would fail horribly.
Well, C++ does it, basically. D currently doesn’t support C++ RTTI, but it should not make decisions that will make it impossible to do so. For an `extern(C++)` interface, how else would you test for identity? As has been pointed out to me, “accurate pointers,” i.e. type paint, cannot be generically expected to work using `cast(void*)` because of `opCast`. Use: ```d R reinterpretCast(R, T)(auto ref T t) { *cast(R*)cast(void**)&t } ``` If anything, `cast(void*)` should do the exact same thing as [`dynamic_cast<void*>`](https://en.cppreference.com/w/cpp/la guage/dynamic_cast) does in C++, at least for `extern(C++)` classes, and if we want to be consistent – and there’s no reason not to –, it should do the same also for `extern(D)` classes.
Dec 12 2023