Issue with forward ranges which are reference types

Steven Schveighoffer schveiguy at yahoo.com
Wed Aug 17 07:14:16 PDT 2011


On Wed, 17 Aug 2011 02:20:14 -0400, Mehrdad <wfunction at hotmail.com> wrote:

> On 8/16/2011 9:37 PM, Jesse Phillips wrote:
>> On Tue, 16 Aug 2011 21:17:31 -0700, Mehrdad wrote:
>>
>>> On 8/16/2011 9:05 PM, Jonathan M Davis wrote:
>>>> Sorry that this is long, but it's very important IMHO, and I don't  
>>>> know
>>>> how to make it much shorter and cover what it's supposed to cover.
>>>>
>>>> Okay. Your typical forward range is either an array a struct which is  
>>>> a
>>>> value type (that is, copying it creates an independent range which
>>>> points to the same elements and is not altered if the original range  
>>>> is
>>>> altered - the elements that it points to aren't copied of
>>>> course).<snip>  Thoughts?
>>>>
>>>> - Jonathan M Davis
>>> Funny, I was also thinking about this recently.
>>>
>>> The trouble is that that's not the only issue. There's also the issue
>>> with polymorphism -- i.e., InputRangeObject is pretty much *useless*
>>> right now because no function ever checks for it (AFAIK... am I  
>>> wrong?).
>>> So if you pass a random-access range object as an InputRange, the  
>>> callee
>>> will just assume it's an InputRange and would reject it. So you're
>>> forced to downcast every time, which is really tedious. Things don't
>>> "just work" anymore.
>> All of the range functions check for functionality, so if your random-
>> access range object contains, popFront, front, empty (which it is
>> required to to be random-access range) then it will be accepted as an
>> InputRange.
> Right, but the problem is that none of this template business (e.g.  
> isInputRange!T, hasLength!T, etc.) works if the input is an Object that  
> implements InputRange.
>
> For example, consider this:
>
>      static Object getItems()
>      { return inputRangeObject([1, 2]); }
>
>      Object collection = getItems();
>      if (collection.empty)  //Whoops...
>      {
>          ...
>      }
>
> The caller has no idea what kind of range is returned by getItems(), but  
> he still needs to be able to check whether it's empty.

What you are looking for is dynamic typing.  That is not supported  
directly by Object.  That is, you have to know *statically* (i.e. at  
compile time) that *all* instances returned by getItems have an empty  
property.  Object does not have that property, so you used the wrong  
return type.

> How can he figure this out? He would be forced to cast (which is by  
> itself a pretty bad option), but what can he cast the object to?   
> InputRange!Object doesn't work because it could be an InputRange!string  
> or something. There's really NO way (that I know of) for the caller to  
> test and see if the collection is an input range, unless he knows the  
> type -- which runs counter to what I've seen in other OOP languages (C#,  
> Java).

Casting is actually the correct solution.  A cast from a based to a  
derived class is not unsafe as long as you forward the type modifiers  
(like const):

if(auto irange = cast(InputRangeObject)collection)
{
    // now you can use irange
    if(collection.empty) // success!
    {
       ...
    }
}

BTW, since input range is the lowest level range, I'd recommend getItems  
return InputRangeObject instead of Object.

Further more, since I'm 100% against class-based ranges, I would recommend  
not using them at all :)  Use a struct instead, or don't use the range  
concept here.

-Steve


More information about the Digitalmars-d mailing list