Suppose I have the following variadic function, which job is to concat together a path from pieces (each piece can either be an index of integral type, or a node of string type):
string
makePath(P...)(P path)
{
import std.conv;
import std.format;
string res;
foreach (piece; path) {
pragma(msg, typeof(piece).stringof);
static if (is(piece: size_t))
res = res is null ? piece.to!string : format("%s[%s]", res, piece);
else if (is(piece: string))
res = res is null ? piece : format("%s.%s", res, piece);
else
static assert(0);
}
}
If I later use it like this: string path = makePath("foo", "bar")
, somehow the code reaches the static assert(0);
and the compilation terminates. This is most curious, but the pragma actually writes string
as the type of the first argument despite the fact that a code path for some other type was taken.
Even better, using makePath(12, 13)
results in the compiler complaining about both the line for strings (about incompatible types of int
and string
) and static assert
. What is going on here?
I've tried this both on DMD and LDC.
The is
keyword is what's at fault here. (It's quite a confusing keyword I find...)
I'd recommend you use the templates in std.traits to test for types, most there are covered: https://dlang.org/phobos/std_traits.html
Here's a working version of your function:
string
makePath(P...)(P path)
{
import std.conv;
import std.format;
import std.traits : isSomeString, isNumeric;
string res;
foreach (piece; path) {
static if (isNumeric!(typeof(piece)))
res = res is null ? piece.to!string : format("%s[%s]", res, piece);
else static if (isSomeString!(typeof(piece))) // Note, you were missing the 'static' on the 'else if' part
res = res is null ? piece : format("%s.%s", res, piece);
else
static assert(0);
}
return res;
}
unittest {
static assert(makePath("foo","bar") == "foo.bar");
static assert(makePath("foo","bar",1) == "foo.bar[1]");
}