c++ - 使用 googletest 的 EXPECT_NO_THROW 和 std::array 的编译错误

标签 c++ c++11 googletest stdarray

我在 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();
}

我使用了 current googletest来自github的代码。制作并安装 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 上提交错误报告吗?

最佳答案

EXPECT_NO_THROW是一个定义如下的宏:

#define EXPECT_NO_THROW(statement) \
  GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_)

如您所见,这是一个类似函数的宏,它接受一个参数。预处理器(处理宏)处理标记。它不理解 C++,也不理解 C,只理解它自己的 token 语言。 (如今,编译和预处理显然在一个阶段发生,但我指的是预处理器语言的语义。)

预处理器需要一个参数 EXPECT_NO_THROW .它通过查找逗号来分隔函数式宏的参数。因此,当它在类似函数的宏的参数列表中看到标记列表时,例如:

EXPECT_NO_THROW( const std::array<unsigned char, 16> foo = {1, 2, 3}; )

然后它将参数列表分成如下参数:

  • const std::array<unsigned char
  • 16> foo = {1
  • 2
  • 3};

这些当然是多个参数,其中一个是函数式宏 EXPECT_NO_THROW 所期望的。 .


为了传递几个预处理 token ,包括 ,作为类似函数的宏的单个参数,您可以将这些标记括在括号中:

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; }

因此,如果您声明 STMTEXPECT_NO_THROW里面宏,你最终会得到:

  if (::testing::internal::AlwaysTrue()) {
    try {
      if (::testing::internal::AlwaysTrue()) { STMT; };
    }
  // ...

因此,如果你输入 (STMT;)进入EXPECT_NO_THROW , 你最终得到一条线

if (::testing::internal::AlwaysTrue()) { (STMT;); };

部分(STMT;);不是合法的 C++。 (STMT); 也不是如果那个STMT是 OP 中的声明。

如果你通过 ({STMT;})进入宏,你最终会得到 ({STMT;});这在 C++ 中仍然是非法的,但在 g++ 中允许作为扩展;这是一个表达式语句。在这里,{STMT;}部分被解释为表达式,括在括号中以形成表达式 ({STMT;}) .

您也可以尝试隔离逗号。作为Yakk在对 OP 的评论中指出,您可以使用 typedef 隐藏模板参数列表中的逗号;初始化列表中的剩余逗号可以使用临时的包装,例如:

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++中的语句不能随意用括号括起来。但是表达式可以任意括在括号中,表达式可以用作语句。这就是将语句作为表达式语句传递的原因。如果我们可以将我们的数组声明表述为表达式,这将解决问题:

EXPECT_NO_THROW(( std::array<unsigned char, 16>{1, 2, 3} ));

注意这创建了一个临时数组;这不是 OP 中的声明语句,而是单个表达式。

但是创建我们想要测试的事物的表达式可能并不总是那么简单。但是,标准 C++ 中有一个表达式可以包含语句:lambda 表达式。

EXPECT_NO_THROW(( []{ const std::array<unsigned char, 16> foo = {1, 2, 3}; }() ));

请注意 ()在 lambda 之后,这对于在 lamdba 中实际执行语句很重要!忘记这是一个非常微妙的错误来源:(

关于c++ - 使用 googletest 的 EXPECT_NO_THROW 和 std::array 的编译错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41681243/

相关文章:

c++ - block 范围内 C++11 函数声明的用例?

gdb - gtest : where to put gdb breakpoint

c++ - 名称查找 "in the class of the object expression"是否应该找到该类的成员?

c++ - 为什么 lexical_cast 要求运算符>>位于匹配的 namespace 中?

c++ - 使用 sse 和 avx 内在函数将一组打包的单打添加到一个值中

c++ - 字符串数组上的 Sizeof 运算符在 C++ 中给出不同的输出

c++ - 谷歌测试框架 : is better to use shadowing or virtual methods?

c++ - 内联函数的gtest问题

c++ - 如何在 C++ 中将纬度的小数部分存储到 4 个无符号字符中

c++ - 使用重载的 new 和 delete 运算符跟踪已用内存?