@@ -698,88 +698,100 @@ foreach (string s, double d; aa)
698
698
699
699
$(H3 $(LNAME2 foreach_over_struct_and_classes, Foreach over Structs and Classes with `opApply`))
700
700
701
- $(P If the aggregate expression is a struct or class object,
702
- the $(D foreach) is defined by the
703
- special $(LEGACY_LNAME2 opApply, op-apply, $(D opApply)) member function, and the
704
- `foreach_reverse` behavior is defined by the special
701
+ $(P If the aggregate expression is a ` struct` or ` class` object,
702
+ iteration with $(D foreach) can be defined by implementing the
703
+ $(LEGACY_LNAME2 opApply, op-apply, $(D opApply)) member function, and the
704
+ `foreach_reverse` behavior is defined by the
705
705
$(LEGACY_LNAME2 opApplyReverse, op-apply-reverse, $(D opApplyReverse)) member function.
706
706
These functions must each have the signature below:
707
707
)
708
708
709
709
$(GRAMMAR
710
- $(GNAME OpApplyDeclaration):
711
- `int opApply` `(` `scope` `int delegate` `(` $(I OpApplyParameters) `)` `dg` `)` `;`
710
+ `int opApply` `(` `scope` `int delegate` `(` $(I OpApplyParameters) `)` `body` `)` `;`
712
711
713
712
$(GNAME OpApplyParameters):
714
- *OpApplyParameter*
715
- *OpApplyParameter* , *OpApplyParameters*
713
+ $(GLINK ParameterDeclaration)
714
+ $(GLINK ParameterDeclaration) , *OpApplyParameters*
716
715
717
716
$(GNAME OpApplyParameter):
718
- $(GLINK ForeachTypeAttributes )$(OPT) $(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator)
717
+ $(GLINK ParameterStorageClass )$(OPT) $(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator)
719
718
)
720
719
721
- $(P where each $(I OpApplyParameter) of `dg` must match a $(GLINK ForeachType)
722
- in a $(GLINK ForeachStatement),
723
- otherwise the *ForeachStatement* will cause an error.)
724
-
725
- $(P Any *ForeachTypeAttribute* cannot be `enum`.)
726
-
727
720
$(PANEL
728
721
To support a `ref` iteration variable, the delegate must take a `ref` parameter:
729
722
730
723
$(SPEC_RUNNABLE_EXAMPLE_COMPILE
731
724
---
732
725
struct S
733
726
{
734
- int opApply(scope int delegate(ref uint n) dg);
727
+ uint n;
728
+ int opApply(scope int delegate(ref uint) body) => body(n);
735
729
}
736
- void f(S s )
730
+ void main( )
737
731
{
732
+ S s;
738
733
foreach (ref uint i; s)
739
- i++;
734
+ {
735
+ i++; // effectively s.n++
736
+ }
737
+ foreach (i; s)
738
+ {
739
+ static assert(is(typeof(i) == uint));
740
+ assert(i == 1);
741
+ }
740
742
}
741
743
---
742
744
)
743
- Above, `opApply` is still matched when `i` is not `ref`, so by using
744
- a `ref` delegate parameter both forms are supported.
745
- )
745
+ Above, `opApply` is matched both when `i` is and is not `ref`, so by using
746
+ a `ref` delegate parameter, both forms are supported.
746
747
747
- $(P There can be multiple $(D opApply) and $(D opApplyReverse) functions -
748
- one is selected
749
- by matching each parameter of `dg` to each $(I ForeachType)
750
- declared in the $(I ForeachStatement).)
748
+ Stating the type of the variable like in the first loop is optional,
749
+ as is showcased in the second loop, where the type is inferred.
750
+ )
751
751
752
- $(P The body of the apply
753
- function iterates over the elements it aggregates, passing each one
754
- in successive calls to the `dg` delegate. The delegate return value
755
- determines whether to interrupt iteration:)
752
+ $(P The apply
753
+ function iterates over the elements it aggregates by passing each one
754
+ in successive calls to the `body` delegate. The delegate return value
755
+ of the `body` delegate
756
+ determines how to proceed iteration:)
756
757
757
758
$(UL
758
- $(LI If the result is nonzero, apply must cease
759
+ $(LI If the result is nonzero, the apply function must cease
759
760
iterating and return that value.)
760
- $(LI If the result is 0 , then iteration should continue.
761
+ $(LI If the result is `0` , then iteration should continue.
761
762
If there are no more elements to iterate,
762
- apply must return 0 .)
763
+ the apply function must return `0` .)
763
764
)
764
765
765
- $(P The result of calling the delegate will be nonzero if the *ForeachStatement*
766
+ $(P The result of the `body` delegate will be nonzero if the *ForeachStatement*
766
767
body executes a matching $(GLINK BreakStatement), $(GLINK ReturnStatement), or
767
768
$(GLINK GotoStatement) whose matching label is outside the *ForeachStatement*.
768
769
)
769
770
770
- $(P For example, consider a class that is a container for two elements:)
771
+ $(P The $(D opApply) and $(D opApplyReverse) member functions can be overloaded.
772
+ Selection works similar to overload resolution by
773
+ comparing the number of `foreach` variables and number of parameters of `body` and,
774
+ if more than one overload remains unique overload,
775
+ matching the parameter types of `body` and each $(I ForeachType)
776
+ declared in the $(I ForeachStatement) when given.)
777
+
778
+ $(BEST_PRACTICE Overload apply functions only with delegates differing in number of parameters
779
+ to enable type inference for `foreach` variables.
780
+ )
781
+
782
+ $(P For example, consider a class that is a container for some elements:)
771
783
772
784
$(SPEC_RUNNABLE_EXAMPLE_RUN
773
785
--------------
774
786
class Foo
775
787
{
776
788
uint[] array;
777
789
778
- int opApply(scope int delegate(ref uint) dg )
790
+ int opApply(scope int delegate(ref uint) body )
779
791
{
780
792
foreach (e; array)
781
793
{
782
- int result = dg (e);
794
+ int result = body (e);
783
795
if (result)
784
796
return result;
785
797
}
@@ -811,57 +823,159 @@ $(CONSOLE
811
823
73
812
824
82
813
825
)
814
- $(PANEL
815
- The `scope` storage class on the $(D dg) parameter means that the delegate does
816
- not escape the scope of the $(D opApply) function (an example would be assigning $(D dg) to a
817
- global variable). If it cannot be statically guaranteed that $(D dg) does not escape, a closure may
818
- be allocated for it on the heap instead of the stack.
819
826
820
827
$(BEST_PRACTICE Annotate delegate parameters to `opApply` functions with `scope` when possible.)
828
+
829
+ $(PANEL
830
+ The `scope` storage class on the `body` parameter means that the delegate does
831
+ not escape the scope of the apply function (an example would be assigning`body` to a
832
+ global variable). If it cannot be statically guaranteed that `body` does not escape, a closure may
833
+ be allocated for it on the heap instead of the stack.
821
834
)
822
835
823
- $(P $(B Important:) If $(D opApply) catches any exceptions, ensure that those
824
- exceptions did not originate from the delegate passed to $(D opApply) . The user would expect
836
+ $(P $(B Important:) If apply functions catch any exceptions, ensure that those
837
+ exceptions did not originate from the delegate. The user would expect
825
838
exceptions thrown from a `foreach` body to both terminate the loop, and propagate outside
826
839
the `foreach` body.
827
840
)
828
841
829
842
$(H4 $(LNAME2 template-op-apply, Template `opApply`))
830
843
831
- $(P $(D opApply) can also be a templated function,
832
- which will infer the types of parameters based on the $(I ForeachStatement).
833
- For example:)
844
+ $(P `opApply` and `opApplyReverse` can also be a function templates,
845
+ which can optionally infer the types of parameters based on the $(I ForeachStatement).
846
+ )
847
+
848
+ $(P $(B Note:) An apply function template cannot infer `foreach` variable types.)
834
849
835
850
$(SPEC_RUNNABLE_EXAMPLE_RUN
836
851
--------------
837
852
struct S
838
853
{
839
- import std.traits : ParameterTypeTuple; // introspection template
840
- import std.stdio;
841
-
842
- int opApply(Dg)(scope Dg dg)
843
- if (ParameterTypeTuple!Dg.length == 2) // foreach with 2 parameters
854
+ int opApply(Body)(scope Body body)
844
855
{
845
- writeln(2);
846
- return 0;
847
- }
848
-
849
- int opApply(Dg)(scope Dg dg)
850
- if (ParameterTypeTuple!Dg.length == 3) // foreach with 3 parameters
851
- {
852
- writeln(3);
856
+ pragma(msg, Body);
853
857
return 0;
854
858
}
855
859
}
856
860
857
861
void main()
858
862
{
859
- foreach (int a, int b; S()) { } // calls first opApply function
860
- foreach (int a, int b, float c; S()) { } // calls second opApply function
863
+ foreach (int a, int b; S()) { } // int delegate(ref int, ref int) pure nothrow @nogc @safe
864
+ foreach (bool b, string s; S()) { } // int delegate(ref bool, ref string) pure nothrow @nogc @safe
865
+ foreach (x; S()) { } // Error: cannot infer type for `foreach` variable `x`, perhaps set it explicitly
861
866
}
862
867
--------------
863
868
)
864
869
870
+ $(H4 $(LNAME2 template-instance-op-apply, `opApply` as an alias to a explicit function template instance))
871
+
872
+ $(P `opApply` and `opApplyReverse` can be aliases to an appropriate function or function template.
873
+ However, special treatment is given to apply functions that are aliases of an appropriate function template instance.)
874
+
875
+ $(P In that case, the function template instance is used for overload selection and `foreach` variable type inference,
876
+ but the template is instantiated again with the actual delegate type of the `foreach` body.
877
+ This way, apply functions can infer attributes depending on the attributes of `body` delegate.)
878
+
879
+ $(SPEC_RUNNABLE_EXAMPLE_RUN
880
+ --------------
881
+ struct A
882
+ {
883
+ int opApply(scope int delegate(long) body) => body(42);
884
+ }
885
+ struct B
886
+ {
887
+ int opApply(Body)(scope Body body) => body(42);
888
+ }
889
+ struct C
890
+ {
891
+ int opApplyImpl(Body)(scope Body body) => body(42);
892
+ alias opApply = opApplyImpl!(int delegate(long));
893
+ }
894
+ void main() @nogc nothrow pure @safe
895
+ {
896
+ // Error: `@nogc` function `D main` cannot call non-@nogc function `onlineapp.A.opApply`
897
+ // Error: `pure` function `D main` cannot call impure function `onlineapp.A.opApply`
898
+ // Error: `@safe` function `D main` cannot call `@system` function `onlineapp.A.opApply`
899
+ // Error: function `onlineapp.A.opApply` is not `nothrow`
900
+ static assert(!__traits(compiles, () @safe {
901
+ foreach (x; A()) { }
902
+ }));
903
+
904
+ // Error: cannot infer type for `foreach` variable `x`, perhaps set it explicitly
905
+ static assert(!__traits(compiles, {
906
+ foreach (x; B()) { }
907
+ }));
908
+
909
+ // Good:
910
+ foreach (x; C())
911
+ {
912
+ static assert(is(typeof(x) == long));
913
+ assert(x == 42);
914
+ }
915
+ }
916
+ --------------
917
+ )
918
+
919
+ $(P The `opApplyImpl` pattern is generally preferable to
920
+ overloading many apply functions with all possible combinations of attributes.)
921
+
922
+ $(P Multiple apply function aliases can exist, and selection and `foreach` variable type inference work:)
923
+
924
+ --------------
925
+ class Tree(T)
926
+ {
927
+ private T label;
928
+ private Tree[] children;
929
+
930
+ this(T label, Tree[] children = null)
931
+ {
932
+ this.label = label;
933
+ this.children = children;
934
+ }
935
+
936
+ alias opApply = opApplyImpl!(int delegate(ref T label));
937
+ alias opApply = opApplyImpl!(int delegate(size_t depth, ref T label));
938
+ alias opApply = opApplyImpl!(int delegate(size_t depth, bool isLastChild, ref T label));
939
+
940
+ int opApplyImpl(Body)(scope Body body) => opApplyImpl2(0, true, body);
941
+ int opApplyImpl2(Body)(size_t depth, bool lastChild, scope Body body)
942
+ {
943
+ import std.meta : AliasSeq;
944
+ static if (is(Body : int delegate(size_t, bool, ref T)))
945
+ alias args = AliasSeq!(depth, lastChild, label);
946
+ else static if (is(Body : int delegate(size_t, ref T)))
947
+ alias args = AliasSeq!(depth, label);
948
+ else
949
+ alias args = label;
950
+ if (auto result = body(args)) return result;
951
+ foreach (i, child; children)
952
+ {
953
+ if (auto result = child.opApplyImpl2!Body(depth + 1, i + 1 == children.length, body))
954
+ return result;
955
+ }
956
+ return 0;
957
+ }
958
+ }
959
+
960
+ void printTree(Tree)(Tree tree)
961
+ {
962
+ // Selects the unique one with 2 parameters.
963
+ // Infers types: size_t and whatever label type the tree has.
964
+ foreach (depth, ref label; tree)
965
+ {
966
+ import std.stdio;
967
+ foreach (_; 0 .. depth) write(" ");
968
+ writeln(label);
969
+ }
970
+ }
971
+
972
+ void main() @safe
973
+ {
974
+ alias T = Tree!int;
975
+ printTree(new T(0, [new T(1, [new T(2), new T(3)]), new T(4, [new T(5)])]));
976
+ }
977
+ --------------
978
+
865
979
$(H3 $(LEGACY_LNAME2 foreach_with_ranges, foreach-with-ranges, Foreach over Structs and Classes with Ranges))
866
980
867
981
$(P If the aggregate expression is a struct or class object, but the
@@ -1028,16 +1142,16 @@ $(H3 $(LNAME2 foreach_over_delegates, Foreach over Delegates))
1028
1142
$(SPEC_RUNNABLE_EXAMPLE_RUN
1029
1143
--------------
1030
1144
// Custom loop implementation, that iterates over powers of 2 with
1031
- // alternating sign. The foreach loop body is passed in dg .
1032
- int myLoop(scope int delegate(int) dg )
1145
+ // alternating sign. The foreach loop body is passed in `body` .
1146
+ int myLoop(scope int delegate(int) body )
1033
1147
{
1034
1148
for (int z = 1; z < 128; z *= -2)
1035
1149
{
1036
- auto ret = dg (z);
1150
+ auto result = body (z);
1037
1151
1038
- // If the loop body contains a break, ret will be non-zero.
1039
- if (ret != 0)
1040
- return ret ;
1152
+ // If the loop body contains a break, result will be non-zero.
1153
+ if (result != 0)
1154
+ return result ;
1041
1155
}
1042
1156
return 0;
1043
1157
}
@@ -1583,7 +1697,7 @@ foreach (item; list)
1583
1697
$(P Any intervening $(RELATIVE_LINK2 try-statement, `finally`) clauses are executed,
1584
1698
and any intervening synchronization objects are released.)
1585
1699
1586
- $(P $(D Note:) If a `finally` clause executes a `throw` out of the finally
1700
+ $(P $(B Note:) If a `finally` clause executes a `throw` out of the finally
1587
1701
clause, the continue target is never reached.)
1588
1702
1589
1703
$(H2 $(LEGACY_LNAME2 BreakStatement, break-statement, Break Statement))
0 commit comments