سؤال

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

ومع ذلك أنا مع حجم التغييرات قد كسر الأرقام السحرية.هنا هو مثال مبسط (سجل مجموعة أكثر تعقيدا):

const short mode0 = 0;
const short mode1 = 1;
const short mode2 = 2;

const short state0 = 0;
const short state1 = 4;
const short state2 = 8;

وذلك بدلا من :

set_register(5);

لدينا:

set_register(state1|mode1);

ما أبحث عنه هو وقت البناء الإصدار:

ASSERT(5==(state1|mode1));

التحديث

@المسيحية ، شكرا على الرد السريع, أنا مهتم على ج / عدم تعزيز البيئة الجواب أيضا لأن هذا هو سائق/رمز النواة.

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

المحلول

إجابة جديدة :

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

كنت قادرا على العثور على الحل الذي عملت Visual Studio و كومو المجمعين باستخدام extern حرف المصفوفات.ولكن كنت قادرا على العثور على أكثر تعقيدا الحل الذي يعمل في دول مجلس التعاون الخليجي.ولكن دول مجلس التعاون الخليجي هو الحل لا يعمل Visual Studio.:( ولكن مضيفا '#ifdef __ GNUC __', فإنه من السهل أن تختار الحق في مجموعة من وحدات الماكرو معين مترجم.

الحل:

#ifdef __GNUC__
#define STATIC_ASSERT_HELPER(expr, msg) \
    (!!sizeof \ (struct { unsigned int STATIC_ASSERTION__##msg: (expr) ? 1 : -1; }))
#define STATIC_ASSERT(expr, msg) \
    extern int (*assert_function__(void)) [STATIC_ASSERT_HELPER(expr, msg)]
#else
    #define STATIC_ASSERT(expr, msg)   \
    extern char STATIC_ASSERTION__##msg[1]; \
    extern char STATIC_ASSERTION__##msg[(expr)?1:2]
#endif /* #ifdef __GNUC__ */

هنا هي رسائل الخطأ الإبلاغ عن STATIC_ASSERT(1==1, test_message); في السطر 22 من الاختبار.ج:

مجلس التعاون الخليجي:

line 22: error: negative width in bit-field `STATIC_ASSERTION__test_message'

Visual Studio:

test.c(22) : error C2369: 'STATIC_ASSERTION__test_message' : redefinition; different subscripts
    test.c(22) : see declaration of 'STATIC_ASSERTION__test_message'

كومو:

line 22: error: declaration is incompatible with
        "char STATIC_ASSERTION__test_message[1]" (declared at line 22)

 
 

الرد الأصلي :

أنا لا شيء مشابه جدا ما الداما لا.ولكن لا تشمل الرسالة التي سوف تظهر في العديد من المجمعين:

#define STATIC_ASSERT(expr, msg)               \
{                                              \
    char STATIC_ASSERTION__##msg[(expr)?1:-1]; \
    (void)STATIC_ASSERTION__##msg[0];          \
}

و لفعل شيء على النطاق العالمي (خارج) وظيفة استخدام هذا:

#define GLOBAL_STATIC_ASSERT(expr, msg)   \
  extern char STATIC_ASSERTION__##msg[1]; \
  extern char STATIC_ASSERTION__##msg[(expr)?1:2]

نصائح أخرى

هناك مقال رالف هولي الذي يدرس مختلف الخيارات ثابت يؤكد في C.

انه يقدم ثلاث طرق مختلفة:

  • التبديل القيم يجب أن تكون فريدة من نوعها
  • المصفوفات يجب أن لا يكون الأبعاد السلبية
  • القسمة على صفر المستمر التعبير

الاستنتاج للحصول على أفضل تنفيذ هذا:

#define assert_static(e) \
    do { \
        enum { assert_static__ = 1/(e) }; \
    } while (0)

الخروج دفعة ثابت تأكيد

يمكنك لفة الخاص بك ثابت تأكيد إذا لم يكن لديك الوصول إلى طرف ثالث مكتبة ثابتة تؤكد وظيفة (مثل زيادة):

#define STATIC_ASSERT(x) \
    do { \
        const static char dummy[(x)?1:-1] = {0};\
    } while(0)

الجانب السلبي هو ، بالطبع ، أن رسالة الخطأ لن تكون مفيدة جدا, ولكن على الأقل أنها سوف تعطيك رقم السطر.

#define static_assert(expr) \
int __static_assert(int static_assert_failed[(expr)?1:-1])

ويمكن استخدامه في أي مكان وفي أي الأوقات.أعتقد أن هذا هو الحل الأسهل.

قبل استخدام اختبار مع المترجم الخاص بك بعناية.

أي من التقنيات المذكورة هنا يجب العمل وعندما C++0x تصبح متوفرة سوف تكون قادرة على استخدام المدمج في static_assert الكلمات الرئيسية.

إذا كان لديك دفعة ثم استخدام BOOST_STATIC_ASSERT هو الطريق للذهاب.إذا كنت تستخدم C أو لا ترغب في الحصول على دفعة هنا هو بلدي c_assert.h الملف الذي يحدد (و يشرح عمل) بضع وحدات الماكرو في التعامل مع ساكنة التأكيدات.

انها قليلا أكثر تعقيدا أنه ينبغي أن يكون لأنه في ANSI C رمز تحتاج 2 وحدات الماكرو مختلفة - واحد التي يمكن أن تعمل في المنطقة حيث لديك الإعلانات التي يمكن أن تعمل في المنطقة حيث من الطبيعي البيانات الذهاب.هناك أيضا قليلا من العمل الذي يدخل في صناعة الماكرو العمل على النطاق العالمي أو في كتلة نطاق مجموعة من المادة اللزجة إلى التأكد من أن هناك أي تضارب الأسماء.

STATIC_ASSERT() يمكن استخدامها في تعريف متغير كتلة أو النطاق العالمي.

STATIC_ASSERT_EX() يمكن أن يكون من بين البيانات العادية.

C++ code (أو C99 البرمجية التي تسمح الإعلانات مختلطة مع البيانات) STATIC_ASSERT() سوف تعمل في أي مكان.

/*
    Define macros to allow compile-time assertions.

    If the expression is false, an error something like

        test.c(9) : error XXXXX: negative subscript

    will be issued (the exact error and its format is dependent
    on the compiler).

    The techique used for C is to declare an extern (which can be used in
    file or block scope) array with a size of 1 if the expr is TRUE and
    a size of -1 if the expr is false (which will result in a compiler error).
    A counter or line number is appended to the name to help make it unique.  
    Note that this is not a foolproof technique, but compilers are
    supposed to accept multiple identical extern declarations anyway.

    This technique doesn't work in all cases for C++ because extern declarations
    are not permitted inside classes.  To get a CPP_ASSERT(), there is an 
    implementation of something similar to Boost's BOOST_STATIC_ASSERT().  Boost's
    approach uses template specialization; when expr evaluates to 1, a typedef
    for the type 

        ::interslice::StaticAssert_test< sizeof( ::interslice::StaticAssert_failed<true>) >

    which boils down to 

        ::interslice::StaticAssert_test< 1>

    which boils down to 

        struct StaticAssert_test

    is declared. If expr is 0, the compiler will be unable to find a specialization for

        ::interslice::StaticAssert_failed<false>.

    STATIC_ASSERT() or C_ASSERT should work in either C or C++ code  (and they do the same thing)

    CPP_ASSERT is defined only for C++ code.

    Since declarations can only occur at file scope or at the start of a block in 
    standard C, the C_ASSERT() or STATIC_ASSERT() macros will only work there.  For situations
    where you want to perform compile-time asserts elsewhere, use C_ASSERT_EX() or
    STATIC_ASSERT_X() which wrap an enum declaration inside it's own block.

 */

#ifndef C_ASSERT_H_3803b949_b422_4377_8713_ce606f29d546
#define C_ASSERT_H_3803b949_b422_4377_8713_ce606f29d546

/* first some utility macros to paste a line number or counter to the end of an identifier
 * this will let us have some chance of generating names that are unique
 * there may be problems if a static assert ends up on the same line number in different headers
 * to avoid that problem in C++ use namespaces
*/

#if !defined( PASTE)
#define PASTE2( x, y) x##y
#define PASTE( x, y)  PASTE2( x, y)
#endif /* PASTE */

#if !defined( PASTE_LINE)
#define PASTE_LINE( x)    PASTE( x, __LINE__)
#endif /* PASTE_LINE */

#if!defined( PASTE_COUNTER)
#if (_MSC_VER >= 1300)      /* __COUNTER__ introduced in VS 7 (VS.NET 2002) */
    #define PASTE_COUNTER( x) PASTE( x, __COUNTER__)   /* __COUNTER__ is a an _MSC_VER >= 1300 non-Ansi extension */
#else
    #define PASTE_COUNTER( x) PASTE( x, __LINE__)      /* since there's no __COUNTER__ use __LINE__ as a more or less reasonable substitute */
#endif
#endif /* PASTE_COUNTER */



#if __cplusplus
extern "C++" {   // required in case we're included inside an extern "C" block
    namespace interslice {
        template<bool b> struct StaticAssert_failed;
        template<>       struct StaticAssert_failed<true> { enum {val = 1 }; };
        template<int x>  struct StaticAssert_test { };
    }
}
    #define CPP_ASSERT( expr) typedef ::interslice::StaticAssert_test< sizeof( ::interslice::StaticAssert_failed< (bool) (expr) >) >  PASTE_COUNTER( IntersliceStaticAssertType_)
    #define STATIC_ASSERT( expr)    CPP_ASSERT( expr)
    #define STATIC_ASSERT_EX( expr) CPP_ASSERT( expr)
#else
    #define C_ASSERT_STORAGE_CLASS extern                  /* change to typedef might be needed for some compilers? */
    #define C_ASSERT_GUID 4964f7ac50fa4661a1377e4c17509495 /* used to make sure our extern name doesn't collide with something else */
    #define STATIC_ASSERT( expr)   C_ASSERT_STORAGE_CLASS char PASTE( PASTE( c_assert_, C_ASSERT_GUID), [(expr) ? 1 : -1])
    #define STATIC_ASSERT_EX(expr) do { enum { c_assert__ = 1/((expr) ? 1 : 0) }; } while (0)
#endif /* __cplusplus */

#if !defined( C_ASSERT)  /* C_ASSERT() might be defined by winnt.h */
#define C_ASSERT( expr)    STATIC_ASSERT( expr)
#endif /* !defined( C_ASSERT) */
#define C_ASSERT_EX( expr) STATIC_ASSERT_EX( expr)



#ifdef TEST_IMPLEMENTATION
C_ASSERT( 1 < 2);
C_ASSERT( 1 < 2);

int main( )
{
    C_ASSERT( 1 < 2);
    C_ASSERT( 1 < 2);

    int x;

    x = 1 + 4;

    C_ASSERT_EX( 1 < 2);
    C_ASSERT_EX( 1 < 2);



    return( 0);
}
#endif /* TEST_IMPLEMENTATION */
#endif /* C_ASSERT_H_3803b949_b422_4377_8713_ce606f29d546 */

محاولة:

#define STATIC_ASSERT(x, error) \
do { \
    static const char error[(x)?1:-1];\
} while(0)

ثم يمكنك كتابة:

STATIC_ASSERT(a == b, a_not_equal_to_b);

والتي قد تعطيك أفضل رسالة الخطأ (حسب المترجم).

الشائعة المحمولة الخيار

#if 5 != (state1|mode1)
#    error "aaugh!"
#endif

لكنه لا يعمل في هذه الحالة لأنهم ج الثوابت لا #defines.

يمكنك أن ترى نواة لينكس هو BUILD_BUG_ON ماكرو لشيء أن يتعامل مع الحالة الخاصة بك:

#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))

عندما condition هو صحيح ، يصبح هذا ((void)sizeof(char[-1])), الذي هو غير قانوني و يجب أن تفشل في وقت الترجمة ، وإلا فإنه يصبح ((void)sizeof(char[1])), الذي هو على ما يرام.

تضمن لك ترجمة مع بما فيه الكفاية الأخيرة مترجم (مثلا ، gcc -std=c11).

ثم البيان الخاص بك هو ببساطة:

_Static_assert(state1|mode1 == 5, "Unexpected change of bitflags");
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top