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...

Reply via email to