Как создать статический метод, который вычисляет локальную статическую переменную один раз?

StackOverflow https://stackoverflow.com/questions/2482890

Вопрос

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

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

Редактировать:

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

Редактировать:

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

Это было полезно?

Решение

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

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) в критическом разделе, потому что инициализация статической переменной - это "магия компилятора", и может случиться так, что переменная to подвергается инициализации в любое время во время вызова 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;
}

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

Другие советы

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

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

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

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

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

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
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top