thumper
thumper

Reputation: 423

Googletest does not accept temporary object in EXPECT_THROW

I have a class that has no default constructor, but the constructor may throw. I was wanting to have a test like:

EXPECT_THROW(MyClass(param), std::runtime_error);

But the compiler, g++, complains that there is no default constructor for MyClass. However, the following...

EXPECT_THROW(MyClass foo(param), std::runtime_error);

...works, and the test passes as expected. Why though won't Googletest accept the temporary object?

class MyClass
{
public:
  MyClass(std::string const& filename);
  //...
};

Interestingly enough, I had refactored my test to not have the filename in a separate variable, and when asked to check I found the following works:

EXPECT_THROW(MyClass("somefilename"), std::runtime_error);

However the following doesn't:

std::string filename("somefilename");
EXPECT_THROW(MyClass(filename), std::runtime_error);

Upvotes: 16

Views: 15527

Answers (3)

Bulletmagnet
Bulletmagnet

Reputation: 6010

If you're hit with the "most vexing parse", the cure is often uniform initialization:

EXPECT_THROW(MyClass{param}, std::runtime_error);

(assuming your compiler understands C++11).

Upvotes: 18

kwesolowski
kwesolowski

Reputation: 724

When dealing with macros, ultimate tool is analyzing expanded macro:

In your case (and for gtest 1.6):

EXPECT_THROW(MyClass(filename), std::runtime_error);

Expands to:

...
    bool gtest_caught_expected = false; \
    try { \
      if (::testing::internal::AlwaysTrue()) { MyClass(filename); }; \
    } \
    catch (std::runtime_error const&) { \
      gtest_caught_expected = true; \
    } \
    catch (...) { \
      gtest_msg.value = \
          "Expected: " "MyClass(filename)" " throws an exception of type " \
          "std::runtime_error" ".\n  Actual: it throws a different type."; \
      goto gtest_label_testthrow_88; \
    } \
    if (!gtest_caught_expected) { \
      gtest_msg.value = \
          "Expected: " "MyClass(filename)" " throws an exception of type " \
          "std::runtime_error" ".\n  Actual: it throws nothing."; \
      goto gtest_label_testthrow_88; \
    } \
...

As you can see, argument to EXPECT_THROW is not object, but expression to be evaluated further, within GTEST provided valid try/catch block.

So anything you pass to it, must be able to evaluate as expression within nested scope of current scope. In your case:

MyClass(filename)

Is ambiguous, but according to Most vexing parse rule, declaration interpretation is preferred, so you end up with:

MyClass filename

So you are creating variable named filename of class MyClass - and hence error about missing constructor.

This mechanism is not triggered if you use literal string:

MyClass("some string")

Becouse following would be invalid (and there is no ambiguity):

MyClass "some string"

Upvotes: 8

Marty B
Marty B

Reputation: 243

Can you give more information? I constructed an example which works fine with a class that only has a one argument constructor.

#include <iostream>
#include <stdexcept>

#include "gtest/gtest.h"

class m {
    public:
        m(std::string a) {std::cout << "one argument constructor" << std::endl;}
};

int main() {
    EXPECT_THROW(m("hat"), std::runtime_error);
}

Output:

one argument constructor
gtt.cc:12: Failure
Expected: m("hat") throws an exception of type std::runtime_error.
Actual: it throws nothing.

EDIT I do not claim to be an expert on the arcane inner-workings of the C/C++ pre-processor, but I think this has to do with the rules followed when evaluating expressions, in particular, in the land of the pre-processor, parentheses are king. When the pre-processor evaluates MyClass(filename) it first evaluates filename which produces a temporary value which is immediately discarded, it then evaluates MyClass(). Calling MyClass("filename") causes the pre-processor to actually copy the literal string into the expression. One way around this problem is to call EXPECT_THROW((MyClass(filename)), std::runtime_error), i.e. use a set of enclosing parentheses around your statement.

Upvotes: 2

Related Questions