www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - casts / wildcards for parametrized types

reply tx <tx lowtech-labs.org> writes:
Hi All,
Hoping somebody can provide some help here, I have a set of classes
that are similar to the following

class Item{}

class Box(T) : Item {
  T value;
  //  ...
}

//Along with some aliases:

alias Box!(long) LongBox;
alias Box!(char) CharBox;
//etc.



I'd like to have a function that operates on instances of Item, but I
want to specialize the behavior to handle instances of Box
differently. Is there anyway I can upcast Item to Box without
specifying the type parameter? In my case I don't really care about
what T is, just that I'm working with a Box of something. Currently I
have a whole if-else ladder checking to see if I'm working with an
instance of each of the aliases and casting appropriately, but I would
prefer to just write that once. I guess I'm looking for something like
cast(Box). Is this possible?

To put it another way, is there a D equivalent to the Java syntax of Box<?>


-tx

--
tx.lowtech-labs.org / tx lowtech-labs.org
Aug 01 2013
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 08/01/2013 04:28 PM, tx wrote:

 Hi All,
 Hoping somebody can provide some help here, I have a set of classes
 that are similar to the following

 class Item{}

 class Box(T) : Item {
    T value;
    //  ...
 }

 //Along with some aliases:

 alias Box!(long) LongBox;
 alias Box!(char) CharBox;
 //etc.



 I'd like to have a function that operates on instances of Item, but I
 want to specialize the behavior to handle instances of Box
 differently. Is there anyway I can upcast Item to Box without
 specifying the type parameter? In my case I don't really care about
 what T is, just that I'm working with a Box of something. Currently I
 have a whole if-else ladder checking to see if I'm working with an
 instance of each of the aliases and casting appropriately, but I would
 prefer to just write that once. I guess I'm looking for something like
 cast(Box). Is this possible?

 To put it another way, is there a D equivalent to the Java syntax of 
Box<?> Inserting another layer to the hierarchy is a solution. ConcreteBox is the same as your Box and the new Box in a non-template intermediate class: class Item{} class Box : Item{} class ConcreteBox(T) : Box { T value; // ... } alias ConcreteBox!(long) LongBox; alias ConcreteBox!(char) CharBox; //etc. // Explicit check int foo(Item item) { if (cast(Box)item) { return 1; } return 0; } unittest { assert(foo(new Item()) == 0); assert(foo(new LongBox()) == 1); assert(foo(new CharBox()) == 1); } // Automatic dispatch for compile-time binding class Foo { int foo(Item item) { return 0; } int foo(Box box) { return 1; } } unittest { auto f = new Foo(); assert(f.foo(new Item()) == 0); assert(f.foo(new LongBox()) == 1); assert(foo(new CharBox()) == 1); } void main() {} Ali
Aug 01 2013
next sibling parent reply "Meta" <jared771 gmail.com> writes:
One more way, not necessarily a workaround but a little trick 
that doesn't require you to know the exact type of box, is to use 
typeof(box).

auto box = Box!int();
auto item = cast(Item)box;
auto box2 = cast(typeof(box));
Aug 01 2013
parent "Meta" <jared771 gmail.com> writes:
On Friday, 2 August 2013 at 00:44:21 UTC, Meta wrote:
 One more way, not necessarily a workaround but a little trick 
 that doesn't require you to know the exact type of box, is to 
 use typeof(box).

 auto box = Box!int();
 auto item = cast(Item)box;
 auto box2 = cast(typeof(box));
Whoops, make that last line: auto box2 = cast(typeof(box))item;
Aug 01 2013
prev sibling parent reply tx <tx lowtech-labs.org> writes:
Thanks for the reply Ali. Everything you wrote makes sense, but I
think I may have oversimplified the problem in my original email. In
general I don't care about what T is when working with Box (or
ConcreteBox as it were), but I do care that Box contains a T. Consider
this rather contrived example (I'm aware that it's a poorly written
function, it just captures my issue.):

bool truthy(Item a, Item b){
  if(cast(LongBox) a && cast(LongBox) b){
    return (cast(LongBox) a).value && (cast(LongBox) a).value;
  } else if(cast(CharBox) a && cast(CharBox) b){
    return (cast(CharBox) a).value && (cast(CharBox) a).value;
  } else {
    return !(a is null || b is null);
  }
}

I would much rather write something like:

bool truthy(Item a, Item b){
  if(cast(Box) a && cast(Box) b){
    return (cast(Box) a).value && (cast(Box) a).value;
  } else {
    return !(a is null || b is null);
  }
}

To be more explicit: By the time I'm writing these if-else/cast
statements I already know that my objects are both instances of some
type of Box and I also know that the operation(s) I plan to perform on
them are valid for any T that box can contain.

-tx

--
tx.lowtech-labs.org / tx lowtech-labs.org


On Thu, Aug 1, 2013 at 5:06 PM, Ali =C3=87ehreli <acehreli yahoo.com> wrote=
:
 On 08/01/2013 04:28 PM, tx wrote:

 Hi All,
 Hoping somebody can provide some help here, I have a set of classes
 that are similar to the following

 class Item{}

 class Box(T) : Item {
    T value;
    //  ...
 }

 //Along with some aliases:

 alias Box!(long) LongBox;
 alias Box!(char) CharBox;
 //etc.



 I'd like to have a function that operates on instances of Item, but I
 want to specialize the behavior to handle instances of Box
 differently. Is there anyway I can upcast Item to Box without
 specifying the type parameter? In my case I don't really care about
 what T is, just that I'm working with a Box of something. Currently I
 have a whole if-else ladder checking to see if I'm working with an
 instance of each of the aliases and casting appropriately, but I would
 prefer to just write that once. I guess I'm looking for something like
 cast(Box). Is this possible?

 To put it another way, is there a D equivalent to the Java syntax of
 Box<?>
Inserting another layer to the hierarchy is a solution. ConcreteBox is th=
e
 same as your Box and the new Box in a non-template intermediate class:

 class Item{}

 class Box : Item{}

 class ConcreteBox(T) : Box {
   T value;
   //  ...
 }

 alias ConcreteBox!(long) LongBox;
 alias ConcreteBox!(char) CharBox;
 //etc.

 // Explicit check
 int foo(Item item)
 {
     if (cast(Box)item) {
         return 1;
     }

     return 0;
 }

 unittest
 {
     assert(foo(new Item()) =3D=3D 0);
     assert(foo(new LongBox()) =3D=3D 1);
     assert(foo(new CharBox()) =3D=3D 1);
 }

 // Automatic dispatch for compile-time binding
 class Foo
 {
     int foo(Item item)
     {
         return 0;
     }

     int foo(Box box)
     {
         return 1;
     }
 }

 unittest
 {
     auto f =3D new Foo();
     assert(f.foo(new Item()) =3D=3D 0);
     assert(f.foo(new LongBox()) =3D=3D 1);
     assert(foo(new CharBox()) =3D=3D 1);
 }

 void main()
 {}

 Ali
Aug 01 2013
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 08/01/2013 05:40 PM, tx wrote:

 Thanks for the reply Ali. Everything you wrote makes sense, but I
 think I may have oversimplified the problem in my original email. In
 general I don't care about what T is when working with Box (or
 ConcreteBox as it were), but I do care that Box contains a T. Consider
 this rather contrived example (I'm aware that it's a poorly written
 function, it just captures my issue.):

 bool truthy(Item a, Item b){
    if(cast(LongBox) a && cast(LongBox) b){
      return (cast(LongBox) a).value && (cast(LongBox) a).value;
    } else if(cast(CharBox) a && cast(CharBox) b){
      return (cast(CharBox) a).value && (cast(CharBox) a).value;
    } else {
      return !(a is null || b is null);
    }
 }

 I would much rather write something like:

 bool truthy(Item a, Item b){
    if(cast(Box) a && cast(Box) b){
      return (cast(Box) a).value && (cast(Box) a).value;
    } else {
      return !(a is null || b is null);
    }
 }

 To be more explicit: By the time I'm writing these if-else/cast
 statements I already know that my objects are both instances of some
 type of Box and I also know that the operation(s) I plan to perform on
 them are valid for any T that box can contain.
Pretty complicated semantics. Something must be wrong there. ;) Here is a solution that shares the solution between a member function and a non-member function. The member returns a three-value enum: class Item{} enum Truthy { nonzero_values, has_zero_value, mismatched_type } class Box(T) : Item { T value; // ... this(T value) { this.value = value; } Truthy truthy_(U)(Box!U b) { auto rhs = cast(Box!T)b; if (!rhs) { return Truthy.mismatched_type; } return value && rhs.value ? Truthy.nonzero_values : Truthy.has_zero_value; } } alias Box!(long) LongBox; alias Box!(char) CharBox; //etc. bool truthy(T0, T1)(Box!T0 lhs, Box!T1 rhs) { if (lhs !is null) { final switch (lhs.truthy_(rhs)) with (Truthy) { case mismatched_type: return rhs !is null; case has_zero_value: return false; case nonzero_values: return true; } } return false; } unittest { auto l0 = new LongBox(0); auto l1 = new LongBox(1); LongBox ln = null; auto c0 = new CharBox(0); auto c1 = new CharBox('a'); CharBox cn = null; // Same types assert(!truthy(l0, l0)); assert(!truthy(l0, l1)); assert( truthy(l1, l1)); assert(!truthy(ln, l0)); assert(!truthy(l1, ln)); assert(!truthy(ln, ln)); assert(!truthy(c0, c0)); assert(!truthy(c0, c1)); assert( truthy(c1, c1)); assert(!truthy(cn, c0)); assert(!truthy(c1, cn)); assert(!truthy(cn, cn)); // Mixed types assert( truthy(c0, l1)); // mismatched but both are non-null assert( truthy(l0, c1)); assert( truthy(c1, l1)); assert( truthy(l1, c1)); assert(!truthy(cn, l0)); // mismatched but one is null assert(!truthy(c1, ln)); } void main() {} Ali
Aug 01 2013