Google test (gtest) `EXPECT_TRUE()` macro won't compile with `std::is_same<>` template as input

Solution 1:

Summary

This does NOT work, because the C++ preprocessor which processes macros was written before templates existed, and sees the comma as separating two separate arguments to the macro. It thinks I have called the EXPECT_TRUE() macro with anything<foo as the 1st argument, and bar> as the 2nd argument:

// This does NOT work, because the preprocessor sees this 1 template
// argument to the macro as two separate arguments separated by the
// comma
EXPECT_TRUE(anything<foo, bar>);

These options DO work:

// Option 1: move the template outside of the macro call
bool isSameType = std::is_same_v<decltype(var1), decltype(var2)>;
EXPECT_TRUE(isSameType);

// Option 2: for this particular case I can instead use the 
// `static_assert()` function in place of the `EXPECT_TRUE()` macro
static_assert(std::is_same_v<decltype(var1), decltype(var2)>);

// Option 3: use double parenthesis to force the macro to treat
// the parameter containing comma-separated template parameters
// as a **single argument** to the macro:
EXPECT_TRUE((std::is_same_v<decltype(var1), decltype(var2)>));

Details

After spending some time chatting with some friends, one of them, Drew Gross, explained the following:

Due to the C++ preprocessor model, commas within a template instantiation are interpreted as separating arguments of the macro, not arguments of the template. This is one of the many reasons that using macros is heavily discouraged in modern C++. So when you write:

SOME_MACRO(some_template<a, b>());

it's interpreted as passing 2 arguments to SOME_MACRO, the first being some_template<a, and the second being b>(). Since EXPECT_TRUE only accepts a single argument, this fails to compile.

In this particular case I'd recommend using static_assert instead of EXPECT_TRUE. If the thing you were testing wasn't a compile time constant, you would have to assign to a local variable first, e.g.

bool localVar = some_template<a, b>();
EXPECT_TRUE(localVar);

He's spot-on correct. Since the C and C++ macro preprocessor was written PRIOR to C++ existing, it does not recognize the C++ < and > template scoping symbols (it thinks they are just "less than" and "greater than" symbols, respectively), so in this statement (EXPECT_TRUE(std::is_same_v<decltype(var1), decltype(var2)>);), it sees the comma and parses std::is_same_v<decltype(var1) as the 1st argument to the gtest EXPECT_TRUE() macro, and decltype(var2)> as a 2nd argument to the macro.

Therefore, here's the solution:

bool isSameType = std::is_same_v<decltype(var1), decltype(var2)>;
EXPECT_TRUE(isSameType);

As Drew states, however, a better solution is to just use static_assert() in this case instead of gtest's EXPECT_TRUE(), since this test can be completed at compile-time rather than run-time:

(better solution):

static_assert(std::is_same_v<decltype(var1), decltype(var2)>);

Note: message not required for static_assert() in C++17. See here.

I did some additional research and experimenting, and also discovered that extra parenthesis solve it too. Using extra parenthesis forces the preprocessor to recognize the whole input argument as 1 argument to the macro, since the preprocessor respects parenthesis but doesn't respect template <> symbols at all since it isn't template-aware.

Therefore, this works too:

EXPECT_TRUE((std::is_same_v<decltype(var1), decltype(var2)>));

If in doubt, parenthesize it out. If in need, parenthesis, indeed. :)

So, now we have 3 viable solutions to this problem. I'd probably go with the static_assert() option as my primary solution, and the extra parenthesis option just above as my solution if I needed some template input to be tested during run-time.

Additional References:

Once I knew what the nature of the problem was (the macro preprocessor seeing the comma and not recognizing C++ template < and > scoping operators), I was able to do some Googling and find the following answers to look at too:

  1. Too many arguments provided to function-like macro invocation
  2. Getting too many arguments provided to function-like macro invocation compile error while defining lambda inside assert (assert.h) in Xcode [c++]

Keywords: macro watch out for template parameter inputs; comma argument delimiter to C/C++ macro preprocessor, c++ extra parenthesis required in macros around macro parameters