Introducing alid

Ali Çehreli acehreli at yahoo.com
Tue Sep 13 15:24:20 UTC 2022


On 9/12/22 22:24, Salih Dincer wrote:

 > `source.CachedRange!(ElementCache!(Result)).CachedRange.front` is not
 > callable using a `const` object

This exposes at least two issues:

1) The error was because I did not define cached's front as const 
because it has to expand the underlying buffer (a member) as needed. (I 
would have marked the buffer 'mutable' but we don't have that in D.)

This can be solved in some ways, crudest of which is the following:

         (cast()this).expandAsNeeded(id, 1);

Which is technically undefined behavior because the 'cached' range 
object could really be on read-only memory but since we are talking 
about a range, and such objects are supposed to be mutated, it becomes 
an interesting discussion at that point.

(There must be other ways like casting just the buffer but it is related 
to the discussion below.)

After fixing it that way (not pushed to github), I hit another 
compilation error.

2) This point is about a topic that I brought up recently: Types gain a 
'const' eagerly (and they have to, understandably).

For example, in your example you are caching an 'int', but my code sees 
const(int) just because std.range.Cycle.front chose to put a 'const' on 
itself. (With all good intentions: Cycle.front really does not mutate a 
Cycle object.)

However, as my range picks the element type with ElementType!T, I see 
const(int) as well. Again, all good so far... And here is my opApply 
funtion:

     int opApply(int delegate(ref EC.ET) func) scope
     {
         while(!empty)
         {
             auto f = front;

             int result = func(f);    // ERROR
             if (result)
             {
                 return result;
             }
             popFront();
         }

         return 0;
     }

ERROR: delegate `func(ref int)` is not callable using argument types 
`(const(int))`

Oh! See: I am passing an int... oh! by ref...  I guess I shouldn't 
expect my users to use 'const' in their foreach parameters. (I am not 
trying to see whether it is possible.)

So instead, I should have decided on my storage type as int (not 
const(int)) in this case. It can be done like this:

             static if (hasIndirections!(EC.ET))
             {
                 // Use the exact type
                 alias StorageT = T;
             }
             else
             {
                 alias StorageT = Unqual!T;
             }

But... Do I have the right to choose my storage type as 'int' even 
though I am caching const(int)? Perhaps I shouldn't because as discussed 
earlier in the general forum, my choice changes function overloading for 
my users. So, perhaps I should really store const(int)... (?)

I have to digest these issues more before jumping to a solution. :/

Ali

P.S. Related, I had to provide an opApply function because of this reason:

/**
     Support for `foreach` loops
  */
// This is needed to support foreach iteration because although
// RefCounted!CachedRange implicitly converts to CachedRange, which would be
// a range, the result gets copied to the foreach loop. Unfortunately, this
// type does not allow copying so we have to support foreach iteration
// explicitly here.
int opApply(int delegate(ref EC.ET) func) scope
{
     // ...
}

See that 'ref' there? Maybe I should have provided different opApply() 
functions to cover all cases. I am thinking... :)

Or maybe I should use something else other than RefCounted... Still 
thinking...


More information about the Digitalmars-d-announce mailing list