Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Undefined reference with gtest

Tags:

c++

googletest

Why does the ASSERT_EQ below cause an undefined reference to Bar::kBar error?

To compile: g++ a.cc -lgtest -lpthread

#include <gtest/gtest.h>

class Bar {
 public:
  static const size_t kBar = 0;
};

TEST(Basic, Basic) {
  ASSERT_EQ(0, Bar::kBar);
}

int main(int argc, char **argv) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
like image 973
Anuj Kalia Avatar asked Dec 24 '22 19:12

Anuj Kalia


1 Answers

From the Googletest FAQ:

The compiler complains about "undefined references" to some static const member variables, but I did define them in the class body.

If your class has a static data member:

// foo.h
class Foo {
  ...
  static const int kBar = 100;
};

You also need to define it outside of the class body in foo.cc:

const int Foo::kBar;  // No initializer here.

Otherwise your code is invalid C++, and may break in unexpected ways. In particular, using it in Google Test comparison assertions (EXPECT_EQ, etc) will generate an "undefined reference" linker error.

This explanation is rather enigmatic. (Why is it "invalid C++?") That is probably because a satisfactory explanation is rather technical.

Even though your class bar declares the static data member kBar with an initializer, that does not suffice to provide the data member with a definition that has external linkage (i.e. that the linker can see), and without one any code that attempts to odr-use1Bar::kBar will encounter an undefined reference linkage error. This can be illustrated without involving Googletest:

foobar.cpp

#include <cstdlib>

class Bar {
    public:
    static const std::size_t kBar = 0;
};

bool foo(std::size_t const & k)
{
    return k == 0;
}

int main()
{
    return foo(Bar::kBar);
}

Try to build:

$ g++ foobar.cpp
/tmp/ccWaEsDu.o: In function `main':
foobar.cpp:(.text+0x1c): undefined reference to `Bar::kBar'
collect2: error: ld returned 1 exit status

The solution is as per the FAQ:

#include <gtest/gtest.h>

class Bar {
    public:
    static const size_t kBar = 0;
};

const size_t Bar::kBar;

TEST(Basic, Basic) {
  ASSERT_EQ(0, Bar::kBar);
}

int main(int argc, char **argv) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

As of C++17 you will be able to omit the out-of-class definition if you prefix the initialized in-class declaration with inline (which will make it a definition).


[1] [ODR-use](http://en.cppreference.com/w/cpp/language/definition) >Informally, an object is odr-used if its address is taken, or a reference is >bound to it, and a function is odr-used if a function call to it is made or its >address is taken. If an object or a function is odr-used, its definition must >exist somewhere in the program; a violation of that is a link-time error.
like image 81
Mike Kinghan Avatar answered Jan 04 '23 06:01

Mike Kinghan