shared arrray problem

Charles Hixson via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sun Nov 20 12:09:39 PST 2016


On 11/20/2016 03:42 AM, ag0aep6g via Digitalmars-d-learn wrote:
> On 11/20/2016 04:34 AM, Charles Hixson via Digitalmars-d-learn wrote:
>> Whether you would call the change "break things for your code" might be
>> dubious.  It would be effectively broken, even if technically my code
>> was doing the correct thing.  But my code wouldn't be storing the data
>> that needed storing, so effectively it would be broken.
>
> I don't see how it's dubious. It's an error by the user. When users 
> are given a dynamic array (and not by reference), they cannot expect 
> that your code sees changes to length. That's just not how arrays 
> work. When a user has that wrong expectation, and writes wrong code 
> because of it, then it's arguably their own fault. However, if you 
> want you can hold their hand a bit and make the mistake less likely.
>
>> "Write something
>> for yourself" is what I'd like to do, given that the language doesn't
>> have that built-in support, but I can't see how to do it.
>
> Wrap the array in a struct that has indexing, but doesn't allow 
> setting the length or appending. Here's a quick prototype:
>
> ----
> struct ConstLengthArray(E)
> {
>     private E[] data;
>     this(E[] arr) { this.data = arr; }
>     ref inout(E) opIndex(size_t i) inout { return data[i]; }
>     @property size_t length() const { return data.length; }
> }
>
> void main()
> {
>     auto cla = ConstLengthArray!ubyte([1, 2, 3, 4, 5]);
>
>     /* Mutating elements is allowed: */
>     cla[0] = 10;
>     assert(cla[0] == 10);
>
>     /* No setting length, no appending: */
>     static assert(!__traits(compiles, cla.length = 3));
>     static assert(!__traits(compiles, cla ~= 6));
> }
> ----
>
> You might want to add support for slicing, concatenation, etc. Maybe 
> allow implicit conversion to const(E[]), though that would also allow 
> conversion to const(E)[] and that has a settable length again
Thinking it over a bit more, the item returned would need to be a 
struct, but the struct wouldn't contain the array, it would just contain 
a reference to the array and a start and end offset.  The array would 
need to live somewhere else, in the class (or struct...but class is 
better as you don't want the array evaporating by accident) that created 
the returned value.  This means you are dealing with multiple levels of 
indirection, so it's costly compared to array access, but cheap compared 
to lots of large copies.  So the returned value would be something like:
struct
{
     private:
     /** this is a reference to the data that lives elsewhere.  It 
should be a pointer, but I don't like the syntax*/
     ubyte[]  data;
     int    start, end;    ///    first and last valid indicies into data
     public:
     this (ubyte[] data, int start, int end)
     {    this.data = data; this.start = start; this.end = end;}
     ...
     // various routines to access the data, but to limit the access to 
the spec'd range, and
     // nothing to change the bounds
}
Which is really the answer you already posted, but just a bit more 
detail on the construct, and what it meant.  (Yeah, I could allow types 
other than ubyte as the base case, but I don't want to.  I'm thinking of 
this mainly as a means of sharing a buffer between applications where 
different parts have exclusive access to different parts of the buffer, 
and where the buffer will be written to a file with a single fwrite, or 
since the underlying storage will be an array, it could even be 
rawwrite).  I don't want to specify any more than I must about how the 
methods calling this will format the storage, and this means that those 
with access to different parts may well use different collections of 
types, but all types eventually map down to ubytes (or bytes), so ubytes 
is the common ground.  Perhaps I'll need to write inbuffer,outbuffer 
methods/wrappings, but that's far in the future.

P.S.:  The traits that I mentioned previously were those given by:
     static assert(!__traits(compiles, cla.length = 3));
     static assert(!__traits(compiles, cla ~= 6));
in your main routine.  I assumed that they were validity tests.  I don't 
understand why they were static.  I've never happened to use static 
asserts, but I would assume that when they ran cla wouldn't be defined.

N.B.:  Even this much is just thinking about design, not something I'll 
actually do at the moment.  But this is a problem I keep coming up 
against, so a bit of thought now seemed a good idea.


More information about the Digitalmars-d-learn mailing list