我是一个新面孔的D新手,我非常有兴趣利用它所提供的一切。我目前正在将用C ++编写的大型代码库转换为D用于教育。我几乎没有开始,我达到了一些奇怪的不一致。我有一个带有构造函数的模板结构和该构造函数的unittest,(希望我正确使用它)。它似乎正确验证。但是,如果我在文件底部为特定的模板化结构创建一个类型别名,我会在unittest中得到一个异常。
main.d
import math;
int main() {
return 0;
}
math.d
import std.math;
import std.stdio;
struct Vector2(T) {
T[2] vec = [0, 0];
@property const T x() {return vec[0];}
@property inout(T) x(inout(T) val) {return vec[0] = val;}
@property const T y() {return vec[1];}
@property inout(T) y(inout(T) val) {return vec[1] = val;}
this(T x, T y) {
this.x = x;
this.y = y;
}
unittest {
Vector2!int a = Vector2!int(2, 2);
assert(a.x == 2 && a.y == 2);
Vector2!float b = Vector2!float(2.2, 2.2);
assert(b.x == 2.2 && b.y == 2.2); // <-- this line excepts given alias
}
}
// if this is not here, no problem.
// if it is here "core.exception.AssertError@math.d(21): unittest failure"
alias Vector2f = Vector2!float;
为什么别名的存在会导致单元测试失败?我正在使用此命令来构建:
dmd -unittest main.d math.d
我使用Windows 10,DMD版本2.070.2和msys2 shell
答案 0 :(得分:3)
模板本身不是(并且通常不能)单元测试。只有它的实例化实例是。
由于2.2
无法在浮点变量中准确表示,因此距离double
最近的2.2
与距{最近的float
不同{1}}。如果将行更改为以下(2.2
是浮点常量,2.2f
是双常量),则测试成功就好了:
2.2
通常,除非really know what they are doing,否则应避免直接比较浮点变量。在D中,可能需要使用std.math.approxEqual来比较它们。
答案 1 :(得分:3)
如果模板没有实例化,模板甚至不会被编译到某些基本语法检查之外。模板类型是类型的模板,而不是实际类型。因此,Vector2
不是类型,而Vector2!int
是。{1}}。您向Vector2!float
声明别名的事实是实例化Vector2
并使其完全编译 - 包括unittest
块。如果模板没有实例化,模板内部的unittest
块不会比模板的其余部分编译,并且它将被编译到模板的每个实例中(这就是为什么它&#39} ;将unittest
块放在模板中通常不是一个好主意。
因此,至少应该在模板之外放置一个unittest
块来实例化模板。如果你想避免将单元测试编译到模板的每个实例中,那么你应该将它们全部移出模板之外(可能标记为注释以指示它们使用哪些函数),这将使模板适当实例化和测试。如果你不关心在模板的每个实例化中获得单元测试的副本,并且想要在他们测试的函数之后立即离开测试(就像你通常那样),那么你只需要放一个{ {1}}阻塞模板后立即实例化它,以便模板内的unittest
块被编译并运行。
有a proposal用于添加功能以允许模板内部的特殊unittest
块,这些块实际上不是模板的一部分,并且功能类似于模板外的unittest
块功能(但仍然会在被测试的功能旁边),但尚未确定它是否会添加到语言中以解决此问题。因此,在此期间,我建议不要在模板中放置unittest
块,但无论如何,您需要确保模板在其自身之外进行实例化,以便对其进行编译和测试。
在旁注中,您真的不应该将浮点值与unittest
或==
进行比较,如本经典论文中所述:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html。使用approxEqual
将是一个更好的选择。