我的C++代码需要一组有限的二维矩阵代数运算。我决定使用std::array
来实现这一点,如下所示:
template <typename T, size_t N, size_t M>
using array_2d = std::array<std::array<T, M>, N>;
我应该如何正确地为这种类型编写一个GMock匹配器,以比较两个这样的二重矩阵?我想出了一个不那么聪明的:
MATCHER_P(Arrays2dDoubleEq, expected, "") {
for (int i = 0; i < arg.size(); i++) {
for (int j = 0; j < arg[i].size(); j++) {
EXPECT_THAT(arg[i][j], DoubleEq(expected[i][j]));
}
}
return true;
}
MATCHER_P2(Arrays2dDoubleNear, expected, max_abs_err, "") {
for (int i = 0; i < arg.size(); i++) {
for (int j = 0; j < arg[i].size(); j++) {
EXPECT_THAT(arg[i][j], DoubleNear(expected[i][j], max_abs_err));
}
}
return true;
}
我使用的是:EXPECT_THAT(result, Arrays2dDoubleEq(expected));
这不仅看起来很难编码,而且没有给出很好的反馈。当矩阵不匹配时,失败断言的输出是一堆不相等的二重。失败的测试输出很难阅读,并且缺少关于矩阵索引的信息。
我觉得可以(也应该(用更好的方式来做。我已经看了一些文档和GMock食谱。虽然有一些容器匹配器,但我不能同时使用它们来比较两个嵌套数组。
有人能指出我应该使用哪些GMock功能来使这个匹配器更好吗?或者,也许有人可以指出我应该更仔细阅读的文件的一部分,以了解我在这里可能遗漏了什么?
您可能会考虑的一件事是从匹配器显式返回false
或true
,而不是调用断言。然后,您可以使用result_listener
来提供有关比赛中到底出了什么问题的其他信息。在执行检查之前,您还应该检查阵列的尺寸,以避免未定义的bahavior
using testing::DoubleEq;
using testing::Value;
using testing::Not;
MATCHER_P(Arrays2dDoubleEq, expected, "") {
if (arg.size() != expected.size())
{
*result_listener << "arg.size() != expected.size() ";
*result_listener << arg.size() << " vs " << expected.size();
return false;
}
for (size_t i = 0; i < arg.size(); i++) {
if (arg[i].size() != expected[i].size())
{
*result_listener << "arg[i].size() != expected[i].size() i = " << i << "; ";
*result_listener << arg[i].size() << " vs " << expected[i].size();
return false;
}
for (size_t j = 0; j < arg[i].size(); j++) {
if (!Value(arg[i][j], DoubleEq(expected[i][j])))
{
*result_listener << "element(" << i << ", " << j << ") mismatch ";
*result_listener << arg[i][j] << " vs " << expected[i][j];
return false;
}
}
}
return true;
}
TEST(xxx, yyy)
{
array_2d<double, 2, 3> arr1 = {std::array<double, 3>({1, 2, 3}), std::array<double, 3>({4, 5, 6})};
array_2d<double, 2, 3> arr2 = arr1;
array_2d<double, 2, 3> arr3 = arr1;
arr3[0][0] = 69.69;
array_2d<double, 5, 6> arr4;
ASSERT_THAT(arr1, Arrays2dDoubleEq(arr2));
ASSERT_THAT(arr2, Not(Arrays2dDoubleEq(arr3)));
ASSERT_THAT(arr2, Not(Arrays2dDoubleEq(arr4)));
}
不幸的是,我还不知道如何告诉gmock不要在Value of
和Expected
反馈字段中打印std::array
的内容。在文档中,他们提到了void PrintTo
功能,但它对我不起作用。
===编辑===
如果创建一个2D数组类而不是typedef是可以的,那么通过提供operator<<
重载:可以很容易地抑制gmock混乱的输出
template <typename T, size_t N, size_t M>
struct Array2D
{
std::array<std::array<T, M>, N> data;
};
template <typename T, size_t N, size_t M>
std::ostream& operator<<(std::ostream& os, const Array2D<T, N, M>&)
{
os << "Array2D<" << typeid(T).name() << "," << N << "," << M << ">";
return os;
}
然后,您需要稍微修改匹配器以使用data
类字段,而不是直接使用operator[]
和size()
。或者你可以为你的班级超载。
如果@JanHackenberg的评论是你想要的,那么在你的匹配器中只需设置标志result = false
而不是return
(不过我不会这么做,因为对于大数组来说,它是不可读的(。
您想要获得有关索引的信息,但似乎不想获得实际值。怎么样:
MATCHER_P(Arrays2dDoubleEq, expected, "") {
bool allMatched = true;
for (int i = 0; i < arg.size(); i++) {
for (int j = 0; j < arg[i].size(); j++) {
if(arg[i][j] != expected[i][j]) {
std::cout << "Failing at indices:" << i << ";" << j << std::endl;
allMatched = false;
}
}
}
EXPECT_THAT(allMatched, true);
return true;
}