Thread overview
@nogc with opApply
Aug 11, 2018
Alex
Aug 12, 2018
tide
Aug 12, 2018
ag0aep6g
Aug 12, 2018
Alex
August 11, 2018
Hi all,
maybe I misunderstand something but having this:

´´´
import std.experimental.all;

static assert(isIterable!S);

void main()
{
    S s;
    s.each!(el => el.writeln);
}

struct S
{
    private Nullable!uint member = 0;
    Nullable!uint front() @nogc { return member; }
    //void popFront(){} // not implementable.
    //bool empty(){} // not implementable
    Nullable!uint successor(uint current) @nogc { return Nullable!uint.init; }

    /**
    the opApply method grants the correct foreach behavior
    */
    int opApply(scope int delegate(ref uint) /*@nogc*/ operations) //@nogc
    {
        int result;

        for(auto leading = front; !leading.isNull; leading = successor(leading.get))
        {
            result = operations(leading.get);

            if(result)
            {
                break;
            }
        }
        return result;
    }
}
´´´

Everything works fine, before I try to use the opApply function inside a @nogc function.

If I do, compiler complains, that opApply is not marked as @nogc. Ok.
If I try to mark opApply @nogc, I would have to mark operations delegate also as @nogc, but I can't do this, as I do not know a priori, how it will be used.

Now, as I learned at some point, a possibility would be, to templatify the function, as the compiler can then derive, if @nogc apply or not.
But if I write
´´´
int opApply()(...){...}
´´´
Then the static assert from above refuses to compile.

So... how to solve this case?
August 12, 2018
On Saturday, 11 August 2018 at 10:00:34 UTC, Alex wrote:
> Hi all,
> maybe I misunderstand something but having this:
>
> ´´´
> import std.experimental.all;
>
> static assert(isIterable!S);
>
> void main()
> {
>     S s;
>     s.each!(el => el.writeln);
> }
>
> struct S
> {
>     private Nullable!uint member = 0;
>     Nullable!uint front() @nogc { return member; }
>     //void popFront(){} // not implementable.
>     //bool empty(){} // not implementable
>     Nullable!uint successor(uint current) @nogc { return Nullable!uint.init; }
>
>     /**
>     the opApply method grants the correct foreach behavior
>     */
>     int opApply(scope int delegate(ref uint) /*@nogc*/ operations) //@nogc
>     {
>         int result;
>
>         for(auto leading = front; !leading.isNull; leading = successor(leading.get))
>         {
>             result = operations(leading.get);
>
>             if(result)
>             {
>                 break;
>             }
>         }
>         return result;
>     }
> }
> ´´´
>
> Everything works fine, before I try to use the opApply function inside a @nogc function.
>
> If I do, compiler complains, that opApply is not marked as @nogc. Ok.
> If I try to mark opApply @nogc, I would have to mark operations delegate also as @nogc, but I can't do this, as I do not know a priori, how it will be used.
>
> Now, as I learned at some point, a possibility would be, to templatify the function, as the compiler can then derive, if @nogc apply or not.
> But if I write
> ´´´
> int opApply()(...){...}
> ´´´
> Then the static assert from above refuses to compile.
>
> So... how to solve this case?

There's no way to solve it, just don't use @nogc is the easiest workaround. It wasn't thought out when it was added and these are one of the cases where it doesn't work. Having functions automatically declare themselves @nogc if they don't use the gc would solve part of the problem. Which is how templates work.

https://dlang.org/library/std/traits/is_iterable.html

If you see how isIterable is defined you'll see that it requires opApply be able to provide the element type automatically. That is "foreach" doesn't define a type and it is automatically deduced. The compiler can't deduce the argument type because the function is a template.


    foreach(t ; S.init) // Error: cannot infer type for `foreach` variable `t`, perhaps set it explicitly
    {

    }
August 12, 2018
On 08/11/2018 12:00 PM, Alex wrote:
> ´´´
> import std.experimental.all;
> 
> static assert(isIterable!S);
> 
[...]
> 
> struct S
> {
[...]
>      int opApply(scope int delegate(ref uint) /*@nogc*/ operations) //@nogc
>      {
[...]
>      }
> }
> ´´´
> 
> Everything works fine, before I try to use the opApply function inside a @nogc function.
> 
> If I do, compiler complains, that opApply is not marked as @nogc. Ok.
> If I try to mark opApply @nogc, I would have to mark operations delegate also as @nogc, but I can't do this, as I do not know a priori, how it will be used.
> 
> Now, as I learned at some point, a possibility would be, to templatify the function, as the compiler can then derive, if @nogc apply or not.
> But if I write
> ´´´
> int opApply()(...){...}
> ´´´
> Then the static assert from above refuses to compile.
> 
> So... how to solve this case?

You can provide to overloads: one with @nogc, one without it. To keep it somewhat DRY, you can let them forward to a template implementation:

    int opApply(scope int delegate(ref uint) operations)
    {
        return opApplyImpl(operations);
    }
    int opApply(scope int delegate(ref uint) @nogc operations) @nogc
    {
        return opApplyImpl(operations);
    }
    int opApplyImpl(O)(O operations)
    {
        /* ... implementation here ... */
    }
August 12, 2018
On Sunday, 12 August 2018 at 01:39:21 UTC, ag0aep6g wrote:
> On 08/11/2018 12:00 PM, Alex wrote:
>> [...]
> [...]
>> [...]
> [...]
>>  [...]
> [...]
>> [...]
>
> You can provide to overloads: one with @nogc, one without it. To keep it somewhat DRY, you can let them forward to a template implementation:
>
>     int opApply(scope int delegate(ref uint) operations)
>     {
>         return opApplyImpl(operations);
>     }
>     int opApply(scope int delegate(ref uint) @nogc operations) @nogc
>     {
>         return opApplyImpl(operations);
>     }
>     int opApplyImpl(O)(O operations)
>     {
>         /* ... implementation here ... */
>     }

Ah... that's nice...
Thanks!