user2939212
user2939212

Reputation: 405

Can I initialize a static const member at run-time in C++?

Is it possible to initialize a static const member of my class during run-time? This variable is a constant throughout my program but I want to send it as a command-line argument.

//A.h
class A {
public: 
    static const int T;
};

//in main method
int main(int argc,char** argv)
{
    //how can I do something like 
    A::T = atoi(argv[1]);
}

If this cannot be done, what is the type of variable I should use? I need to initialize it at run-time as well as preserve the constant property.

Upvotes: 25

Views: 14229

Answers (11)

Unimportant
Unimportant

Reputation: 2096

Having been facing the same problem myself lately I found @A.S.H 's answer to be the closest to perfect but the fact that the variables have to be initialized so early can cause some problems:

  • Can't use data sources that aren't available yet, such as argc and argv as per the question.
  • Some dependencies might not be initialized yet. For example, many a GUI framework does not allow creating textboxes that early on yet. This is a problem because we might want to display a error textbox if loading the configuration file fails to inform the user.

So I came up with the following:

template <class T>
class StaticConfig
{
public:

    StaticConfig()
    {
        if (!mIsInitialised)
        {
            throw std::runtime_error("Tried to construct uninitialised StaticConfig!");
        }
    }

    const T*
    operator -> () const
    {
        return &mConfig;
    }

private:

    friend class ConfigHandler;

    StaticConfig(const T& config)
    {
        mConfig = config;
        mIsInitialised = true;
    }

    static T mConfig;
    static bool mIsInitialised;
};

template <class T>
T StaticConfig<T>::mConfig;
template <class T>
bool StaticConfig<T>::mIsInitialised = false;

We make our data static but non-const so we don't have to initialize it immediately and can assign the correct values to it at a more opportune time. Read only access is given trough a overload of operator -> The default constructor checks if a StaticConfig of this type has already been loaded with valid data and throws if it is not. This should never happen in practice but serves as a debugging aid. A private constructor allows loading the type with valid data. A ConfigHandler class, responsible for loading the data, is made a friend so it can access the private constructor.

A ConfigHandler instance can be briefly created at an opportune time when all the dependencies are available to initialize all the StaticConfig types. Once done, the ConfigHandler instance can be discarded. After that, a class can simply include the appropriate type of StaticConfig as a member and read-only access the data with minimal intrusion.

Online demonstration.

Upvotes: 1

hs_
hs_

Reputation: 181

There is a trick, but you should probably avoid it! Here's a bare bones example to illustrate the principle:

int const& foo(int i) {
    static const int j = (i == 0 ? throw 0 : i); 
    return j; 
}

int main() {
    try { 
        int x = foo(0); // oops, we throw        
    } catch(...) {}

    int x = foo(1); // initialized..
    int y = foo(0); // still works..     
}

Careful!

Upvotes: 0

g24l
g24l

Reputation: 3145

N - O

The semantics of what is being required are all wrong, and you shouldn't use a static-const for that.

A static is an object or integral type which has static storage duration and internal linkage.

A const is an object that does not change its value throughout application's lifetime, any attempt to change it results in UD . ( the overwhelming majority of such cases is a pretty well defined crash )

As a result of this question dangerous workarounds have been proposed to mimic the implied behavior. In most of examples a static-const-reference is given a somehow hidden static which is assignable at runtime, e.g. this.

Apart from the difficulties in maintaining such code, the problem remains that declared semantics are not actually enforced.

For example in keeping the value const throughout the application runtime can be hacked by doing const_cast<int &>(A::T) = 42 , which is perfectly valid, perfectly define code since the referenced type is not const.

What is being sought after here is an class that permits to be initialized only once throughout the application, has internal linkage, and the lifetime of the application.

So just do a template class that does that:

template<typename V> class fixation
{
  bool init = true;
 V val;
  public:

    fixation(V const & v) : init(true), val(v) {}

    fixation & operator=( fixation arg)
    {
      if(init )
      {
        this->val = arg.val;
      }
      this->init = false;
      return *this;
    }


    V get()
    {
      return val;
    }
};

struct A
{
    static fixation<int> T;
};

How to handle the case that it is called a second time, that is an implementation decision. In this example the value is totally ignored. Others may prefer to throw an exception, do an assertion, ... etc.

Upvotes: 0

Dimitrios Bouzas
Dimitrios Bouzas

Reputation: 42909

Method #1: Initialize a hidden non-const variable, and provide a const reference to it (as shown by dasblinkenlight):

class A {
public: 
  static const int &T;
};

static int dummy = 0;
const int &A::T  = dummy;

int main() {
  dummy = 10;
  std::cout << A::T << std::endl;
}

Live Demo

Method #2: Use a non const static member (as shown by R Sahu):

class A {
public: 
  static int T;
};

int A::T = 0;

int main() {
  A::T = 10;
}

Live Demo

Method #3: Declare a hidden non-const variable as a private static member of your class and provide a static member const reference to interface it. Define a friend function as inititalizer:

class A {
  friend void foo(int);
    static int dummy;
public: 
    static const int &T;
};

const int &A::T = A::dummy;
int A::dummy = 0;

void foo(int val) { A::dummy = val; }

int main() {
    foo(10);
    std::cout << A::T << std::endl;
}

Live Demo

Method #4: Declare a hidden non-const variable as a private static member of your class and provide a static member const reference to interface it. Define a static member function as inititalizer:

class A {
    static int dummy;
public: 
    static const int &T;
    static void foo(int val) { A::dummy = val; }
};

const int &A::T = A::dummy;
int A::dummy = 0;

int main() {
    A::foo(10);
    std::cout << A::T << std::endl;
}

Live Demo

Bonus:

If you want to initialize only once you can change the helper function to:

static void foo(int val) { 
  static bool init = true;
  if(init) A::dummy = val;
  init = false;
}

Live Demo

Upvotes: 3

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726779

You cannot rely on data produced after your main has started for initialization of static variables, because static initialization in the translation unit of main happens before main gets control, and static initialization in other translation units may happen before or after static initialization of main translation unit in unspecified order.

However, you can initialize a hidden non-const variable, and provide a const reference to it, like this:

struct A {
public: 
    // Expose T as a const reference to int
    static const int& T;
};

//in main.cpp

// Make a hidden variable for the actual value
static int actualT;
// Initialize A::T to reference the hidden variable
const int& A::T(actualT);

int main(int argc,char** argv) {
    // Set the hidden variable
    actualT = atoi(argv[1]);
    // Now the publicly visible variable A::T has the correct value
    cout << A::T << endl;
}

Demo.

Upvotes: 33

JDługosz
JDługosz

Reputation: 5652

Typically you will have more than one configuration value. So put them in a struct, and the normal global access to it is const.

const config* Config;
...
main (int argc, char* argv [])
{
Config= new config (argc, argv);
...
}

You can get fancier and have a global function to return config, so normal code can't even change the pointer, but it is harder to do that by accident.

A header file exposes get_config () for all to use, but the way to set it is only known to the code that's meant to do so.

Upvotes: 4

A.S.H
A.S.H

Reputation: 29332

I am sorry to disagree with the comments and answers saying that it is not possible for a static const symbol to be initialized at program startup rather than at compile time.

Actually this IS possible, and I used it many times, BUT I initialize it from a configuration file. Something like:

// GetConfig is a function that fetches values from a configuration file
const int Param1 = GetConfig("Param1");
const int MyClass::Member1 = GetConfig("MyClass.Member1");

As you see, these static consts are not necessarily known at compile time. They can be set from the environment, such as a config file.

On the other hand, setting them from argv[], seems very difficult, if ever feasible, because when main() starts, static symbols are already initialized.

Upvotes: 15

Use a Singleton Pattern here. have a data member which you'd like to initialize at run time in the singleton class. One a single instance is created and the data member is properly initialized, there would be no further risk of overwriting it and altering it.

Singleton would preserve the singularity of your data.

Hope this helps.

Upvotes: -1

R Sahu
R Sahu

Reputation: 206657

No, you cannot do that.

If this cannot be done what is the type of variable I should use ?

You can use a non-const member.

class A 
{
   public: 
      static int T;
};

int A::T;

Another option is to make T a private member, make main a friend so only it can modify the value, and then expose the member through a function.

#include <cstdlib>

class A 
{
   public: 
      static int getT() { return T; }
   private:
      static int T;
      friend int main(int argc, char** argv);
};

int A::T;

int main(int argc, char** argv)
{
   A::T = std::atoi(argv[1]);
   return 0;
}

Upvotes: 8

NSachdeva
NSachdeva

Reputation: 51

No, since you defined the variable as static and const, you cannot change its value. You will have to set its value in the definition itself, or through a constructor called when you create an object of class A.

Upvotes: 3

SergeyA
SergeyA

Reputation: 62593

Not only you can't, you should not try doing this by messing with const_cast. Static const members have a very high chance of ending up in read-only segment, and any attempt to modify them will cause program to crash.

Upvotes: 5

Related Questions