Thread overview
How to check if object is an instance of generic class?
May 03, 2017
Nothing
May 03, 2017
H. S. Teoh
May 03, 2017
Nothing
May 03, 2017
ag0aep6g
May 03, 2017
H. S. Teoh
May 03, 2017
ag0aep6g
May 03, 2017
H. S. Teoh
May 03, 2017
Hi, Honestly I am new to D and templates system so excuse me if my question will seem trivial.

I want to develop a generic Box(T) class that can be either empty or hold a value of arbitrary type T.

//____________________
class Box(T)
{
    override bool opEquals(Object o)
    {
        //...
        return false;
    }

private:
    T t;
    bool empty;
public:
    this(T t)
    {
        this.t = t;
        empty = false;
    }

    this()
    {
        empty = true;
    }

    bool isEmpty()
    {
        return empty;
    }

    void set(T t)
    {
        this.t = t;
        empty = false;
    }
}

void main()
{
    Box!int b1 = new Box!int(1);
    Box!int b2 = new Box!int(2);
}
//_________________________


Equality checking is where I stuck. It should work as follows:
0. If we compare the Box [b]b[/b] to an object [b]o[/b] that is not an instance of Box, it should return false.
1. Empty boxes are equal no matter the type.
2. If type of payload for two boxes they're not equal.
3. If both boxes hold values of same types their payload is compared and it is the final result.


Box!string() == Box!bool() -> true
Box!int(1) == Box!Int(1) -> true
Box!int(1) == Box!Int(2) -> false

So is there an idiomatic approach to know if the Object is an instance of Box (regardless of content type T) and than if necessary to know exactly if two boxes have same concrete type T?

Thanks.
May 03, 2017
On Wed, May 03, 2017 at 05:26:27PM +0000, Nothing via Digitalmars-d-learn wrote:
> Hi, Honestly I am new to D and templates system so excuse me if my question will seem trivial.
> 
> I want to develop a generic Box(T) class that can be either empty or hold a value of arbitrary type T.

Have a look at std.variant.Variant and std.typecons.Nullable. The combination of these two may already do what you want.

But of course, if you wish to write your own Box type, then to answer your question:

[...]
> So is there an idiomatic approach to know if the Object is an instance of Box (regardless of content type T) and than if necessary to know exactly if two boxes have same concrete type T?

If the types of the Boxes are known at compile-time, you could make opEquals a template, like this:

	class Box(T) {
		T t;

		bool opEquals(U)(U u)
		{
			static if (is(U == Box!V, V)) {
				if (is(V == T))
					return t == u.t; // Has the same content type
				else
					return false; // Has different content types
			} else {
				return false; // not a Box!T instantiation
			}
		}
		...
	}

However, this requires that the types of the incoming objects are known beforehand. If you're using runtime polymorphism and don't know the concrete types of the incoming Boxes beforehand, you could do this:

	class Box(T) {
		T t;

		bool opEquals(Object o)
		{
			auto p = cast(typeof(this)) o;
			if (p is null) {
				// This means either o is not a Box
				// type, or it has a different content
				// type: Box!A and Box!B are considered
				// to be distinct types at runtime in
				// spite of their common origin in the
				// same template.
				return false;
			}
			// Here, p !is null meaning that o must be an
			// instance of Box!T with the same T as this
			// object. So you could just compare them
			// directly.
			return t == p.t;
		}
	}


T

-- 
Век живи - век учись. А дураком помрёшь.
May 03, 2017
On 05/03/2017 07:26 PM, Nothing wrote:
> Equality checking is where I stuck. It should work as follows:
> 0. If we compare the Box [b]b[/b] to an object [b]o[/b] that is not an
> instance of Box, it should return false.
> 1. Empty boxes are equal no matter the type.
> 2. If type of payload for two boxes they're not equal.
> 3. If both boxes hold values of same types their payload is compared and
> it is the final result.
>
>
> Box!string() == Box!bool() -> true
> Box!int(1) == Box!Int(1) -> true
> Box!int(1) == Box!Int(2) -> false
>
> So is there an idiomatic approach to know if the Object is an instance
> of Box (regardless of content type T) and than if necessary to know
> exactly if two boxes have same concrete type T?

No. You can check if a type is an instance of the Box template, but that doesn't help you here because you're dealing with `Object`. From there you can only get back to Box!Foo when you know Foo.

You can add a common non-templated interface for all different Box types.

----
interface BoxI
{
    bool isEmpty();
}
class Box(T) : BoxI
{
    /* ... */
}
----

Now you can cast to BoxI, and call isEmpty. Which is all you need to do your steps 0 and 1. For steps 2 and 3, you cast to the Box type at hand, i.e. `Box!T` or just `Box`.

May 03, 2017
On Wed, May 03, 2017 at 08:04:20PM +0200, ag0aep6g via Digitalmars-d-learn wrote:
> On 05/03/2017 07:26 PM, Nothing wrote:
[...]
> > So is there an idiomatic approach to know if the Object is an instance of Box (regardless of content type T) and than if necessary to know exactly if two boxes have same concrete type T?
> 
> No. You can check if a type is an instance of the Box template, but that doesn't help you here because you're dealing with `Object`. From there you can only get back to Box!Foo when you know Foo.

For opEquals, having an Object is sufficient. You just cast the Object into the current type (Box!T for some concrete T), and if it's null, then either it's not a Box type, or it's Box!U where U != T.  Presumably if U != T then opEquals should just return false, so that's no problem. If U == T, then you now have a reference with the right type that you can use to compare the contents.

You only need a common interface if you wish to do something more with Box!X instantiations that's common across all Boxes.


T

-- 
Meat: euphemism for dead animal. -- Flora
May 03, 2017
On 05/03/2017 08:04 PM, H. S. Teoh via Digitalmars-d-learn wrote:
> You only need a common interface if you wish to do something more with
> Box!X instantiations that's common across all Boxes.

The goal is to return `true` for two empty boxes with different payload types. From the OP: "Empty boxes are equal no matter the type."
May 03, 2017
On Wed, May 03, 2017 at 08:24:31PM +0200, ag0aep6g via Digitalmars-d-learn wrote:
> On 05/03/2017 08:04 PM, H. S. Teoh via Digitalmars-d-learn wrote:
> > You only need a common interface if you wish to do something more with Box!X instantiations that's common across all Boxes.
> 
> The goal is to return `true` for two empty boxes with different payload types. From the OP: "Empty boxes are equal no matter the type."

Ah, I missed that part. In that case, yes, you'll need an interface with an isEmpty() method as you described.


T

-- 
Живёшь только однажды.
May 03, 2017
On Wednesday, 3 May 2017 at 17:54:13 UTC, H. S. Teoh wrote:
> On Wed, May 03, 2017 at 05:26:27PM +0000, Nothing via Digitalmars-d-learn wrote:
>> Hi, Honestly I am new to D and templates system so excuse me
> But of course, if you wish to write your own Box type, then to answer your question:
>
> [...]
>> So is there an idiomatic approach to know if the Object is an instance of Box (regardless of content type T) and than if necessary to know exactly if two boxes have same concrete type T?
>
> If the types of the Boxes are known at compile-time, you could make opEquals a template, like this:
>
> 	class Box(T) {
> 		T t;
>
> 		bool opEquals(U)(U u)
> 		{
> 			static if (is(U == Box!V, V)) {
> 				if (is(V == T))
> 					return t == u.t; // Has the same content type
> 				else
> 					return false; // Has different content types
> 			} else {
> 				return false; // not a Box!T instantiation
> 			}
> 		}
> 		...
> 	}
>
Thx for your input.
Yes the types are known at compile-time.
However I tried something like your suggestion and it doesn't seem to work. I tried adding a writeln like this:
> writeln("entering opEquals");
At the start of opEquals's body and apparently when I use b1 == b2 it is not invoked.