Как создать статический метод, который оценивает локальную статическую переменную один раз? - PullRequest
1 голос
/ 20 марта 2010

У меня есть класс со статическим методом, который имеет локальную статическую переменную. Я хочу, чтобы эта переменная была вычислена / оценена один раз (в первый раз, когда я вызываю функцию), и для любого последующего вызова она больше не оценивается. Как это сделать? Вот мой класс:

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:

Спасибо всем за оперативную помощь. +1 каждому. Я выбрал ответ Тайлера МакГенри, потому что он не нуждается ни в каком сравнении, просто в статической оценке функций. Мне понадобится этот код для распределителя, поэтому лучше избегать другого «если». Еще раз спасибо!

EDIT:

Ответ gf оказался лучшим, поскольку он имеет дело с присваиванием во время компиляции и спасает программу от поточно-ориентированной головной боли и явной инициализации. Тем не менее, я уважаю предыдущий лучший ответ. Я дам кредит здесь вместо того, чтобы менять отметку. Спасибо всем за помощь!

Ответы [ 4 ]

4 голосов
/ 20 марта 2010

Создайте другую статическую функцию, которая выполняет вычисления, и используйте ее для инициализации переменной, например,

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;
}

Статические переменные гарантированно инициализируются ровно один раз (при первом использовании функции, содержащей их).

Редактировать: Но это не поточно-ориентированный. На этой странице подробно объясняется, почему .

Чтобы сделать его потокобезопасным, недостаточно обернуть инициализацию totalSize (вызов computeSize) в критической секции, потому что инициализация статической переменной - это «магия компилятора», и это может быть переменная, которая подвергается инициализации в любое время во время вызова getSize перед ее использованием, даже перед первым оператором функции. То, что вам нужно сделать, это предотвратить одновременный вызов более чем одним потоком getSize, что может быть достигнуто с помощью еще одного уровня косвенности, например

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;
}

Это предотвращает одновременный вход двух потоков в функцию, содержащую статическую переменную, и гарантирует, что ее инициализация будет происходить в критической секции.

3 голосов
/ 20 марта 2010

Переместить расчет в вспомогательную функцию:

static uint32_t totalSize = calculateTotalSize();

Вспомогательная функция будет вызываться только при инициализации totalSize.

Немного поздно, но почему вы вообще делаете (потенциально) расчет времени выполнения? Используйте константы времени компиляции, и у вас никогда не будет проблем с потоками:

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;
}
2 голосов
/ 20 марта 2010
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
}
2 голосов
/ 20 марта 2010

Что-то вроде:

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

сделает трюк. Обратите внимание, что это не потокобезопасно.

...