使用 googletest 的 EXPECT_NO_THROW 和 std::array 的编译错误
Compilation error using googletest's EXPECT_NO_THROW with std::array
我在 googletest 中尝试使用 std::array 时遇到此错误。以下是产生此错误的最小示例:
arr.cpp
#include "gtest/gtest.h"
#include <array>
TEST(Test, Positive) {
EXPECT_NO_THROW({
const std::array<unsigned char, 16> foo = {1, 2, 3};
});
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
我使用了 github 中的 current googletest 代码。制作并安装 googletest。
作为编译器,我在 Ubuntu 14.04 LTS 上使用了 clang3.8。
使用以下命令:
clang++ -std=c++11 -o arr arr.cpp
结果:
arr.cpp:6:41: error: too many arguments provided to function-like macro invocation
const std::array<unsigned char, 16> blasdasd = {1, 2, 3};
^
/usr/local/include/gtest/gtest.h:1845:9: note: macro 'EXPECT_NO_THROW' defined here
#define EXPECT_NO_THROW(statement) \
^
arr.cpp:5:5: note: cannot use initializer list at the beginning of a macro argument
EXPECT_NO_THROW({
^ ~
arr.cpp:5:5: error: use of undeclared identifier 'EXPECT_NO_THROW'
EXPECT_NO_THROW({
^
2 errors generated.
删除 EXPECT_NO_THROW 宏并简单地声明数组编译正常。有什么明显的我遗漏的东西或者我应该在 github 上提交错误报告吗?
正如你们中的许多人在评论中指出的那样,宏和模板不能很好地协同工作。文档中是否记录了 googletest 的这种限制?我找不到任何暗示这种限制的东西。
如果您确实有更多解决方法,请提供它们,我会将它们添加到我的答案中,如果您确实有真正的答案,请分享:)
在下文中,我尝试了建议的解决方案,并使用以下方法对其进行了编译:
clang++ -std=c++11 -o arr arr.cpp -lgtest_main -lgtest -lpthread
EXPECT_NO_THROW 参数周围的额外大括号 有效 :
#include "gtest/gtest.h"
#include <array>
TEST(Test, Positive) {
EXPECT_NO_THROW(({
const std::array<unsigned char, 16> foo = {1, 2, 3};
}));
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
提供 using 声明不起作用:
#include "gtest/gtest.h"
#include <array>
using uchar_16_arr = std::array<unsigned char, 16>;
TEST(Test, Positive) {
EXPECT_NO_THROW({
const uchar_16_arr foo = {1, 2, 3};
});
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
因为它仍然会导致相同的错误消息:
arr.cpp:8:38: error: too many arguments provided to function-like macro invocation
const uchar_16_arr foo = {1, 2, 3};
^
/usr/local/include/gtest/gtest.h:1845:9: note: macro 'EXPECT_NO_THROW' defined here
#define EXPECT_NO_THROW(statement) \
^
arr.cpp:7:5: note: cannot use initializer list at the beginning of a macro argument
EXPECT_NO_THROW({
^ ~
arr.cpp:7:5: error: use of undeclared identifier 'EXPECT_NO_THROW'
EXPECT_NO_THROW({
^
2 errors generated.
EXPECT_NO_THROW
是一个宏,定义如下:
#define EXPECT_NO_THROW(statement) \
GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_)
如您所见,这是一个带有一个参数的 function-like 宏。预处理器(处理宏)处理标记。它不理解 C++,也不理解 C,只理解它自己的令牌语言。 (如今,编译和预处理显然在一个阶段发生,但我指的是预处理器语言的语义。)
预处理器需要 EXPECT_NO_THROW
的单个参数。它通过查找逗号来分隔 function-like 宏的参数。因此,当它在 function-like 宏的参数列表中看到标记列表时,例如:
EXPECT_NO_THROW( const std::array<unsigned char, 16> foo = {1, 2, 3}; )
然后它将参数列表分成如下参数:
const std::array<unsigned char
16> foo = {1
2
3};
这些当然是多个参数,function-like 宏 EXPECT_NO_THROW
.
需要一个参数
为了将包括 ,
在内的多个预处理标记作为单个参数传递给 function-like 宏,您可以将这些标记括在括号中:
EXPECT_NO_THROW( (const std::array<unsigned char, 16> foo = {1, 2, 3};) );
然而,这不会编译:
EXPECT_NO_THROW
宏扩展如下:
#define GTEST_TEST_NO_THROW_(statement, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::internal::AlwaysTrue()) { \
try { \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
} \
catch (...) { \
goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \
} \
} else \
GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \
fail("Expected: " #statement " doesn't throw an exception.\n" \
" Actual: it throws.")
其中不可达代码宏定义如下:
#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \
if (::testing::internal::AlwaysTrue()) { statement; }
因此,如果您在 EXPECT_NO_THROW
宏中放置一个语句 STMT
,您将得到:
if (::testing::internal::AlwaysTrue()) {
try {
if (::testing::internal::AlwaysTrue()) { STMT; };
}
// ...
因此,如果您将 (STMT;)
放入 EXPECT_NO_THROW
,您最终会得到一行
if (::testing::internal::AlwaysTrue()) { (STMT;); };
(STMT;);
部分不是合法的 C++。如果 STMT
是 OP 中的声明,则 (STMT);
也不是。
如果你将 ({STMT;})
传递给宏,你将得到 ({STMT;});
,这在 C++ 中仍然是非法的,但在 g++ 中作为扩展是允许的;这是一个 expression-statement。这里,{STMT;}
部分被解释为一个表达式,括在括号中形成表达式 ({STMT;})
.
您也可以尝试隔离逗号。正如 Yakk 在对 OP 的评论中指出的那样,您可以使用 typedef 隐藏 template-argument 列表中的逗号; initializer-list 中剩余的逗号可以使用临时值换行,例如:
using array_t = std::array<unsigned char, 16>;
EXPECT_NO_THROW( const array_t foo = (array_t{1, 2, 3}); );
虽然原来的EXPECT_NO_THROW(STMT)
确实允许STMT
是一个语句,但C++中的语句不能随意括在括号中。但是表达式可以任意括在括号中,表达式可以用作语句。这就是为什么将语句作为 expression-statement 传递的原因。如果我们可以将我们的数组声明表述为表达式,这将解决问题:
EXPECT_NO_THROW(( std::array<unsigned char, 16>{1, 2, 3} ));
注意这创建了一个临时数组;这不是 OP 中的 declaration-statement,而是单个表达式。
但是创建我们想要测试的事物的表达式可能并不总是那么简单。但是,标准 C++ 中有一个表达式可以包含语句:A lambda-expression.
EXPECT_NO_THROW(( []{ const std::array<unsigned char, 16> foo = {1, 2, 3}; }() ));
请注意 lambda 之后的 ()
,这对于在 lamdba 中实际执行语句很重要!忘记这是一个非常微妙的错误来源:(
我在 googletest 中尝试使用 std::array 时遇到此错误。以下是产生此错误的最小示例:
arr.cpp
#include "gtest/gtest.h"
#include <array>
TEST(Test, Positive) {
EXPECT_NO_THROW({
const std::array<unsigned char, 16> foo = {1, 2, 3};
});
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
我使用了 github 中的 current googletest 代码。制作并安装 googletest。
作为编译器,我在 Ubuntu 14.04 LTS 上使用了 clang3.8。
使用以下命令:
clang++ -std=c++11 -o arr arr.cpp
结果:
arr.cpp:6:41: error: too many arguments provided to function-like macro invocation
const std::array<unsigned char, 16> blasdasd = {1, 2, 3};
^
/usr/local/include/gtest/gtest.h:1845:9: note: macro 'EXPECT_NO_THROW' defined here
#define EXPECT_NO_THROW(statement) \
^
arr.cpp:5:5: note: cannot use initializer list at the beginning of a macro argument
EXPECT_NO_THROW({
^ ~
arr.cpp:5:5: error: use of undeclared identifier 'EXPECT_NO_THROW'
EXPECT_NO_THROW({
^
2 errors generated.
删除 EXPECT_NO_THROW 宏并简单地声明数组编译正常。有什么明显的我遗漏的东西或者我应该在 github 上提交错误报告吗?
正如你们中的许多人在评论中指出的那样,宏和模板不能很好地协同工作。文档中是否记录了 googletest 的这种限制?我找不到任何暗示这种限制的东西。
如果您确实有更多解决方法,请提供它们,我会将它们添加到我的答案中,如果您确实有真正的答案,请分享:)
在下文中,我尝试了建议的解决方案,并使用以下方法对其进行了编译:
clang++ -std=c++11 -o arr arr.cpp -lgtest_main -lgtest -lpthread
EXPECT_NO_THROW 参数周围的额外大括号 有效 :
#include "gtest/gtest.h"
#include <array>
TEST(Test, Positive) {
EXPECT_NO_THROW(({
const std::array<unsigned char, 16> foo = {1, 2, 3};
}));
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
提供 using 声明不起作用:
#include "gtest/gtest.h"
#include <array>
using uchar_16_arr = std::array<unsigned char, 16>;
TEST(Test, Positive) {
EXPECT_NO_THROW({
const uchar_16_arr foo = {1, 2, 3};
});
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
因为它仍然会导致相同的错误消息:
arr.cpp:8:38: error: too many arguments provided to function-like macro invocation
const uchar_16_arr foo = {1, 2, 3};
^
/usr/local/include/gtest/gtest.h:1845:9: note: macro 'EXPECT_NO_THROW' defined here
#define EXPECT_NO_THROW(statement) \
^
arr.cpp:7:5: note: cannot use initializer list at the beginning of a macro argument
EXPECT_NO_THROW({
^ ~
arr.cpp:7:5: error: use of undeclared identifier 'EXPECT_NO_THROW'
EXPECT_NO_THROW({
^
2 errors generated.
EXPECT_NO_THROW
是一个宏,定义如下:
#define EXPECT_NO_THROW(statement) \
GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_)
如您所见,这是一个带有一个参数的 function-like 宏。预处理器(处理宏)处理标记。它不理解 C++,也不理解 C,只理解它自己的令牌语言。 (如今,编译和预处理显然在一个阶段发生,但我指的是预处理器语言的语义。)
预处理器需要 EXPECT_NO_THROW
的单个参数。它通过查找逗号来分隔 function-like 宏的参数。因此,当它在 function-like 宏的参数列表中看到标记列表时,例如:
EXPECT_NO_THROW( const std::array<unsigned char, 16> foo = {1, 2, 3}; )
然后它将参数列表分成如下参数:
const std::array<unsigned char
16> foo = {1
2
3};
这些当然是多个参数,function-like 宏 EXPECT_NO_THROW
.
为了将包括 ,
在内的多个预处理标记作为单个参数传递给 function-like 宏,您可以将这些标记括在括号中:
EXPECT_NO_THROW( (const std::array<unsigned char, 16> foo = {1, 2, 3};) );
然而,这不会编译:
EXPECT_NO_THROW
宏扩展如下:
#define GTEST_TEST_NO_THROW_(statement, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::internal::AlwaysTrue()) { \
try { \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
} \
catch (...) { \
goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \
} \
} else \
GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \
fail("Expected: " #statement " doesn't throw an exception.\n" \
" Actual: it throws.")
其中不可达代码宏定义如下:
#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \
if (::testing::internal::AlwaysTrue()) { statement; }
因此,如果您在 EXPECT_NO_THROW
宏中放置一个语句 STMT
,您将得到:
if (::testing::internal::AlwaysTrue()) {
try {
if (::testing::internal::AlwaysTrue()) { STMT; };
}
// ...
因此,如果您将 (STMT;)
放入 EXPECT_NO_THROW
,您最终会得到一行
if (::testing::internal::AlwaysTrue()) { (STMT;); };
(STMT;);
部分不是合法的 C++。如果 STMT
是 OP 中的声明,则 (STMT);
也不是。
如果你将 ({STMT;})
传递给宏,你将得到 ({STMT;});
,这在 C++ 中仍然是非法的,但在 g++ 中作为扩展是允许的;这是一个 expression-statement。这里,{STMT;}
部分被解释为一个表达式,括在括号中形成表达式 ({STMT;})
.
您也可以尝试隔离逗号。正如 Yakk 在对 OP 的评论中指出的那样,您可以使用 typedef 隐藏 template-argument 列表中的逗号; initializer-list 中剩余的逗号可以使用临时值换行,例如:
using array_t = std::array<unsigned char, 16>;
EXPECT_NO_THROW( const array_t foo = (array_t{1, 2, 3}); );
虽然原来的EXPECT_NO_THROW(STMT)
确实允许STMT
是一个语句,但C++中的语句不能随意括在括号中。但是表达式可以任意括在括号中,表达式可以用作语句。这就是为什么将语句作为 expression-statement 传递的原因。如果我们可以将我们的数组声明表述为表达式,这将解决问题:
EXPECT_NO_THROW(( std::array<unsigned char, 16>{1, 2, 3} ));
注意这创建了一个临时数组;这不是 OP 中的 declaration-statement,而是单个表达式。
但是创建我们想要测试的事物的表达式可能并不总是那么简单。但是,标准 C++ 中有一个表达式可以包含语句:A lambda-expression.
EXPECT_NO_THROW(( []{ const std::array<unsigned char, 16> foo = {1, 2, 3}; }() ));
请注意 lambda 之后的 ()
,这对于在 lamdba 中实际执行语句很重要!忘记这是一个非常微妙的错误来源:(