views:

193

answers:

4

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!

+3  A: 

Move the calculation into a helper function:

static uint32_t totalSize = calculateTotalSize();

The helper function will be only invoked when totalSize gets initialized.

A bit late, but why are you doing a (potentially) runtime calculation here at all? Use compile-time constants and you can never even have any threading issues:

template<
  typename T1, unsigned N1,
  typename T2, unsigned N2,
  /* ... */
>
struct totalSize {
    static const uint32_t sum = 
        sizeof(T1)*N1
      + sizeof(T2)*N2
      /* ... */
      ;
    static const uint32_t value =
        8*((sum + 7)/8);
};

uint32_t GroupAlloc::getSize() {
    return totalSize<T1,N1,T2,N2,/*...*/>::value;
}
Georg Fritzsche
Thanks for the addition! It's never late to enlighten me :)
Viet
This is the best answer! I tried and it run flawlessly!
Viet
Note that i am not entirely sure why i used a different struct - just defining the value as a static constant member of `GroupAlloc` should be better here.
Georg Fritzsche
Well, I used the same struct and it worked without a sweat! Thanks!
Viet
+4  A: 

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.

Tyler McHenry
May I ask if this thread-safe? If not, I need to wrap inside the computeSize() function, rite?
Viet
It's not thread safe, but this fix is not as simple as wrapping `computeSize`. Addressing this as an edit to the answer...
Tyler McHenry
You can make it thread-safe trivially by calling the method once before you initialize the threads. At that point your application is single threaded. As the variable is constant throughout the execution (you may consider marking the internal static variable as `const`) there will be thread safety issues after the first completed call.
David Rodríguez - dribeas
...last sentence should be: 'there will NOT be thread safety...'
David Rodríguez - dribeas
Viet
It can be made threadsafe (and faster ) to make totalSize a static member. Then it is initialized on program load by the computeSize function before any threads are started. Plus, the implicit if in a function static does not have to be evaulated. Then again, computeSize can be evaluated at compile time, and if it will fit in a 32 bit int, can go inside an enum
Lance Diduck
+1 thanks. I don't quite get you yet. Please kindly considering putting an answer to illustrate. The problem is that, I have 32 template inputs and 31 of them have default values. I had some problems before when trying to use a static member.
Viet
+2  A: 

Something like:

static uint32_t getSize()
{
    static uint32_t totalSize = 0;
    static bool computed = 0;
    if(computed)
      return totalSize;
    computed = 1;
    // ... go on with your computation

would do the trick. Note that it isn't thread-safe.

jpalecek
+2  A: 
static uint32_t totalSize = 0;    // initialisation performed once only
if ( totalSize == 0 ) {
        totalSize += sizeof(T1)*N1;
        totalSize += sizeof(T2)*N2;
        totalSize += sizeof(T3)*N3;
        totalSize += sizeof(T4)*N4;
        // etc
}
anon