I have a big example code (compared to me) for this particular case, I can't include example as narrowing it down would take several hours.
I just ask you to give me general tips to avoid it.
How can I detect them earlier for example?
Is it a way analyze the --vtemplates compiler output and detect weak spots?
I already know a few effective workarounds (I learned from the other template recursion topics here):
- Don't use dynamic returns. Put all the cases into static if else chains.
- If the "template instance recursive expansion" pops up, narrow the import clause to individual names.
I have a vector/matrix/2d-image math module. Most of the times the error pops there because there is a lot of template work there. But I know it can pop anywhere, this time it is inside my traits.isNumeric() inside my vector.opBinary(). Is there a way to access the full instation path? Maybe it could show me a bug.
I just put there some sources near the error message, maybe someone can spot a terrible mistake.
Thank you in advance!
(LDC 1.40 Win64)
private template OperationResultType(string op, A, B)
{
static if(op.among("<<", ">>", ">>>")) alias OperationResultType = A;
else alias OperationResultType = CommonType!(A, B);
}
auto generateVector(CT, alias fun, T...)(in T args)
{
static if(anyVector!T)
{
Vector!(CT, CommonVectorLength!T) res;
static foreach(i; 0..res.length)
res[i] = cast(CT) mixin("fun(", T.length.iota.map!(j => "args["~j.text~"].vectorAccess!i").join(','), ")");
return res;
}
else
{ return cast(CT) fun(args); }
}
struct Vector(CT, int N)
if(N.inRange(2, 4))
{
... lots of code ...
private static binaryVectorOp(string op, A, B)(in A a, in B b)
{
alias CT = OperationResultType!(op, ScalarType!A, ScalarType!B);
return generateVector!(CT, (a, b) => mixin("a", op, "b") )(a, b);
}
auto opBinary(string op, T)(in T other) const
{
static if(isNumeric!T || isVector!T)
//^^^^^^^^^^^ Here the compiler decided that enough is enough :S
//Error: template instance /+Code: std.traits.isNumeric!(Vector!(float, 3))+/ recursive expansion
{
//vector * (scalar or vector)
return binaryVectorOp!op(this, other);
}
else static if(op=="*" && isMatrix!T && T.height==length)
{
//vector * matrix
return other.transpose * this;
//Opt: this is slow if it is not unrolled.
}
else static if(op=="in" && isBounds!T && T.VectorLength == length)
{ return other.contains(this); }
else static if(op=="in" && isImage!T && T.Dimension == length)
{ return other.contains(this); }
else static if(op.among("+", "*") && isBounds!T && T.VectorLength == length)
{ return other.opBinary!op(this); }
else
{ static assert(false, "Unhandled operation: "~op~" "~T.stringof); }
}
... lots of code ...
}