I'm writing a type-parameterized test fixture using gtest, and I'm seeing a not declared in this scope
compiler error when I try to use members from the fixture class.
When I'm not using a type-parameterized fixture, I can access class members just fine. When I am using a type-parameterized fixture, I can use this->(member name)
to access the member. But why can't I use the member name explicitly? Is there a way I can avoid littering my test code with this->
all over the place?
Here's a trivial reproduction:
Test_Sanity.h:
#include "gtest/gtest.h"
/* Using a member in a simple, non-paramed fixture: */
class SimpleFixture : public ::testing::Test {
protected:
int N;
};
TEST_F(SimpleFixture, FooTest) {
N=6;
ASSERT_LE(0,N);
}
/* Using a member in a type-parameterized fixture: */
template <typename PARAM_T>
class TypeParamFixture : public ::testing::Test {
protected:
int N;
};
TYPED_TEST_CASE_P(TypeParamFixture);
TYPED_TEST_P(TypeParamFixture, FooTest) {
N=6; /* COMPILE-ERROR: ‘N’ was not declared in this scope */
ASSERT_LE(0,N);
}
/* As above, but using this->N */
TYPED_TEST_P(TypeParamFixture, FooTestUsingThisPtr) {
this->N=6; /* No compilation error */
ASSERT_LE(0,this->N);
}
/* Registration and instantiation of type-paramed tests */
REGISTER_TYPED_TEST_CASE_P(TypeParamFixture, FooTest, FooTestUsingThisPtr);
struct StructA {
int myInt;
double myDouble;
};
struct StructB {
char myCharArr[42];
};
typedef ::testing::Types<StructA, StructB> MyTypes;
INSTANTIATE_TYPED_TEST_CASE_P(Sanity, TypeParamFixture, MyTypes );
I compile the above with the boilerplate gtest main, and get a compilation error only for the use of N
in TypeParamFixture/FooTest
.
After pre-processing, your type-parameterized test:
TYPED_TEST_P(TypeParamFixture, FooTest) {
N=6; /* COMPILE-ERROR: ‘N’ was not declared in this scope */
ASSERT_LE(0,N);
}
turns into:
namespace gtest_case_TypeParamFixture_ {
template <typename gtest_TypeParam_>
class FooTest : public TypeParamFixture<gtest_TypeParam_>
{
private:
typedef TypeParamFixture<gtest_TypeParam_> TestFixture;
typedef gtest_TypeParam_ TypeParam; virtual void TestBody();
};
static bool gtest_FooTest_defined_ __attribute__ ((unused)) =
gtest_typed_test_case_p_state_TypeParamFixture_.AddTestName(
"gtest.cpp", 22, "TypeParamFixture", "FooTest");
}
template <typename gtest_TypeParam_>
void gtest_case_TypeParamFixture_::FooTest<gtest_TypeParam_>::TestBody()
{
N=6; //<- Won't compile
switch (0) case 0: default:
if (const ::testing::AssertionResult gtest_ar =
(::testing::internal::CmpHelperLE("0", "N", 0, N))) ;
else return ::testing::internal::AssertHelper(
::testing::TestPartResult::kFatalFailure,
"gtest.cpp",
24,
gtest_ar.failure_message()
) = ::testing::Message();
}
The key point about this is that your typed-parameterized test becomes a class template that inherits your type-parameterized test fixture.
In that light, the following very simple example illustrates the problem:
template<typename T>
struct my_base
{
my_base() : N(0){}
int N;
int bar() const {
return N; //<- This will compile
}
};
template<typename T>
struct my_derived : my_base<T>
{
int foo() {
// return N; // <- This will not compile
return this->N; //<- This will compile
}
};
int main()
{
my_derived<int> d;
return d.foo() + d.bar();
}
The lesson is that within the implementation of a class template
derived from a class template, members of the base template must
be referred to via the this
pointer. That is just an inconvenient
fact about the C++ template mechanism.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With