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 beingsome_template<a
, and the second beingb>()
. SinceEXPECT_TRUE
only accepts a single argument, this fails to compile.In this particular case I'd recommend using
static_assert
instead ofEXPECT_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:
- Too many arguments provided to function-like macro invocation
- 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