简体   繁体   中英

Implement opApply with nogc and inferred parameters

Note: I initially posted an over-simplified version of my problem. A more accurate description follows:


I have the following struct:

struct Thing(T) {
  T[3] values;

  int opApply(scope int delegate(size_t, ref T) dg) {
    int res = 0;
    foreach(idx, ref val; values) {
      res = dg(idx, val);
      if (res) break;
    }
    return res;
  }
}

Foreach can be used like so:

unittest {
  Thing!(size_t[]) thing;
  foreach(i, ref val ; thing) val ~= i;
}

However, it is not @nogc friendly:

@nogc unittest {
  Thing!size_t thing;
  foreach(i, ref val ; thing) val = i;
}

If I change the signature to

  int opApply(scope int delegate(size_t, ref T) @nogc dg) { ... }

It works for the @nogc case, but fails to compile for non- @nogc cases.

The solutions I have tried are:

  1. Cast the delegate

     int opApply(scope int delegate(size_t, ref T) dg) { auto callme = cast(int delegate(size_t, ref T) @nogc) dg; // use callme instead of dg to support nogc 

This seems wrong as I am willfully casting a @nogc attribute even onto functions that do may not support it.

  1. Use opSlice instead of opApply :

I'm not sure how to return an (index, ref value) tuple from my range. Even if I could, I think it would have to contain a pointer to my static array, which could have a shorter lifetime than the returned range.

  1. Use a templated opApply :

All attempts to work with this have failed to automatically determine the foreach argument types. For example, I needed to specify:

foreach(size_t idx, ref int value ; thing)

Which I see as a significant hindrance to the API.

Sorry for underspecifying my problem before. For total transparency, Enumap is the "real-world" example. It currently uses opSlice , which does not support ref access to values. My attempts to support 'foreach with ref' while maintaining @nogc support is what prompted this question.

Instead of overloading the opApply operator you can implement an input range for your type. Input ranges work automatically as the agregate argument in foreach statements:

struct Thing(K,V) {
    import std.typecons;
    @nogc bool empty(){return true;}
    @nogc auto front(){return tuple(K.init, V.init);}
    @nogc void popFront(){}
}

unittest {
  Thing!(int, int) w;
  foreach(val ; w) { 
    int[] i = [1,2,3];  // spurious allocation
  }
}

@nogc unittest {
  Thing!(int, int) w;
  foreach(idx, val ; w) { assert(idx == val); }
}

This solves the problem caused by the allocation of the delegate used in foreach.

Note that the example is shitty (the range doesn't work at all, and usually ranges are provided via opSlice, etc) but you should get the idea.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM