-3

There doesn't seem to be a clear, concise example of real word use of a public static variable in C++ from multiple files on StackOverflow.

There are many examples showing how to use static variables in a single C++ translation unit, and many questions about the precise nature of the various uses of the static keyword, but as a programmer more experienced with C# I found it hard to scrape together what I needed to simply "have a static variable on a class and use it elsewhere" like you would in C# or Java.

Here I will try to demonstrate what I discovered in the most straightforward way. I hope that others will then improve on the answer and give more technical details for those that are interested.

SomeClass.h

In the header file for the class where we want to have a static variable, we just declare it static like we would in C# or Java. Don't try to initialise the variable here; C++ doesn't like that.

class SomeClass
{
public:
  static bool some_flag;
};

SomeClass.cpp

In the cpp file for the class, we create the storage for the static variable, just like we would provide the implementation for a member function. Here we can initialise the variable. So far, this is going swimmingly!

#include "SomeClass.h"

bool SomeClass::some_flag = true;

SomeOtherClass.cpp

Here is where I ran into problems. If we try to just access SomeClass::some_flag from elsewhere (which, let's face it, is probably the reason you wanted a public static variable in the first place), the linker will complain that it doesn't know where it lives. We've told the compiler that the static variable exists, so the compiler is happy. The problem is that in this other translation unit we've never specified where that static variable is stored. You may be tempted to try redeclaring the storage, and hoping the linker resolves them both as "the some_flag I declared in SomeClass.h", but it won't. It will complain that you've given the variable two homes, which of course is not what you meant.

What we need to do is to tell the linker that the storage for some_flag lives elsewhere, and that it will be found once we try to put all the translation units together at link time. We use the extern keyword for this.

#include "SomeOtherClass.h"
#include "SomeClass.h"

extern bool SomeClass::some_flag;

void SomeOtherClass::SomeOtherFunction()
{
  SomeClass::some_flag = true;
};

Voila! The compiler is happy, the linker is happy, and hopefully the programmer is also happy.

I now leave this open for a discussion on how I should not have used a public variable, could have just passed an instance of SomeClass through the 87 layers of code that's between it and the usage, should have used some feature coming in C++23, should have used boost::obscurething etc. etc.

I do however welcome any alternative approaches to this fundamental problem that are true to the usage I've demonstrated here.

Derf Skren
  • 479
  • 2
  • 22
  • 2
    The paragraph starting "Here is where I ran into problems." is nonsense, if you get a linker error then you have some other bug that's not apparent from what you posted. The suggestion to use `extern` is bogus. – M.M Sep 06 '19 at 00:31
  • BTW you can use a `static` [inline variable](https://stackoverflow.com/questions/38043442/how-do-inline-variables-work) since C++14, instead of having separate declaration and definition – M.M Sep 06 '19 at 00:35
  • You should be using a static member function to implement this kind of global flag, not least that you cannot make the code as-is multithread-safe. Furthermore, static class globals and shared libraries on Linux do not play well together. – Ken Y-N Sep 06 '19 at 00:42
  • @M.M Since C++17, surely? – eerorika Sep 06 '19 at 01:00
  • @M.M are you saying that it will compile without declaring the extern? Because it doesn't for me. – Derf Skren Sep 06 '19 at 03:13
  • @DerfSkren you have some other bug in your code that is not reproduced by what you have posted in the question – M.M Sep 06 '19 at 03:17
  • @KenY-N are you saying I could get a torn read/write on a bool? That's definitely interesting. – Derf Skren Sep 06 '19 at 03:17
  • @M.M could it be because they are part of two separate compilation steps in a CMake process? – Derf Skren Sep 06 '19 at 03:19
  • Possibly , hard to say from here – M.M Sep 06 '19 at 03:20
  • @M.M that turned out to be what the case is. Unfortunately for me the many layers in between SomeClass and SomeOtherClass also are only compiled in certain steps so, there were even more complications. Thanks for your help. – Derf Skren Sep 06 '19 at 05:26
  • Thanks also to @KenY-N for hinting that the globals would not work with shared libraries, which pointed me in the right direction to investigate the issue. – Derf Skren Sep 06 '19 at 05:27

2 Answers2

2

Here is where I ran into problems. If we try to just access SomeClass::some_flag from elsewhere [...], the linker will complain that it doesn't know where it lives.

The linker won't complain, as long as you link with the translation unit that defines the variable (SomeClass.cpp).

extern bool SomeClass::some_flag;

This declaration is neither allowed, nor is it necessary. The class definition that contains the variable declaration is sufficient for the compiler, and the variable definition in SomeClass.cpp is sufficient for the linker. Demo

Don't try to initialise the variable here; C++ doesn't like that.

I do however welcome any alternative approaches to this fundamental problem

You could simply use an inline variable (since C++17):

class SomeClass
{
public:
    inline static bool some_flag = true;
Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326
-3

The answer, in summary form, for people who just want the code so they can get back to work:

SomeClass.h

#pragma once

class SomeClass
{
public:
  static bool some_flag;
};

SomeClass.cpp

#include "SomeClass.h"

bool SomeClass::some_flag = true;

SomeOtherClass.cpp

#include "SomeOtherClass.h"
#include "SomeClass.h"

extern bool SomeClass::some_flag;

void SomeOtherClass::SomeOtherFunction()
{
  SomeClass::some_flag = true;
}
Tagger5926
  • 442
  • 3
  • 13
Derf Skren
  • 479
  • 2
  • 22
  • This but with a semicolon after the class definition, and without the `extern bool` line, and with an extra colon on the second-to-last line. It would be a good idea to post code you actually compiled – M.M Sep 06 '19 at 00:32
  • Thanks for the corrections, I was hastily copying code out of a much larger file. – Derf Skren Sep 06 '19 at 03:13