문제

나는 하드웨어 레지스터를 설정하기 위해 모든 곳에서 '매직 숫자'를 사용하는 오래된 코드를 정리하고 있으며, 코드를 다소 표현력으로 만들기 위해이 숫자 대신 상수를 사용하고 싶습니다 (실제로 이름에 매핑됩니다. /레지스터를 문서화하는 데 사용되는 값).

그러나 나는 변화의 양으로 마법의 숫자를 깨뜨릴 수 있다고 우려합니다. 여기 단순화 된 예입니다 (레지스터 세트가 더 복잡합니다).

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

업데이트

@Christian, 빠른 응답에 감사드립니다. C / Nonboost 환경 답변에도 관심이 있습니다. 이것은 드라이버 / 커널 코드이기 때문입니다.

도움이 되었습니까?

해결책

새로운 답변 :

원래 대답 (아래)에서는 기능 범위와 글로벌 범위에서 주장을 뒷받침하기 위해 두 가지 매크로가 있어야했습니다. 나는 두 범위에서 모두 작동하는 단일 솔루션을 만들 수 있는지 궁금했습니다.

외부 문자 배열을 사용하여 Visual Studio 및 Comeau 컴파일러에서 작동하는 솔루션을 찾을 수있었습니다. 그러나 GCC에 효과적인보다 복잡한 솔루션을 찾을 수있었습니다. 그러나 GCC의 솔루션은 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); test.c :

GCC :

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

비주얼 스튜디오:

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

comeau :

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의 정적 어설 션에 대한 다양한 옵션을 조사합니다.

그는 세 가지 다른 접근법을 제시합니다.

  • 스위치 케이스 값은 고유해야합니다
  • 배열에는 네거티브 치수가 없어야합니다
  • 일정한 표현식의 경우 0으로 분할

최상의 구현에 대한 그의 결론은 다음과 같습니다.

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

체크 아웃 부스트 정적 인제

타사 라이브러리 정적 어제 기능 (Boost 등)에 액세스 할 수없는 경우 자신의 정적 인제를 굴릴 수 있습니다.

#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 ++ 코드의 경우 (또는 명령문과 혼합 된 선언을 허용하는 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

그러나이 경우에는 작동하지 않습니다. C 상수이기 때문에 #define에스.

Linux 커널을 볼 수 있습니다 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