كيفية إنشاء طريقة ثابتة تقوم بتقييم المتغير الثابت المحلي مرة واحدة؟

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 للجميع. لقد اخترت إجابة Tyler McHenry لأنها لا تحتاج إلى أي مقارنة ، وتقييم الوظيفة الثابتة البحتة. سأحتاج إلى هذا الرمز للتخصيص ، لذا تجنب "إذا" يجب أن يكون أفضل. شكرًا لك مرة أخرى!

تعديل:

اتضح أن إجابة 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) في القسم الحاسم ، لأن التهيئة المتغيرة الثابتة هي "سحر التحويل البرمجي" ، ويمكن أن يكون المتغير للخضوع للتهيئة في أي وقت أثناء المكالمة 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