خلط المتغيرات والثوابت الصحيح في المعالج المسبق التعزيز

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

  •  28-09-2019
  •  | 
  •  

سؤال

أنا أستخدم BOOST_PP لإجراء حسابات مسبقة في المعالج المسبق. أنا أركز على تطبيق حيث يكون حجم الرمز الى ابعد حد مهم لي. (لذا من فضلك لا تقل المترجم ينبغي أو عادة هل هذا ، أحتاج إلى التحكم في ما يتم تنفيذه في وقت الترجمة والرمز الذي تم إنشاؤه). ومع ذلك ، أريد أن أكون قادرًا على استخدام نفس اسم الماكرو/الوظيفة لكل من ثوابت ومتغيرات عدد صحيح. كمثال تافهة ، يمكنني الحصول على

#define TWICE(n) BOOST_PP_MUL(n,2)
//.....
// somewhere else in code
int a = TWICE(5);

هذا يفعل ما أريده ، وتقييمه

int a = 10;

خلال وقت الترجمة.

ومع ذلك ، أريد أيضًا استخدامه في

int b = 5;
int a = TWICE(b);

يجب معالجة هذا مسبقًا

int b = 5;
int a = 5 * 2;

بالطبع ، يمكنني القيام بذلك باستخدام وحدات الماكرو التقليدية مثل

#define TWICE(n) n * 2

ولكن بعد ذلك لا يفعل ما أريد أن يفعله من أجل ثوابت عدد صحيح (تقييمها خلال وقت الترجمة).

لذا ، سؤالي هو ، هل هناك خدعة للتحقق مما إذا كانت الوسيطة عبارة عن متغير حرفي أو متغير ، ثم استخدم تعريفات مختلفة. أي شيء مثل هذا:

#define TWICE(n) BOOST_PP_IF( _IS_CONSTANT(n), \
                              BOOST_PP_MUL(n,2), \
                              n * 2 )

تحرير: إذن ما أنا عليه حقًا هو طريقة ما إذا كان هناك شيء ثابت متاح في وقت الترجمة ، وبالتالي وسيطة جيدة لوظائف BOOST_PP_. أدرك أن هذا يختلف عن ما يتوقعه معظم الناس من توصيات البرمجة العامة والبرمجة العامة. ولكن لا يوجد خاطئ - ظلم - يظلم طريقة البرمجة ، لذا من فضلك لا تكره السؤال إذا كنت لا توافق على فلسفتها. هناك سبب لوجود مكتبة BOOST_PP ، وهذا السؤال هو بنفس الروح. قد لا يكون ذلك ممكنًا.

هل كانت مفيدة؟

المحلول

أنت تحاول القيام بشيء من الأفضل تركه لتحسين المترجم.

int main (void) {
  int b = 5;
  int a = b * 2;

  return a; // return it so we use a and it's not optimized away
}

GCC -O3 -S TC

 .file "t.c"
 .text
 .p2align 4,,15
.globl main
 .type main, @function
main:
.LFB0:
 .cfi_startproc
 movl $10, %eax
 ret
 .cfi_endproc
.LFE0:
 .size main, .-main
 .ident "GCC: (Debian 4.5.0-6) 4.5.1 20100617 (prerelease)"
 .section .note.GNU-stack,"",@progbits

سيؤدي تحسين التحويل البرمجي إلى تحسين.

تحرير: أنا أدرك أنك لا تريد أن تسمع أن المترجم "يجب" أو "عادة" يفعل ذلك. ومع ذلك ، فإن ما تحاول القيام به ليس شيئًا من المفترض القيام به في المعالج المسبق C ؛ صمم الأشخاص الذين صمموا لغة C والمعالج المسبق C للعمل مع رمز المعالجة المسبقة لأنه ذرة أساسية. CPP هو ، من نواح كثيرة ، "غبية". هذا ليس شيئًا سيئًا (في الواقع ، هذا في كثير من الحالات ما يجعله مفيدًا للغاية) ، ولكن في نهاية اليوم ، إنه معالج مسبق. إنها ملفات المصدر قبل المعالجة قبل تحليلها. انها المعالجة المسبقة ملفات مصدر قبل حدوث التحليل الدلالي. إنه معالجة ملفات المصدر قبل التحقق من صحة ملف مصدر معين. أفهم أنك لا تريد أن تسمع أن هذا شيء يجب على المحلل والمحلل الدلالي التعامل معه أو يفعله عادة. ومع ذلك ، هذا هو واقع الوضع. إذا كنت ترغب في تصميم رمز صغير بشكل لا يصدق ، فيجب عليك الاعتماد على المترجم الخاص بك للقيام بعمله بدلاً من محاولة إنشاء بنيات مسبق للقيام بالعمل. فكر في الأمر بهذه الطريقة: ذهبت آلاف الساعات من العمل إلى المترجم الخاص بك ، لذلك حاول إعادة استخدام أكبر قدر ممكن من هذا العمل!

نصائح أخرى

ليس نهجًا مباشرًا تمامًا ، ولكن:

struct operation {
    template<int N>
    struct compile {
        static const int value = N;
    };
    static int runtime(int N) { return N; }
};

operation::compile<5>::value;
operation::runtime(5);

بدلا من ذلك

operation<5>();
operation(5);

لا توجد فرصة حقيقية لخلط المستويين (المعالج المسبق وتقييم المتغيرات). مما أفهمه من سؤالك ، b يجب أن يكون ثابتا رمزيا؟

أعتقد أنك يجب أن تستخدم التقليدية

#define TWICE(n) ((n) * 2)

ولكن بدلاً من تهيئة المتغيرات مع التعبيرات ، يجب عليك تهيئتها مع ثوابت وقت الترجمة. الشيء الوحيد الذي أراه لإجبار التقييم في وقت الترجمة والحصول على ثوابت رمزية في C هو ثوابت التعداد المتكاملة. يتم تعريف هذه على نوعها int ويتم تقييمها في وقت الترجمة.

enum { bInit = 5 };
int b = bInit;
enum { aInit = TWICE(bInit) };
int a = aInit; 

وعمومًا يجب ألا تكون أكثر هدوءًا const (أما بالنسبة لك b) والتحقق من المجمع المنتج مع -S.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top