Most idiomatic way to have String constants

I want to share a bunch of String constants (property or attribute names) across several .cpp files, let’s say in Processor.cpp and Editor.cpp.

What I am currently doing is having them all defined in a single Constants.h file, like:

// Constants.h

#pragma once 

namespace properties
{
    const String flavour {"flavour"};
    const String allowed {"allowed"};
}

then:

// Processor.cpp

#include "Constants.h"

// ... more code

and

// Editor.cpp

#include "Constants.h"

// ... more code

Doubts:

  1. Is this safe regarding the “static initialization order fiasco”?
    Obviously, I need it to be so.

  2. The translation units that are created by the compilation of Processor.cpp and Editor.cpp, do they both contain their own copy of all the constant Strings, or are those constants only instantianted once and shared in the final linked product?
    I’d like the “one copy shared by all translation units” more, as it feels like a waste of resources to have all those constants (they will become a lot) to be duplicated in each translation unit.

Is there a more idiomatic / ideal way to do this, perhaps via constexpr usage?

1 Like
  1. This is safe regarding static initialisation order. You are guaranteed that static data in each translation unit (TU) is initialised in the order it is declared, so because your constants are defined before the code that depends on them, you should be fine.
  2. Every TU that includes this header will get its own copy of these constants, because const variables implicitly have internal linkage. The strings are pretty small though, so it’s probably nothing to worry about.

Is there a more idiomatic/ideal way to do this?

In C++17, you can declare variables inline:

namespace props
{
  inline const String flavour { "flavour" };
  inline const String allowed { "allowed" };
}

This gives the constants external linkage, and hints to the linker that it should only keep one copy of each object, should it find the same object defined in multiple TUs.

5 Likes

Thank you.

One more question: If the compiler (or better, the linker) decides to honour the inline keyword and only keep one of copy of the constant that would otherwise appear in multiple TUs, is the static initialisation of that single copy still guaranteed to happen before any usage in any linked TU?

In other words: does point #1 of your answer still holds even in the case of inline variables at namespace scope?

From the draft standard: [basic.start]

Dynamic initialization of a non-local variable with static storage duration… is partially-ordered if the variable is an inline variable that is not an implicitly or explicitly instantiated specialization

If V has partially-ordered initialization, W does not have unordered initialization, and V is defined before W in every translation unit in which W is defined, then… [omitting some stuff about threads] the initialization of V is sequenced before the initialization of W.

1 Like

Thanks!

And the Oscar for “Most convoluted way to say ‘yes’” goes to the wording above :smile: