C++: What is the proper way to define non-class static const values?

704 views Asked by At

First I'll try to describe the current situation:

I am adapting an existing code base for our uses and in some instances a .h/.cpp file contains multiple class definitions. We cannot change the existing public interface of the API without major modifications to other portions of the code that we would rather avoid at this time.

I have found the need for constant values that are used by more than one class (in the same .cpp file and nowhere else) and therefore cannot be defined as a class-specific constant. The only way I know to do this is to define the constants externally from any class (but still in the .cpp file) and reference them as needed.

I have done this and the code compiles and links, but when I go to run a test program on the code it fails with an error that appears to be related to the constant value I have defined. I get the impression that when the code executes, the constant has not actually been defined and the code blows up because of that.

I don't have a lot of experience writing C++ code and wonder if I'm doing this the wrong way. I will include code snippets below to try to illustrate what I'm doing.

In DateTime.cpp (currently nothing defined in DateTime.h for DATE_FORMAT_REGEX):

...
#include <boost/regex.hpp>

static const boost::regex DATE_FORMAT_REGEX("[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])(Z|([+|-]([01][0-9]|2[0-4]):[0-5][0-9]))?");

// Other class method implementations ...

bool our_ns::Date::parse_string(const std::string& s)
{
  // Validate string against regex.
  bool valid = boost::regex_match(s, DATE_FORMAT_REGEX);

  if (valid) {
    ...
  }
}

...

The call to regex_match is what fails. All of the classes, by the way are defined within a namespace in the header file.

It would appear the constant is not being initialized. What do I need to do to initialize these values? Based upon what I've described, is there a better way to do this?

[Update: 6/9/15 12:52 EDT] Actually, I am relaying information witnessed by another developer in the organization. He verified in a debugger that the regex item was null when he reached the line where it blows up. He also mentioned that when he did some experimentation and moved the definitions from the .cpp to the .h file, the error did not recur. Beyond that, I don't know if things were executing properly.

3

There are 3 answers

1
Jack Aidley On

The correct way is to not defined these as static const but rather as const. These are constants, they do not need static linkage. If you're doing it to avoid the global namespace, const variables have implicit compilation unit scoping anyway.

0
roman On

If Date::parse_string is called from a Date constructor and the Date object is static and initialized in a different compilation unit (C++ file) then it could be an issue related to static variable initialization order. In this case the order of initialization of variables is not defined so the Date object might be initialized before DATE_FORMAT_REGEX and as a result parse_string is called before DATE_FORMAT_REGEX is initialized. You can fix it by moving DATE_FORMAT_REGEX to the Date class definition.

0
KalyanS On

This is likely a result of initialization order, a well-known issue that one could run into.

It has been described in books, such as C++ FAQs in detail for a very long time. Here is a quick reference:

https://isocpp.org/wiki/faq/ctors#static-init-order

Basically, there is no guaranteed order in which static variables must initialize. This creates situations where you could run into an error, when the "second" object makes use of the "first", but, "first" hasn't been initialized. Please note that "first" and "second" are interchangeable, therein lies the crux of the problem.

In fact, the more dangerous situation is a latent bug, where things continue to work until one day when they don't - usually, induced by a new compiler version or some such change.

It is better to get out of this dependency altogether. You don't seem to need to static here. If the goal is to limit visibility of the variable, then use anonymous namespace and place it before any use.