1

I am a newbie in c++. I am trying to create a static constant container in c++. In java we typically do that by static constant initialization. For e.g.

class ConstantDefinition {
  public static const List<String> stringList = new ArrayList<String>();
  static {
       stringList.add("foo");
       stringList.add("boo");
       ...blah
       }                       
} 

The way java works, I don't need to call a specific method to get the initialization done. Static block gets initialized once the class is loaded into JVM. But in c++ we don't have the same class loading mechanism as java. And what I want is to have a single copy of non modifiable container that I can use without creating the class objects every time. One way I understand is that I create a class(similar to my java example above) and define a const static container. But I am finding it difficult to write that kind of code in C++ because I can't do the initialization without calling a method. So what's the best way to achieve this? The second approach could be that I define a header file and initialize global variables within namespaces. If I take this approach then would it create different global variable each time when I include that header file or the same one will be used?

Thanks, RG

3 Answers3

2

If you put this code in any compilation unit (probably a source file)

namespace {
    static struct Initialiser
    {
        Initialiser()
        {
            // ToDo - initialisation code here
        }
    } TheInitialiser;
}

Then the code block will be ran once the library / executable is loaded (and before the main function, if any, is called). This idiom is quite common.

The outer namespace{} (called an anonymous namespace) block prevents this code being emitted into the linker and other compilation units.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • If I define a vector inside the struct Initializer and initialize it inside the constructor, it appears to work. However I can't make the vector constant. So how to get around that restriction? In this case do I need to define this anonymous namespace in the same file that intends to use this struct or can it be defined in a separate source file? If I define it in a separate source file then should I do #include ( Not sure but adding .cpp does not look okay to me in #include). Also, just for my understanding purposes, I think we can create more than one struct object, right? – user3669040 May 23 '14 at 14:38
2

There are several solutions, depending on how complicated the actual initialization is, and whether you can count on C++11 or not. In all cases, the solution depends on the fact that constructors are called on static variables when the code is loaded for execution.

In the simplest case, you just define the variable with an initialzier, e.g.:

In the class:

class ConstantDefinition
{
    static std::vector<std::string> const stringList;
    //  ...
};

(This will be the same for all of the solutions.)

And in a source file:

std::vector<std::string> const ConstantDefinition::stringList{
    "foo",
    "boo",
    //  ...
}

This only works in C++11, however. With earlier versions of C++, you'll need to define the variable:

std::string const stringListInit[] =
{
    "foo",
    "boo",
    //  ...
};
std::vector<std::string> const ConstantDefinition::stringList(
    begin( stringListInit ), end( stringListInit ) );

You'll also need the functions begin and end:

template <typename T, size_t N>
T* begin( T (&array)[N} ) { return array; }
template <typename T, size_t N>
T* end( T (&array)[N] ) { return array + N; }

If you don't have C++11 (where they are in the standard library), you'll want them anyway.

Don't forget that in either of the initializer lists, you can use any arbitrary expression for the initialization, including function calls.

Finally, if your initialization is too complex for this, you can always encapsulate it in a function:

namespace {

std::vector<std::string> stringListInit()
{
    std::vector<std::string> results;
    results.push_back( "foo" );
    //  ...
    return results;
}
}

std::vector<std::string> const ConstantDefinition::stringList( stringListInit() );

Don't worry too much about creating an array which will be copied and then destroyed; C++ allows something calls NRVO, which means that the compiler can actually "merge" the local variable in stringListInit and ConstantDefinition::stringList, so there will only be one std::vector<std::string> actually constructed.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
1

In ConstantDefinition.hpp :

#include <string>
#include <vector>

struct ConstantDefinition
{
    static const std::vector<std::string> string_array;
};

In ConstantDefinition.cpp :

const std::vector<std::string> ConstantDefinition::string_array =
{
    "foo",
    "boo"
};

Note : C++11 or higher is required.

Chnossos
  • 9,971
  • 4
  • 28
  • 40