Thread overview
casts / wildcards for parametrized types
Aug 01, 2013
tx
Aug 02, 2013
Ali Çehreli
Aug 02, 2013
Meta
Aug 02, 2013
Meta
Aug 02, 2013
tx
Aug 02, 2013
Ali Çehreli
August 01, 2013
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
August 02, 2013
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

August 02, 2013
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));

August 02, 2013
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;
August 02, 2013
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 Çehreli <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 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
>
August 02, 2013
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