كيف يمكنني جعل C++ الكلي تتصرف مثل وظيفة ؟

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

  •  03-07-2019
  •  | 
  •  

سؤال

دعنا نقول أن لسبب ما تحتاج إلى كتابة ماكرو: MACRO(X,Y). (دعونا نفترض أن هناك سبب وجيه لا يمكنك استخدام وظيفة مضمنة.) تريد هذا الماكرو إلى محاكاة مكالمة مع وظيفة لا قيمة الإرجاع.


مثال 1:هذا يجب أن تعمل كما هو متوقع.

if (x > y)
  MACRO(x, y);
do_something();

مثال 2:هذا لا ينبغي أن يؤدي إلى خطأ برنامج التحويل البرمجي.

if (x > y)
  MACRO(x, y);
else
  MACRO(y - x, x - y);

مثال 3:هذا يجب أن لا تجميع.

do_something();
MACRO(x, y)
do_something();

السذاجة طريقة لكتابة الماكرو هو مثل هذا:

#define MACRO(X,Y)                       \
cout << "1st arg is:" << (X) << endl;    \
cout << "2nd arg is:" << (Y) << endl;    \
cout << "Sum is:" << ((X)+(Y)) << endl;

هذا هو سيئة للغاية الحل الذي فشل كل ثلاثة أمثلة ، و لا تحتاج إلى شرح السبب.

تجاهل ما الماكرو في الواقع لا, هذا ليس النقطة.


والان أنا في أغلب الأحيان نرى وحدات الماكرو هو مكتوب أن أرفق لهم في الأقواس المتعرجة, مثل هذا:

#define MACRO(X,Y)                         \
{                                          \
  cout << "1st arg is:" << (X) << endl;    \
  cout << "2nd arg is:" << (Y) << endl;    \
  cout << "Sum is:" << ((X)+(Y)) << endl;  \
}

هذا يحل سبيل المثال 1 ، لأن الماكرو في بيان كتلة.ولكن المثال 2 مكسورة لأن وضعنا فاصلة منقوطة بعد استدعاء الماكرو.هذا يجعل المترجم أعتقد الفاصلة المنقوطة هو بيان حد ذاته ، مما يعني بيان آخر لا تتوافق مع أي كان!وأخيرا ، على سبيل المثال 3 يجمع حسنا, على الرغم من عدم وجود فاصلة منقوطة ، لأن كتلة رمز لا يحتاج إلى منقوطة.


هل هناك طريقة لكتابة الماكرو بحيث يمر كل ثلاثة الأمثلة ؟


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

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

المحلول

وحدات الماكرو عموما يجب تجنبها ، تفضل وظائف مضمنة لهم في جميع الأوقات.أي مترجم يستحق الملح ينبغي أن تكون قادرة على تضمين وظيفة صغيرة كما لو كانت ماكرو, و مضمنة وظيفة احترام مساحات وغيرها من المجالات ، وكذلك تقييم كل الحجج مرة واحدة.

إذا كان يجب أن يكون ماكرو, حلقة while (اقترح) العمل ، أو يمكنك محاولة الفاصلة المشغل:

#define MACRO(X,Y) \
 ( \
  (cout << "1st arg is:" << (X) << endl), \
  (cout << "2nd arg is:" << (Y) << endl), \
  (cout << "3rd arg is:" << ((X) + (Y)) << endl), \
  (void)0 \
 )

على (void)0 أسباب البيان إلى تقييم أحد void نوع استخدام الفواصل بدلا من الفاصلة المنقوطة يسمح لاستخدامها داخل البيان ، وليس فقط بذاتها.ما زال أنصح مضمنة وظيفة لمجموعة من الأسباب ، أقل من التي يجري نطاق حقيقة MACRO(a++, b++) الاضافة a و b مرتين.

نصائح أخرى

هناك بالأحرى ذكي الحل:

#define MACRO(X,Y)                         \
do {                                       \
  cout << "1st arg is:" << (X) << endl;    \
  cout << "2nd arg is:" << (Y) << endl;    \
  cout << "Sum is:" << ((X)+(Y)) << endl;  \
} while (0)

الآن لديك كتلة واحدة على مستوى البيان ، الذي يجب أن يكون متبوعا بفاصلة منقوطة.هذا يتصرف كما من المتوقع و المطلوب في كل الأمثلة الثلاثة.

أنا أعلم أنك قلت "تجاهل ما الماكرو لا" ، ولكن الناس سوف تجد على هذا السؤال من خلال البحث بناء على اللقب ، لذلك أعتقد أن مناقشة المزيد من تقنيات محاكاة وظائف مع وحدات الماكرو لها ما يبررها.

أقرب أعرف من هو:

#define MACRO(X,Y) \
do { \
    auto MACRO_tmp_1 = (X); \
    auto MACRO_tmp_2 = (Y); \
    using std::cout; \
    using std::endl; \
    cout << "1st arg is:" << (MACRO_tmp_1) << endl;    \
    cout << "2nd arg is:" << (MACRO_tmp_2) << endl;    \
    cout << "Sum is:" << (MACRO_tmp_1 + MACRO_tmp_2) << endl; \
} while(0)

هذا يفعل ما يلي:

  • يعمل بشكل صحيح في كل من ذكر السياقات.
  • يقيم كل من الحجج بالضبط مرة واحدة ، وهي مضمونة ميزة استدعاء دالة (على افتراض في كلتا الحالتين لا استثناءات في أي من تلك التعبيرات).
  • يعمل على أي أنواع من استخدام "السيارات" من C++0x.هذه ليست حتى الآن معيار C++, ولكن لا توجد طريقة أخرى للحصول على tmp المتغيرات التي تقتضيها واحد التقييم القاعدة.
  • لا يحتاج الطالب إلى استيراد الأسماء من مساحة الأمراض المنقولة جنسيا ، الأصلي الكلي ، ولكن وظيفة لا.

غير أنها لا تختلف عن وظيفة في ذلك:

  • في بعض صالح يستخدم أنها قد تعطي مختلفة مترجم أخطاء أو تحذيرات.
  • هذا خطأ إذا كان X أو Y تحتوي على استخدامات 'MACRO_tmp_1' أو 'MACRO_tmp_2' من المحيط نطاق.
  • ذات مساحة الأمراض المنقولة جنسيا الشيء:وظيفة يستخدم المعجمية السياق للبحث عن الأسماء ، بينما ماكرو يستخدم سياق دعوته الموقع.ليس هناك طريقة لكتابة الماكرو الذي يتصرف مثل وظيفة في هذا الصدد.
  • فإنه لا يمكن أن تستخدم بمثابة عودة التعبير عن وظيفة الفراغ ، فراغ التعبير (مثل الفاصلة الحل) يمكن.هذا هو أكثر من قضية عندما المطلوب العودة النوع ليست باطلة ، وخاصة عند lvalue.ولكن فاصلة الحل لا يمكن أن تشمل استخدام الإعلانات لأنهم البيانات ، لذلك اختيار واحد أو استخدام ({ ...}) غنو التمديد.

هنا هو الجواب يأتي من libc6!نلقي نظرة على /usr/include/x86_64-linux-gnu/bits/byteswap.h, وجدت خدعة كنت تبحث عنه.

بعض النقاد من الحلول السابقة:

  • كيب الحل لا تسمح تقييم تعبير, الذي هو في نهاية المطاف في كثير من الأحيان هناك حاجة.
  • coppro الحل لا تسمح تعيين متغير كما التعبيرات منفصلة ، ولكن يمكن تقييم تعبير.
  • ستيف جيسوب الحل يستخدم C++11 auto الكلمات الرئيسية, هذا جيد, ولكن لا تتردد في استخدام المعروف أن نوع بدلا من ذلك.

الحيلة هي أن استخدام كل من (expr,expr) بناء و {} نطاق:

#define MACRO(X,Y) \
  ( \
    { \
      register int __x = static_cast<int>(X), __y = static_cast<int>(Y); \
      std::cout << "1st arg is:" << __x << std::endl; \
      std::cout << "2nd arg is:" << __y << std::endl; \
      std::cout << "Sum is:" << (__x + __y) << std::endl; \
      __x + __y; \
    } \
  )

لاحظ استخدام register الكلمة أنها مجرد تلميح إلى مترجم.على X و Y ماكرو المعلمات (بالفعل) محاصرون بين قوسين ، مسبوك إلى النوع المتوقع.هذا الحل يعمل بشكل صحيح مع ما قبل وما بعد الاضافة كما المعلمات يتم تقييم مرة واحدة فقط.

على سبيل المثال الغرض ، حتى ولو لم يطلب ، وأضفت __x + __y; البيان الذي هو الطريق لجعل كل كتلة يتم تقييمها كما أن دقة التعبير.

انها أكثر أمانا للاستخدام void(); إذا كنت ترغب في التأكد من أن الكلية لن تقييم إلى تعبير ، وبالتالي كونها غير قانونية حيث rvalue من المتوقع.

ومع ذلك, الحل لا ISO C++ متوافقة كما يشكو g++ -pedantic:

warning: ISO C++ forbids braced-groups within expressions [-pedantic]

من أجل إعطاء بعض الراحة g++, استخدام (__extension__ OLD_WHOLE_MACRO_CONTENT_HERE) حتى أن التعريف الجديد على ما يلي:

#define MACRO(X,Y) \
  (__extension__ ( \
    { \
      register int __x = static_cast<int>(X), __y = static_cast<int>(Y); \
      std::cout << "1st arg is:" << __x << std::endl; \
      std::cout << "2nd arg is:" << __y << std::endl; \
      std::cout << "Sum is:" << (__x + __y) << std::endl; \
      __x + __y; \
    } \
  ))

من أجل تحسين الحل حتى أكثر قليلا, دعونا استخدام __typeof__ الكلمات الرئيسية ، كما رأينا في MIN و MAX في ج:

#define MACRO(X,Y) \
  (__extension__ ( \
    { \
      __typeof__(X) __x = (X); \
      __typeof__(Y) __y = (Y); \
      std::cout << "1st arg is:" << __x << std::endl; \
      std::cout << "2nd arg is:" << __y << std::endl; \
      std::cout << "Sum is:" << (__x + __y) << std::endl; \
      __x + __y; \
    } \
  ))

الآن المترجم تحديد النوع المناسب.هذا أيضا هو gcc تمديد.

ملاحظة إزالة register الكلمات الرئيسية ، كما أن التحذير التالية عند استخدامها مع فئة نوع:

warning: address requested for ‘__x’, which is declared ‘register’ [-Wextra]

C++11 جلبت لنا lambdas ، والتي يمكن أن تكون مفيدة بشكل لا يصدق في هذه الحالة:

#define MACRO(X,Y)                              \
    [&](x_, y_) {                               \
        cout << "1st arg is:" << x_ << endl;    \
        cout << "2nd arg is:" << y_ << endl;    \
        cout << "Sum is:" << (x_ + y_) << endl; \
    }((X), (Y))

يمكنك الحفاظ على توليدي قوة من وحدات الماكرو ، ولكن يكون مريح النطاق التي يمكنك من خلالها العودة ما تريد (بما في ذلك void).بالإضافة إلى ذلك مسألة تقييم الكلي المعلمات عدة مرات هو تجنبها.

إنشاء كتلة باستخدام

 #define MACRO(...) do { ... } while(false)

لا تضيف ؛ بعد حين(كاذبة)

الجواب يعاني من عدة تقييم المشكلة ، لذلك (على سبيل المثال)

macro( read_int(file1), read_int(file2) );

سوف تفعل شيء غير متوقع و ربما غير المرغوب فيها.

كما ذكر آخرون ، يجب تجنب وحدات الماكرو كلما كان ذلك ممكنا.أنها خطرة في وجود آثار جانبية إذا الماكرو الحجج يتم تقييم أكثر من مرة.إذا كنت تعرف نوع من الحجج (أو يمكن استخدام C++0x auto ميزة), هل يمكن استخدام المؤقتات إلى فرض واحد التقييم.

مشكلة أخرى:ترتيب متعددة التقييمات يحدث قد لا يكون ما كنت تتوقع!

النظر في هذا الكود:

#include <iostream>
using namespace std;

int foo( int & i ) { return i *= 10; }
int bar( int & i ) { return i *= 100; }

#define BADMACRO( X, Y ) do { \
    cout << "X=" << (X) << ", Y=" << (Y) << ", X+Y=" << ((X)+(Y)) << endl; \
    } while (0)

#define MACRO( X, Y ) do { \
    int x = X; int y = Y; \
    cout << "X=" << x << ", Y=" << y << ", X+Y=" << ( x + y ) << endl; \
    } while (0)

int main() {
    int a = 1; int b = 1;
    BADMACRO( foo(a), bar(b) );
    a = 1; b = 1;
    MACRO( foo(a), bar(b) );
    return 0;
}

و هو الإخراج كما تجميعها وتشغيلها على الجهاز الخاص بي:

X=100, Y=10000, X+Y=110
X=10, Y=100, X+Y=110

إذا كنت على استعداد لتبني ممارسات دائما استخدام الأقواس المتعرجة في الخاص بك إذا البيانات ،

الماكرو الخاص بك سوف تكون في عداد المفقودين آخر منقوطة:

#define MACRO(X,Y)                       \
cout << "1st arg is:" << (X) << endl;    \
cout << "2nd arg is:" << (Y) << endl;    \
cout << "Sum is:" << ((X)+(Y)) << endl

مثال 1:(برمجيا)

if (x > y) {
    MACRO(x, y);
}
do_something();

مثال 2:(برمجيا)

if (x > y) {
    MACRO(x, y);
} else {
    MACRO(y - x, x - y);
}

مثال 3:(لا تجمع)

do_something();
MACRO(x, y)
do_something();
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top