Reputation: 13
#include <gtest/gtest.h>
template<typename T, size_t N>
size_t getSize(T (&arr)[N]){
return N;
}
template<int N>
struct ArrayParam {
static const int _length = N;
int _arr[N];
};
ArrayParam<3> ap1 = {{1,2,3}};
//ArrayParam<4> ap2 = {{1,2,3,4}};
class ParamTest: public ::testing::TestWithParam<ArrayParam<3>>{};
TEST_P(ParamTest, SizeTest){
ArrayParam<3> param = GetParam();
printf("\nparam._length == %d\n",param._length); //OK
printf("\nValue2 == %d\n",ArrayParam<3>::_length); //OK
//EXPECT_EQ(param._length,getSize(param._arr)); //NOT OK
//EXPECT_EQ(3,param._length); // NOT OK
EXPECT_EQ(3,sizeof(param._arr)/sizeof(int)); //OK
}
INSTANTIATE_TEST_CASE_P(ArraySize,ParamTest,testing::Values(ap1));
Does someone know why EXPECT_EQ doesn't work as printf when I try to access _length
?
My ultimate goal is to write a single test for various ArrayParam<T>
instance objects such as ArrayParam<4> ap2
, ArrayParam<5> ap3
, and so on.
Error I'm getting:
~/tests.cpp.o: In function ParamTest_SizeTest_Test::TestBody()': ~/tests.cpp: undefined reference to
ArrayParam<3>::_length' collect2: error: ld returned 1 exit status
Upvotes: 0
Views: 1867
Reputation: 22394
In general, static data members in C++ require definition outside of the class, like this:
struct A {
static int myInt;
};
A::myInt; //doesn't even have to be initialized
Const and non-volatile members are special, as can be seen in the reference. They can be initialized with any constant expression inside the class body:
struct A {
static const int myInt = 1;
};
int main() {
std::cout << A::myInt;
}
But, there is one exception to this rule (from the same paragraph in cppreference, emphasis mine):
If a const [non-inline (since C++17)] static data member [or a constexpr static data member (since C++11)] is odr-used, a definition at namespace scope is still required, but it cannot have an initializer. This definition is deprecated for constexpr data members (since C++17).
odr-used is explained as such (emphasis mine):
Informally, an object is odr-used if its value is read (unless it is a compile time constant) or written, its address is taken, or a reference is bound to it; a reference is odr-used if it is used and its referent is not known at compile time; and a function is odr-used if a function call to it is made or its address is taken. If an object, a reference or a function is odr-used, its definition must exist somewhere in the program; a violation of that is usually a link-time error.
This is exactly what happens here. EXPECT_EQ
takes parameters by const T&
, i.e. binds a reference to this type. And since a reference is bound to _length
, it makes it odr-used
and requires an out-of-class member definition.
odr-used
exception doesn't apply to printf
, because printf
(being a C function) doesn't take a reference. It falls into "read (unless it is a compile time constant)" part of the definition. And since we have a compile time constant, everything works fine.
If you are using C++17, it's as simple as changing const
to constexpr
:
template<int N>
struct ArrayParam<N> {
static constexpr int _length = N;
int _arr[N];
};
C++17 standard made definition for constexpr static
members at namespace scope deprecated (you not only don't have to use it, you actually shouldn't use it).
If you are not using C++17, you have to add a definition of this data member in the same namespace as the class:
template<int N>
struct ArrayParam<N> {
static constexpr int _length = N; //const is fine as well
int _arr[N];
};
template<int N>
constexpr int ArrayParam<N>::_length;
This will allow you to use it with GoogleTest's EXPECT_EQ
As a side note, I'd like to recommend using std::array
one more time. It's more readable and easily recognized by all C++ programmers.
As most standard containers, it's efficient and well written by people with great experience. And it was tested and proven fine by countless programmers using that before you.
Upvotes: 3