Search code examples
c++templatesstatic-methodslazy-evaluationstatic-variables

How to create static method that evaluates local static variable once?


I have a class with static method which has a local static variable. I want that variable to be computed/evaluated once (the 1st time I call the function) and for any subsequent invocation, it is not evaluated anymore. How to do that? Here's my class:

template<
    typename T1 = int, unsigned N1 = 1,
    typename T2 = int, unsigned N2 = 0,
    typename T3 = int, unsigned N3 = 0,
    typename T4 = int, unsigned N4 = 0,
    typename T5 = int, unsigned N5 = 0,
    typename T6 = int, unsigned N6 = 0,
    typename T7 = int, unsigned N7 = 0,
    typename T8 = int, unsigned N8 = 0,
    typename T9 = int, unsigned N9 = 0,
    typename T10 = int, unsigned N10 = 0,
    typename T11 = int, unsigned N11 = 0,
    typename T12 = int, unsigned N12 = 0,
    typename T13 = int, unsigned N13 = 0,
    typename T14 = int, unsigned N14 = 0,
    typename T15 = int, unsigned N15 = 0,
    typename T16 = int, unsigned N16 = 0>
struct GroupAlloc
{
    static const uint32_t sizeClass;
    static uint32_t getSize()
    {
        static uint32_t totalSize = 0;

        totalSize += sizeof(T1)*N1;
        totalSize += sizeof(T2)*N2;
        totalSize += sizeof(T3)*N3;
        totalSize += sizeof(T4)*N4;

        totalSize += sizeof(T5)*N5;
        totalSize += sizeof(T6)*N6;
        totalSize += sizeof(T7)*N7;
        totalSize += sizeof(T8)*N8;

        totalSize += sizeof(T9)*N9;
        totalSize += sizeof(T10)*N10;
        totalSize += sizeof(T11)*N11;
        totalSize += sizeof(T12)*N12;

        totalSize += sizeof(T13)*N13;
        totalSize += sizeof(T14)*N14;
        totalSize += sizeof(T15)*N15;
        totalSize += sizeof(T16)*N16;

        totalSize = 8*((totalSize + 7)/8);

        return totalSize;
    }
};

EDIT:

Thanks all for your prompt help. +1 to everyone. I chose Tyler McHenry's answer because it does not need any comparison, purely static function evaluation. I will need this code for allocator so avoiding another "if" should be better. Thanks again!

EDIT:

gf's answer turned out to be the best one as it deals with assignment during compile-time and saves the program from thread-safe headache and explicit initialization. However, I respect the previous best answer. I will give credit here instead of changing the tick mark. Thanks everyone for helping!


Solution

  • Make another static function that does the computation, and use that for the initialization of the variable, e.g.

    static uint32_t computeSize() 
    {
      uint32_t init_totalSize;
    
      // Lots of code
    
      return init_totalSize;
    }
    
    static uint32_t getSize()
    {
      static uint32_t totalSize = computeSize();
      return totalSize;
    }
    

    Static variables are guaranteed to be initialized exactly once (the first time the function containing them is used).

    Edit: But this is not thread-safe. This page explains why in great detail.

    To make it thread-safe, it is not sufficient to wrap the initialization of totalSize (the call to computeSize) in a critical section, because static variable initialization is "compiler magic", and it can be that the variable to undergoes initialization at any time during the call to getSize before it is used, even before the function's first statement. What you need to do is prevent more than one thread from even calling getSize at the same time, which can be accomplished with yet another level of indirection, e.g.

    static uint32_t computeSize() 
    {
      uint32_t init_totalSize;
    
      // Lots of code
    
      return init_totalSize;
    }
    
    static uint32_t real_getSize()
    {
      static uint32_t totalSize = computeSize();
      return totalSize;
    }
    
    static uint32_t getSize()
    {
      uint32_t totalSize;
      /* --- Enter Critical Section (acquire lock) -- */
      totalSize = real_getSize();
      /* --- Exit Critical Section (release lock) -- */
      return totalSize;
    }
    

    This prevents two threads from even entering the function that contains the static variable at the same time, and ensure that its initialization will occur within a critical section.