www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - nogc with opApply

reply Alex <sascha.orlov gmail.com> writes:
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?
Aug 11 2018
next sibling parent tide <tide tide.tide> writes:
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 { }
Aug 11 2018
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
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 ... */ }
Aug 11 2018
parent Alex <sascha.orlov gmail.com> writes:
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!
Aug 12 2018