Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have static data members in a header-only library?

What is the best way to have a static member in a non-templated library class, without placing the burden of defining the member on the class user?

Say I want to provide this class:

class i_want_a_static_member {     static expensive_resource static_resource_;  public:     void foo()     {         static_resource_.bar();     } }; 

Then the user of the class must not forget to define the static member somewhere (as already answered many times):

// this must be done somewhere in a translation unit expensive_resource i_want_a_static_member::static_resource_; 

I do have an answer below, but it has some disadvantages. Are there better and/or more elegant solutions?

like image 343
pesche Avatar asked Jul 29 '12 14:07

pesche


People also ask

Can static variables be declared in a header file?

Yes there is difference between declaring a static variable as global and local. If it is local, it can be accessed only in the function where it's declared. But if it is global, all functions can access it.

What happens if static variable is declared in header file?

When defining a static variable in a header file, a new instance of the variable is created for each file including the header file. This is often surprising as people often expect to have only one instance of the variable. This leads to errors that are very difficult to track/understand.

Do static libraries need headers?

Static libraries allow you to create a library and use that library in many projects. The need for header files: Since the project using the library is programmed and compiled independent of the library, that program needs to know the declaration of the things you're using.


2 Answers

C++17 and above

Use inline static variables for non-dynamic initialization:

struct Foo {     inline static int I = 0; }; 

And use function local static variables otherwise:

struct Foo {     static std::string& Bar()     {         static std::string S = compute();         return S;     } }; 

C++14 and below

Use function local statics, as they are plain easier to use.

If for some reason you really wish for a static data member, then you can use the template trick:

template <typename T = void> struct Foo {      static int I = 0; // inline initialization only for simple types. };  template <typename T> int Foo<T>::I; 

On local statics

For resources which require dynamic initialization, it is best to use a local static.

The order in which file-scope or class-scope statics are dynamically initialized is undefined, in general, leading to the Static Initialization Order Fiasco when you try to read a uninitialized static as part of the initialization of another. Local static solve the issue by being initialized lazily, on first use.

There is some slight overhead to using local statics, however. From C++11 onwards, the initialization is required to be thread-safe, which typically means that any access is gated by an atomic read and well-predicted branch.

like image 68
Matthieu M. Avatar answered Sep 19 '22 14:09

Matthieu M.


My own solution is to use a templated holder class, as static members work fine in templates, and use this holder as a base class.

template <typename T> struct static_holder {     static T static_resource_; };  template <typename T> T static_holder<T>::static_resource_; 

Now use the holder class:

class expensive_resource { /*...*/ };  class i_want_a_static_member : private static_holder<expensive_resource> { public:     void foo()     {         static_resource_.bar();     } }; 

But as the name of the member is specified in the holder class, you can't use the same holder for more than one static member.

like image 38
pesche Avatar answered Sep 20 '22 14:09

pesche