-1

I have a couple global pointers defined in a UtTestBase.hpp which are to be used by the files that include it (e.g: UtTest1.cpp).

SetUpTestSuite() is static and thus performs a shared set up for all the tests. I view it as preconfiguration. Doing the way I have would complain about multiple definitions of the globals however using extern for each doesn't work either. Using extern errors out

namespace TEST
{
extern std::weak_ptr<int> weakPtr;
extern std::shared_ptr<int> sharedInt;
// ...
}

// errors
undefined reference to `weakPtr' in line "weakPtr = sharedInt;"
undefined reference to `sharedInt' in line "sharedInt = std::make_shared<int>();"

From what I have seen the usage of extern involves declaring the variables in a header and defining in a respective source file that uses it

What's the way around? static inline is other option but does it make sense for each file to have a separate instance of a global given they are assigned in SetUpTestSuite()?

UtTestBase.hpp

namespace TEST
{
std::weak_ptr<int> weakPtr;
std::shared_ptr<int> sharedInt;

struct UtTestBase
{
  static void SetUpTestSuite()
  {
     sharedInt = std::make_shared<int>();
     weakPtr = sharedInt;
  }
  // .. some common methods
};
}

UtTestBase.cpp

#include "UtTestBase.hpp"

namespace TEST
{
TEST_F(UtTestBase, Test1)
{
  // ...
}
}

UtTest1.cpp

#include "UtTestBase.hpp"

namespace TEST
{
struct UtTest1 : public UtTestBase
{
  void SetUp() override
  {
    // do init for a local test
  }
  // should have sharedInt and weakPtr assigned
};
}
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
xyf
  • 664
  • 1
  • 6
  • 16
  • Since you derive from UTTestBase, why not make them static members of the UTTestBase class? – Pepijn Kramer Jan 16 '23 at 16:59
  • I'd ideally make them member variables and have child classes access them but it's just `SetUpTestSuite()` is static – xyf Jan 16 '23 at 17:01
  • Yes but static methods can access static members. [C++ Access private static member from public static method?](https://stackoverflow.com/questions/18433752/c-access-private-static-member-from-public-static-method) – Pepijn Kramer Jan 16 '23 at 17:03
  • True. making them `static` makes sense ... more than `static inline` which would mean each file would have its own instance, yes? – xyf Jan 16 '23 at 17:04
  • Just make them static, you need to initialize them in one of the cpp files though. – Pepijn Kramer Jan 16 '23 at 17:05
  • `weakPtr` is declared in namespace `TEST` while `extern` declares `weakPtr` in global namespace. – user7860670 Jan 16 '23 at 17:05
  • @PepijnKramer so I'd have to what's done in `SetUpTestSuite()` in one of the source files that's including it? – xyf Jan 16 '23 at 17:09
  • You can also use something called a meyer's singleton. Which is threadsafe (since C++11) and will initialize your variable on first use. `int get_shared_int() { static int value{1}; return value;` or for a class `const some_class& get_shared_class() { static const some_class object; return object; }`. I've used this too, though in general I try to stay away from globals/singletons. – Pepijn Kramer Jan 16 '23 at 17:59
  • 1
    What version of google test is this? Fixture classes should inherit `::testing::Test`. – debido Jan 16 '23 at 18:36
  • @debido i am using `using namespace testing;` (not shown here) – xyf Jan 16 '23 at 18:40

1 Answers1

1

The reason for the compile error with extern is that this only references the global variable. One of the translation units (exactly one!) has to define it, too. So, in the header, you need it declared with extern, and in one(!) of the cpp files, it additionally needs to be defined without extern.

If you don't define the variable anywhere (and only reference it with extern), the linker will complain that the variable doesn't exist - that's the error messages you're getting.

Here's a simple example with an int.

Header:

//Declare that the variable exists, don't actually create it
extern int g_whatever;

CPP file 1:

#include "Header"

//Create (define) the variable
int g_whatever;

CPP file 2, 3, ...:

#include "Header"

//Use g_whatever, *without* defining it again

This is independent of where you actually initialize the global variable. You could put the definition into UtTestBase.cpp, for example.

For a more in-depth explanation, see: How do I use extern to share variables between source files?

Jonathan S.
  • 1,796
  • 5
  • 14
  • Thanks. Do you mind providing a solution based on my code though? defining it one cpp file now doesn't error out but fails with `exit code 11` when I run the test – xyf Jan 16 '23 at 17:32
  • If the linker error is gone, you're using `extern` correctly and are now running into a different, unrelated problem. GMock exit code 11 apparently means that your program segfaulted. Attach a debugger and check what happens. – Jonathan S. Jan 16 '23 at 17:34
  • I mean the problem wasn't only about using `extern` but more about using `extern` to solve the problem at hand given it's a test mock environment. `SetUpTestSuite` assigns the globals and that needs to happen before other files access the globals. Accessing the globals elsewhere *before* would certainly segfault. Do you see the problem here? – xyf Jan 16 '23 at 17:48
  • As far as I understood your question, you wanted a way to share these variables between cpp files - which you now can. If you now want to know how to make GMock call a function before executing tests, you can open a separate question, since it's completely independent of sharing variables. In general, you should only ask one question in a StackOverflow question. If you have multiple questions, create multiple questions. – Jonathan S. Jan 16 '23 at 17:55
  • created a new question: https://stackoverflow.com/questions/75138102/how-to-share-globals-across-files-that-are-assigned-in-setuptestsuite – xyf Jan 16 '23 at 18:11
  • I am sorry. The question I opened has been closed so I expect your answer to be changed since it doesn't solve the problem... – xyf Jan 16 '23 at 18:17