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