@@ -1860,88 +1860,65 @@ private template Iota(size_t n)
1860
1860
assert (Iota! 3 == AliasSeq! (0 , 1 , 2 ));
1861
1861
}
1862
1862
1863
- /* The number that the dim-th argument's tag is multiplied by when
1864
- * converting TagTuples to and from case indices ("caseIds").
1865
- *
1866
- * Named by analogy to the stride that the dim-th index into a
1867
- * multidimensional static array is multiplied by to calculate the
1868
- * offset of a specific element.
1869
- */
1870
- private size_t stride (size_t dim, lengths... )()
1871
- {
1872
- import core.checkedint : mulu;
1873
-
1874
- size_t result = 1 ;
1875
- bool overflow = false ;
1876
-
1877
- static foreach (i; 0 .. dim)
1878
- {
1879
- result = mulu(result, lengths[i], overflow);
1880
- }
1881
-
1882
- /* The largest number matchImpl uses, numCases, is calculated with
1883
- * stride!(SumTypes.length), so as long as this overflow check
1884
- * passes, we don't need to check for overflow anywhere else.
1885
- */
1886
- assert (! overflow, " Integer overflow" );
1887
- return result;
1888
- }
1889
-
1890
1863
private template matchImpl (Flag! " exhaustive" exhaustive, handlers... )
1891
1864
{
1892
1865
auto ref matchImpl (SumTypes... )(auto ref SumTypes args)
1893
1866
if (allSatisfy! (isSumType, SumTypes) && args.length > 0 )
1894
1867
{
1895
- alias stride (size_t i) = .stride! (i, Map! (typeCount, SumTypes));
1896
- alias TagTuple = .TagTuple! (SumTypes);
1897
-
1898
- /*
1899
- * A list of arguments to be passed to a handler needed for the case
1900
- * labeled with `caseId`.
1901
- */
1902
- template handlerArgs (size_t caseId)
1868
+ // Single dispatch (fast path)
1869
+ static if (args.length == 1 )
1903
1870
{
1904
- enum tags = TagTuple.fromCaseId(caseId);
1905
- enum argsFrom (size_t i : tags.length) = " " ;
1906
- enum argsFrom (size_t i) = " args[" ~ toCtString! i ~ " ].get!(SumTypes[" ~ toCtString! i ~ " ]" ~
1907
- " .Types[" ~ toCtString! (tags[i]) ~ " ])(), " ~ argsFrom! (i + 1 );
1908
- enum handlerArgs = argsFrom! 0 ;
1909
- }
1871
+ /* When there's only one argument, the caseId is just that
1872
+ * argument's tag, so there's no need for TagTuple.
1873
+ */
1874
+ enum handlerArgs (size_t caseId) =
1875
+ " args[0].get!(SumTypes[0].Types[" ~ toCtString! caseId ~ " ])()" ;
1910
1876
1911
- /* An AliasSeq of the types of the member values in the argument list
1912
- * returned by `handlerArgs!caseId`.
1913
- *
1914
- * Note that these are the actual (that is, qualified) types of the
1915
- * member values, which may not be the same as the types listed in
1916
- * the arguments' `.Types` properties.
1917
- */
1918
- template valueTypes (size_t caseId)
1877
+ alias valueTypes (size_t caseId) =
1878
+ typeof (args[0 ].get ! (SumTypes[0 ].Types[caseId])());
1879
+
1880
+ enum numCases = SumTypes[0 ].Types.length;
1881
+ }
1882
+ // Multiple dispatch (slow path)
1883
+ else
1919
1884
{
1920
- enum tags = TagTuple.fromCaseId(caseId);
1885
+ alias typeCounts = Map! (typeCount, SumTypes);
1886
+ alias stride (size_t i) = .stride! (i, typeCounts);
1887
+ alias TagTuple = .TagTuple! typeCounts;
1888
+
1889
+ alias handlerArgs (size_t caseId) = .handlerArgs! (caseId, typeCounts);
1921
1890
1922
- template getType (size_t i)
1891
+ /* An AliasSeq of the types of the member values in the argument list
1892
+ * returned by `handlerArgs!caseId`.
1893
+ *
1894
+ * Note that these are the actual (that is, qualified) types of the
1895
+ * member values, which may not be the same as the types listed in
1896
+ * the arguments' `.Types` properties.
1897
+ */
1898
+ template valueTypes (size_t caseId)
1923
1899
{
1924
- enum tid = tags[i];
1925
- alias T = SumTypes[i].Types[tid];
1926
- alias getType = typeof (args[i].get ! T());
1900
+ enum tags = TagTuple.fromCaseId(caseId);
1901
+
1902
+ template getType (size_t i)
1903
+ {
1904
+ enum tid = tags[i];
1905
+ alias T = SumTypes[i].Types[tid];
1906
+ alias getType = typeof (args[i].get ! T());
1907
+ }
1908
+
1909
+ alias valueTypes = Map! (getType, Iota! (tags.length));
1927
1910
}
1928
1911
1929
- alias valueTypes = Map! (getType, Iota! (tags.length));
1912
+ /* The total number of cases is
1913
+ *
1914
+ * Π SumTypes[i].Types.length for 0 ≤ i < SumTypes.length
1915
+ *
1916
+ * Conveniently, this is equal to stride!(SumTypes.length), so we can
1917
+ * use that function to compute it.
1918
+ */
1919
+ enum numCases = stride! (SumTypes.length);
1930
1920
}
1931
1921
1932
- /* The total number of cases is
1933
- *
1934
- * Π SumTypes[i].Types.length for 0 ≤ i < SumTypes.length
1935
- *
1936
- * Or, equivalently,
1937
- *
1938
- * ubyte[SumTypes[0].Types.length]...[SumTypes[$-1].Types.length].sizeof
1939
- *
1940
- * Conveniently, this is equal to stride!(SumTypes.length), so we can
1941
- * use that function to compute it.
1942
- */
1943
- enum numCases = stride! (SumTypes.length);
1944
-
1945
1922
/* Guaranteed to never be a valid handler index, since
1946
1923
* handlers.length <= size_t.max.
1947
1924
*/
@@ -1998,7 +1975,12 @@ private template matchImpl(Flag!"exhaustive" exhaustive, handlers...)
1998
1975
mixin (" alias " , handlerName! hid, " = handler;" );
1999
1976
}
2000
1977
2001
- immutable argsId = TagTuple(args).toCaseId;
1978
+ // Single dispatch (fast path)
1979
+ static if (args.length == 1 )
1980
+ immutable argsId = args[0 ].tag;
1981
+ // Multiple dispatch (slow path)
1982
+ else
1983
+ immutable argsId = TagTuple(args).toCaseId;
2002
1984
2003
1985
final switch (argsId)
2004
1986
{
@@ -2029,10 +2011,11 @@ private template matchImpl(Flag!"exhaustive" exhaustive, handlers...)
2029
2011
}
2030
2012
}
2031
2013
2014
+ // Predicate for staticMap
2032
2015
private enum typeCount (SumType) = SumType.Types.length;
2033
2016
2034
- /* A TagTuple represents a single possible set of tags that `args`
2035
- * could have at runtime.
2017
+ /* A TagTuple represents a single possible set of tags that the arguments to
2018
+ * `matchImpl` could have at runtime.
2036
2019
*
2037
2020
* Because D does not allow a struct to be the controlling expression
2038
2021
* of a switch statement, we cannot dispatch on the TagTuple directly.
@@ -2054,22 +2037,23 @@ private enum typeCount(SumType) = SumType.Types.length;
2054
2037
* When there is only one argument, the caseId is equal to that
2055
2038
* argument's tag.
2056
2039
*/
2057
- private struct TagTuple (SumTypes ... )
2040
+ private struct TagTuple (typeCounts ... )
2058
2041
{
2059
- size_t [SumTypes .length] tags;
2042
+ size_t [typeCounts .length] tags;
2060
2043
alias tags this ;
2061
2044
2062
- alias stride (size_t i) = .stride! (i, Map ! (typeCount, SumTypes) );
2045
+ alias stride (size_t i) = .stride! (i, typeCounts );
2063
2046
2064
2047
invariant
2065
2048
{
2066
2049
static foreach (i; 0 .. tags.length)
2067
2050
{
2068
- assert (tags[i] < SumTypes [i].Types.length , " Invalid tag" );
2051
+ assert (tags[i] < typeCounts [i], " Invalid tag" );
2069
2052
}
2070
2053
}
2071
2054
2072
- this (ref const (SumTypes) args)
2055
+ this (SumTypes... )(ref const SumTypes args)
2056
+ if (allSatisfy! (isSumType, SumTypes) && args.length == typeCounts.length)
2073
2057
{
2074
2058
static foreach (i; 0 .. tags.length)
2075
2059
{
@@ -2104,6 +2088,52 @@ private struct TagTuple(SumTypes...)
2104
2088
}
2105
2089
}
2106
2090
2091
+ /* The number that the dim-th argument's tag is multiplied by when
2092
+ * converting TagTuples to and from case indices ("caseIds").
2093
+ *
2094
+ * Named by analogy to the stride that the dim-th index into a
2095
+ * multidimensional static array is multiplied by to calculate the
2096
+ * offset of a specific element.
2097
+ */
2098
+ private size_t stride (size_t dim, lengths... )()
2099
+ {
2100
+ import core.checkedint : mulu;
2101
+
2102
+ size_t result = 1 ;
2103
+ bool overflow = false ;
2104
+
2105
+ static foreach (i; 0 .. dim)
2106
+ {
2107
+ result = mulu(result, lengths[i], overflow);
2108
+ }
2109
+
2110
+ /* The largest number matchImpl uses, numCases, is calculated with
2111
+ * stride!(SumTypes.length), so as long as this overflow check
2112
+ * passes, we don't need to check for overflow anywhere else.
2113
+ */
2114
+ assert (! overflow, " Integer overflow" );
2115
+ return result;
2116
+ }
2117
+
2118
+ /* A list of arguments to be passed to a handler needed for the case
2119
+ * labeled with `caseId`.
2120
+ */
2121
+ private template handlerArgs (size_t caseId, typeCounts... )
2122
+ {
2123
+ enum tags = TagTuple! typeCounts.fromCaseId(caseId);
2124
+
2125
+ alias handlerArgs = AliasSeq! ();
2126
+
2127
+ static foreach (i; 0 .. tags.length)
2128
+ {
2129
+ handlerArgs = AliasSeq! (
2130
+ handlerArgs,
2131
+ " args[" ~ toCtString! i ~ " ].get!(SumTypes[" ~ toCtString! i ~ " ]" ~
2132
+ " .Types[" ~ toCtString! (tags[i]) ~ " ])(), "
2133
+ );
2134
+ }
2135
+ }
2136
+
2107
2137
// Matching
2108
2138
@safe unittest
2109
2139
{
0 commit comments