| Thread overview | ||||||||
|---|---|---|---|---|---|---|---|---|
|
August 01, 2013 casts / wildcards for parametrized types | ||||
|---|---|---|---|---|
| ||||
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 Re: casts / wildcards for parametrized types | ||||
|---|---|---|---|---|
| ||||
Posted in reply to tx | 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 Re: casts / wildcards for parametrized types | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | 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 Re: casts / wildcards for parametrized types | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Meta | 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 Re: casts / wildcards for parametrized types | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | 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 Re: casts / wildcards for parametrized types | ||||
|---|---|---|---|---|
| ||||
Posted in reply to tx | 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
| |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply