Thread overview
August 10

A common pattern when working with inheriting classes or classes that implement an interface is to use the base class or interface as a generic type:

interface Thing{}
class OtherThing: Thing{}

Thing[] array = [new OtherThing(), …];

So how do people usually check if an item in array is a sub-class of Thing, or implements an interface that inherited from Thing? Is typeid the only way to do this?

August 10
On 10/8/23 7:59, IchorDev wrote:
> A common pattern when working with inheriting classes or classes that implement an interface is to use the base class or interface as a generic type:
> ```
> interface Thing{}
> class OtherThing: Thing{}
> 
> Thing[] array = [new OtherThing(), …];
> ```
> So how do people usually check if an item in `array` is a sub-class of `Thing`, or implements an interface that inherited from `Thing`? Is `typeid` the only way to do this?

I faced this issue recently. In my case, and I think it's the most common case, I didn't care what exact type it was, I just wanted to make sure it was `OtherThing`. If it wasn't, the exact type didn't really matter.

If that's also your case, you can just cast to the type you need, and check for `null`. My case was like this:

```d
interface Thing{
	void foo(Thing[] array);
}

class OtherThing: Thing{
	override void foo(Thing[] array) {
		foreach (thing; array) {
			auto otherThing = cast (OtherThing) thing;
			assert(otherThing, "Wrong type in `array`: " ~ typeid(thing).toString);
			// Do something here
		}
	}
}
```
August 10

On 8/10/23 1:59 AM, IchorDev wrote:

>

A common pattern when working with inheriting classes or classes that implement an interface is to use the base class or interface as a generic type:

interface Thing{}
class OtherThing: Thing{}

Thing[] array = [new OtherThing(), …];

So how do people usually check if an item in array is a sub-class of Thing, or implements an interface that inherited from Thing? Is typeid the only way to do this?

The correct way is to use a cast. Though, that can be overridden.

A very nice pattern in D is:

if(auto x = cast(DerivedTypeOrInterface)y) {
   // use x
}

This also can be dangerous if e.g. y in the future becomes const.

This is one place where pattern matching would be very useful. In fact, we already do it for try/catch, I don't see why we can't do it in general.

-Steve

August 11

On Thursday, 10 August 2023 at 15:25:41 UTC, Steven Schveighoffer wrote:

>

The correct way is to use a cast. Though, that can be overridden.

A very nice pattern in D is:

if(auto x = cast(DerivedTypeOrInterface)y) {
   // use x
}

This also can be dangerous if e.g. y in the future becomes const.

To avoid both the opCast and the casting away const problem, perhaps std.conv should have:

D derived(D, B : Object)(B base)
if (is(D : B) && !__traits(compiles, base.opCast!D))
{
    return cast(D)base;
}

alias E = Exception;
void main()
{
    Object o = new E("hi");
    E c = o.derived!E;
    assert(c.msg == "hi");
}

There's probably some way to statically enforce that B has extern(D) linkage, derived could enforce that too. (extern(C++) classes don't have RTTI).

August 12

On Thursday, 10 August 2023 at 15:25:41 UTC, Steven Schveighoffer wrote:

>

This also can be dangerous if e.g. y in the future becomes const.

But what if y is const, or even immutable? How is it dangerous?

August 12

On Saturday, 12 August 2023 at 09:32:14 UTC, IchorDev wrote:

>

On Thursday, 10 August 2023 at 15:25:41 UTC, Steven Schveighoffer wrote:

>

This also can be dangerous if e.g. y in the future becomes const.

But what if y is const, or even immutable? How is it dangerous?

Because the cast removes the type qualifier, so could allow mutation of immutable data. In @safe code I expect that situation would error.

August 12

On Thursday, 10 August 2023 at 15:25:41 UTC, Steven Schveighoffer wrote:

>

A very nice pattern in D is:

if(auto x = cast(DerivedTypeOrInterface)y) {
   // use x
}

This also can be dangerous if e.g. y in the future becomes const.

I hate this. Why don't we have an "unqual cast" (a cast that changes the type but leave all qualifiers unchanged)?
That should not be complicated to implement, but would be useful very often.

August 12
This should work:

```d
import std.traits : CopyTypeQualifiers;

if(auto x = cast(CopyTypeQualifiers!(typeof(y), DerivedTypeOrInterface))y) {
   // use x
}
```