Student
Student

Reputation: 13

GTest's EXPECT_EQ giving undefined reference to error

#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 toArrayParam<3>::_length' collect2: error: ld returned 1 exit status

Upvotes: 0

Views: 1867

Answers (1)

Yksisarvinen
Yksisarvinen

Reputation: 22394

Explanation

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.

Solution

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

Related Questions