arne
arne

Reputation: 4674

static initialization order and concatenation of strings

We have a rather large project that defines static const std::strings in several places to be used as parameter names; some of them need to be concatenated during static initialization:

foo.h:

struct Foo {
  static const std::string ParamSuffix;
};

foo.cpp:

const std::string Foo::ParamSuffix = ".suffix";

bar.h:

struct Bar {
  static const std::string ParamPrefix;
};

bar.cpp:

const std::string Bar::ParamPrefix = "prefix";

baz.h:

struct Baz {
    static const std::string ParamName;
};

baz.cpp:

const std::string Baz::ParamName = Bar::ParamPrefix + Foo::ParamSuffix;

The problem is obviously the "static initialization fiasco" as it is undefined in which order the static const members are initialized.

I dislike the usual solution, i.e. replace all these variables by functions, because

  1. There's a lot of these variables, i.e. a lot of code changes
  2. The concatenations require special functions, which makes the code base inconsistent and even more ugly

I currently cannot use C++11, which would make things easier with its constexpr feature (I think).

The question is: Is there any trick that would allow me to concatenate static const std::strings (or wrapper objects or whatever) to initialize another static const std::string?

Upvotes: 3

Views: 1591

Answers (3)

Tony Delroy
Tony Delroy

Reputation: 106196

When the text is not dependent on another variable, you might consider changing the type from const std::string to a const char array ala const char param_prefix[] = "prefix"; - there may well be less forced change to client usage, but that could hide new temporary std::strings having to be (or unintentionally being) created each time they're used.

Check your system, but you can almost certainly safely use those const char variables in the construction of other std::string constants - but you're out of luck for necessarily-std::string dependencies on other std::strings.

If it's too hard to untangle the existing values into can-be-const-char and std::strings that only depend on const-chars, or that just doesn't cover enough of your cases, then wholesale modifications to every constant are worth pursuing.

The appeal of wrappers is that you can keep the type and semantics of usage the same. To do that, you'll need to replace all these runtime-initialised strings with your own type that coordinates two central lists: - initialised objects, and waiting objects. An observer pattern is appropriate, or you could e.g. register a list of callbacks and complete initialisation by looping in main() calling them until they all indicate success: if at least some of the object initialisation is known to be static then that could allow one object to test another's initialisation status, which would avoid having to have constructors register their completion.

If it's possible with the help of smarter source code modifying tools (e.g. awk) - it may be better to simply change the constants to be cleanly returned by functions.

Upvotes: 2

utnapistim
utnapistim

Reputation: 27375

The question is: Is there any trick that would allow me to concatenate static const std::strings (or wrapper objects or whatever) to initialize another static const std::string?

No trivial ones, other than the one you dislike: create functions for the static strings.

There are non-trivial alternatives though (e.g. replace all your hard-coded strings with a string container/string map and load the map on application start-up).

My recommendation though would be to go with static functions (the solution you rejected).

Upvotes: 3

Dietmar Kühl
Dietmar Kühl

Reputation: 153955

Why do you think that std::string const is in any way special? (hint: it is not) Even if you could use C++11's constexpr it wouldn't help because you can't make std:strings constant expressions (they may need to allocate memory which isn't a viable constant expression). You could deal with a string-like class used for constexpr that could be made to convert to std::string but I'm not sure if these could be concatenated to produce a new constexpr.

What you maybe able to do is to extract those strings you need to use as part of a concatenation and provide their value as macro. String literals (which have type char const[N] for a suitable N) can be concatenated by simply placing them next to each other:

// foo.h
#define FOO_PARAM_SUFFIX ".suffix"
struct Foo {
    static const std::string ParamSuffix;
};

// foo.cpp:
std::string const Foo::ParamSuffix(FOO_PARAM_SUFFIX);

// bar.h:
#define BAR_PARAM_SUFFIX "prefix"
struct Bar {
    static std::string const ParamPrefix;
};

// bar.cpp:
std::string const Bar::ParamPrefix(BAR_PARAM_SUFFIX);

// baz.h:
#include "foo.h"
#include "bar.h"
#define BAZ_PARAM_NAME BAR_PARAM_PREFIX FOO_PARAM_SUFFIX

struct Baz {
    static std::string const ParamName;
};

// baz.cpp:
#include "foo.h"
#include "bar.h"
std::string const Baz::ParamName(BAR_PARAM_PREFIX FOO_PARAM_SUFFIX);

I would guess that BAZ_PARAM_NAME isn't used elsewhere in which case it also doesn't have to be defined: it is defined just to show it can be done. The initialization of Bar::ParamName assumes that BAZ_PARAM_NAME isn't defined.

Upvotes: 2

Related Questions