2017-01-16 11 views
2

Ich habe diesen Fehler beim Versuch, mit Std :: Array in googletest arbeiten. Im Anschluss ist ein minimales Beispiel, das diesen Fehler erzeugt:Compilation Fehler mit googletest EXPECT_NO_THROW mit 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(); 
} 

Ich benutzen den current googletest Code von GitHub. Um & make googletest zu machen.

Als Compiler habe ich clang3.8 auf Ubuntu 14.04 LTS verwendet.

den follownig Befehl:

clang++ -std=c++11 -o arr arr.cpp 

Ergebnisse in:

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. 

die EXPECT_NO_THROW Makro aus- und einfach das Array deklarieren kompiliert in Ordnung. Ist etwas offensichtlich, das ich vermisse oder sollte ich einen Fehlerbericht auf GitHub einreichen?

+2

Makros verstehen nicht C++, und vor allem '<,>' Vorlagen und Kommas innerhalb der Vorlage Argumente. Diese Kommas werden als mehrere Argumente für das Makro geparst. Als erster Durchgang, ' Verwenden Sie uchar_16_array = std :: array 'und verwenden Sie' uchar_16_array' im Makro Ich nehme an, dass es die Initialisierungsliste versteht (kann aber nicht garantieren) – Yakk

+2

Sie können einen anderen Satz Klammern verwenden, um das Problem zu umgehen Yakk erwähnt: 'EXPECT_NO_THROW ((...));' Dies wird jeden Code in der '...' als ein einziges Argument an das Makro übergeben. – dyp

+1

@dyp, Leider sind die zusätzlichen Parens noch durchgeführt, so ist es möglich, dass sie den Ersatztext brechen, wenn das Makro sie nicht behandelt – chris

Antwort

6

Die EXPECT_NO_THROW ein Makro wie folgt definiert ist:

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

Wie Sie sehen, ist dies ein funktionsähnliches Makro, das ein Argument benötigt. Der Präprozessor (der sich mit Makros beschäftigt) arbeitet mit Token. Es versteht weder C++ noch C, sondern nur seine eigene Token-Sprache. (Heutzutage erfolgt das Kompilieren und Vorverarbeiten offensichtlich in einer Stufe, aber ich beziehe mich auf die Semantik der Präprozessorsprache.)

Der Präprozessor erwartet ein einzelnes Argument für EXPECT_NO_THROW. Es trennt Argumente für funktionsähnliche Makros, indem es nach Kommas sucht.Also, wenn es eine Liste von Token in der Liste der Argumente für eine funktionsähnliche Makro sieht wie:

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

dann trennt es die Argumentliste in Argumente wie folgt:

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

Und das sind natürlich mehrere Argumente, wo man für die Funktion wie Makro EXPECT_NO_THROW erwartet wird.


Um mehrere Vorverarbeitung Token einschließlich , passieren als ein einziges Argument an eine funktionsähnlichen Makros, können Sie diese Tokens in Klammern setzen können:

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

Dies wird jedoch nicht kompilieren:

Das EXPECT_NO_THROW Makro wird wie folgt ergänzt:

#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.") 

Wo der unerreichbaren Code Makro wie folgt definiert ist:

#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ 
    if (::testing::internal::AlwaysTrue()) { statement; } 

Also, wenn Sie eine Anweisung STMT innerhalb des EXPECT_NO_THROW Makro setzen, werden Sie am Ende mit:

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

Deshalb, wenn Sie setzen (STMT;) in EXPECT_NO_THROW, beenden Sie mit einem Line-up

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

Der Teil (STMT;); ist n Rechtliches C++. Weder ist (STMT);, wenn das STMT eine Erklärung wie im OP ist.

Wenn Sie ({STMT;}) in das Makro übergeben, erhalten Sie am Ende ({STMT;});, die in C++ immer noch illegal ist, aber in g ++ als Erweiterung zulässig ist; Es ist eine Ausdruck-Anweisung. Hier wird der {STMT;} Teil als ein Ausdruck interpretiert, der in Klammern eingeschlossen ist, um den Ausdruck ({STMT;}) zu bilden.

Sie können auch versuchen, die Kommata zu isolieren. Wie in einem Kommentar zum OP auf Yakk hingewiesen, können Sie das Komma in der Template-Argumentenliste mit einem typedef verbergen; die restlichen Kommas in der Initialisierer-Liste kann unter Verwendung eines temporären, beispielsweise gewickelt werden:

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

Während die ursprüngliche EXPECT_NO_THROW(STMT) erlaubt STMT eine Aussage, Aussagen in C++ sein kann nicht beliebig in Klammern gesetzt werden . Ausdrücke können jedoch beliebig in Klammern eingeschlossen werden, und Ausdrücke können als Anweisung verwendet werden. Aus diesem Grund funktioniert die Übergabe der Anweisung als Ausdruck-Anweisung.Wenn wir unsere Array-Deklaration als Ausdruck formulieren können, wird dies das Problem lösen:

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

Hinweis dies ein temporäres Array erzeugt; Dies ist keine Deklarationsanweisung wie im OP, sondern ein einzelner Ausdruck.

Aber es ist vielleicht nicht immer so einfach, einen Ausdruck der Dinge zu erzeugen, die wir testen wollen. Im Standard C++ gibt es jedoch einen Ausdruck, der Anweisungen enthalten kann: Ein Lambda-Ausdruck.

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

Bitte beachten Sie die () nach dem Lambda was wichtig ist, um innerhalb der lamdba die Anweisung tatsächlich ausgeführt werden! Dies zu vergessen ist eine sehr subtile Quelle von Fehlern :(

+1

Sobald wir die ['source_location'-Klasse] (http://en.cppreference.com/w/cpp/experimental/source_location) erhalten haben, können wir dieses Chaos beseitigen, indem wir einfach das Lambda an eine Funktionsvorlage übergeben. Dies beseitigt auch das explizite manuelle '()', um das Lambda aufzurufen. – dyp

1

So wie viele von Ihnen in den Kommentaren darauf hingewiesen, dass Makros und Vorlage nicht gut zusammen arbeiten. Ist diese Beschränkung des Google-Tests in den Dokumenten dokumentiert? Ich kann nichts finden, was auf eine solche Beschränkung hindeutet.

Wenn Sie mehr Abhilfen zu tun haben, gefallen sie bieten und ich werde sie auf meine Antwort hinzufügen, der ich Ihnen eine wirkliche Antwort haben Sie bitte teilen :)

Im folgenden habe ich versucht, die vorgeschlagenen Lösungen und kompiliert sie verwenden:

clang++ -std=c++11 -o arr arr.cpp -lgtest_main -lgtest -lpthread 

zusätzliche Klammern um das Argument EXPECT_NO_THROW tut Arbeit:

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

Stellen Sie eine using-Deklaration ist nicht:

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

Da es immer noch in der gleichen Fehlermeldung führt:

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. 
+0

Ich glaube, der Grund, warum die using-Deklaration nicht funktioniert, ist, dass '{' im Präprozessor keine spezielle Bedeutung hat, daher wird bis zum ersten ',' als erstes Makro-Argument betrachtet. Andererseits haben '(' und ')' im Präprozessor eine besondere Bedeutung (speziell bei der Makroersetzung): Die Trennung der Argumente für das Makro sucht nach '' ', außer in Klammern. – dyp