contiguous ranges

Pasqui23 via Digitalmars-d digitalmars-d at puremagic.com
Wed Feb 18 08:10:28 PST 2015


On Wednesday, 18 February 2015 at 08:34:49 UTC, Vlad Levenfeld 
wrote:
>
> I would argue that the only operations which preserve 
> contiguity are slicing, concatenating and appending; r.retro, 
> r.stride, r.map!f, etc should yield a RandomAccessRange.
>
> I don't think this is a deal breaker, as conceptually its akin 
> to losing random-accessiblity under a filtering or grouping. 
> Input ranges are ubiquitous, RandomAccessRanges are more rare, 
> and ContiguousRanges are rarer still. It stands to reason that 
> contiguity is unlikely to be preserved under a given 
> transformation.

No.The main strenght of the range api is its ability to preserve 
range categories.The loss of range categories is a *price* we pay 
for lazy  evalutation,categories wich can be restored by .array 
in exchange for the usual price for dynamic allocation.

>> This needs, however, a few more implementations that
>> motivate the concept.
>
> The main use cases I had in mind was for optimized data 
> transfers and passing arguments to C APIs, and in this regard 
> the definition of ContiguousRange would need a bit of 
> refinement, maybe like so:
>
>   A ContiguousRange r of type R is a RandomAccessRange which 
> satisfies hasLValueElements and defines a member called ptr 
> which satisfies the following conditions:
>
>     1) *r.ptr == r[0] && r.ptr == &r[0]
>     2) for all 0 <= i < r.length, *(r.ptr + i) == r[i] && r.ptr 
> + i == &r[i]
>
> We could then have:
>
>   void load_data (R)(R r) {
>     static if (isContiguousRange!R)
>       auto ptr = r.ptr;
>     else {
>       auto cache = r.array;
>       auto ptr = cache.ptr;
>     }
>
>     some_C_lib_call (r.length, ptr);
>   }
>
>   and
>
>   void copy (R,S)(R r, S s) if (allSatisfy!(isContiguousRange, 
> R, S)) {
>     // type and length equality assertions
>     vectorized_blit (ElementType!R.sizeof, r.length r.ptr, 
> s.ptr);
>   }
>
>  ---

You are on track when you say the main use case would be 
optimized data transfer and comunication with C.However you are 
describing ContiguousRange as
a type implicitly convertable to T[],wich I deem too 
restrictive.Optimized data transfers don't care at all that the 
bytes are in order,only that the data are in the slice you gave 
them.

Instead a contiguous range is one wich satisfies the following  
contract:

R r;
static assert(hasLValueElements!R && !isInfinite!R);
void[]s=r.toContiguous;
foreach(ref i;r)assert(s.ptr<=&i<s.ptr+s.lenght);

This allow,for example,to say:

void copy(R)(R r1,R r2)if(isContiguousRange!R){
   void[]s1=r1.toContiguous,s2=r2.toContiguous;
   memcpy(s1.ptr,s2.ptr,min(s1.lenght,s2.lenght);
}

It even give us a contiguous BinaryHeap,simply by

struct BinaryHeap(Store){
   ...
   private Store store;
   @propriety void[]toContiguous()if(isContiguousRange!Store){
     return store.toContiguous;
   }
}

and this BinaryHeap will be copied using only 1 call to memcpy.

> Extending the concept to multiple dimensions is thorny, but 
> then, I've found that the same is true for everything but 
> RandomAccessRanges.

My proposal allow simple extension to multiple dimension.
if  r is a n-dimensional range,then then the following must hold 
true:

void[]s=r.toContiguous;
foreach(i1;r)foreach(i2;i1)...foreach(in;inprev)
   assert(s.ptr<=&in<s.ptr+s.lenght);

On Tuesday, 17 February 2015 at 15:50:17 UTC, Andrei Alexandrescu 
wrote:
> for an array r, is r.retro contiguous or not?

I would say yes.In general,a good rule of thumb would be:this 
range can be copied via memcpy?Then preserve contiguous-ness.

For funcion accepting ranges ia a bit more complicated:
If the function cares that r[i]==*(s+i) then it should accept 
only T[]
else it should accept contiguous ranges.


More information about the Digitalmars-d mailing list