25

ハードウェア レジスタを設定するためにあちこちで「マジック ナンバー」を使用している古いコードを整理しています。これらの数値の代わりに定数を使用して、コードをより表現力豊かなものにしたいと考えています (実際、それらは名前にマップされます)。 /レジスタを文書化するために使用される値)。

ただし、変更の量が多くなると、マジック ナンバーが壊れてしまうのではないかと心配しています。簡単な例を次に示します (レジスタ セットはより複雑です)。

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

アップデート

@クリスチャン、迅速な対応に感謝します。これはドライバー/カーネルコードであるため、C /非ブースト環境の回答にも興味があります。

4

11 に答える 11

27

新しい答え:

私の元の回答 (以下) では、関数スコープとグローバル スコープでアサーションをサポートするために、2 つの異なるマクロが必要でした。両方の範囲で機能する単一のソリューションを考え出すことができるかどうか疑問に思いました.

extern 文字配列を使用して、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 の 22 行目で報告されたエラー メッセージです。

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'

コモー:

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

 
 

元の答え

私は Checkers がすることと非常によく似たことをします。ただし、多くのコンパイラで表示されるメッセージを含めます。

#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]
于 2008-10-06T14:15:32.360 に答える
15

C の静的アサートのさまざまなオプションを検討しているRalf Hollyによる記事があり ます。

彼は 3 つの異なるアプローチを提示します。

  • 大文字と小文字を切り替える値は一意である必要があります
  • 配列に負の次元があってはなりません
  • 定数式のゼロ除算

最良の実装に関する彼の結論は次のとおりです。

#define assert_static(e) \
    do { \
        enum { assert_static__ = 1/(e) }; \
    } while (0)
于 2008-12-02T13:01:35.467 に答える
11

チェックアウト ブーストの静的アサート

于 2008-10-06T13:50:31.240 に答える
11

サードパーティ ライブラリの静的アサート関数 (boost など) にアクセスできない場合は、独自の静的アサートをロールできます。

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

欠点は、もちろん、エラー メッセージがあまり役に立たないことですが、少なくとも、行番号が表示されます。

于 2008-10-06T13:55:12.807 に答える
8
#define static_assert(expr) \
int __static_assert(int static_assert_failed[(expr)?1:-1])

いつでもどこでも使用できます。それが最も簡単な解決策だと思います。

使用する前に、コンパイラで注意深くテストしてください。

于 2011-05-22T10:46:40.857 に答える
6

ここにリストされている手法はどれでも機能するはずであり、C ++ 0xが利用可能になると、組み込みのstatic_assertキーワードを使用できるようになります。

于 2008-10-06T15:24:11.580 に答える
5

ブーストがある場合BOOST_STATIC_ASSERTは、使用するのが最適です。C を使用している場合、または Boost を取得したくない場合はc_assert.h、静的アサーションを処理するためのいくつかのマクロを定義 (およびその動作を説明) する私のファイルを次に示します。

ANSI C コードでは 2 つの異なるマクロが必要なため、これは少し複雑です。1 つは宣言がある領域で機能し、もう 1 つは通常のステートメントが存在する領域で機能します。また、マクロをグローバル スコープまたはブロック スコープで動作させるためのちょっとした作業と、名前の衝突がないようにするための一連の作業もあります。

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 */
于 2008-10-06T17:04:31.270 に答える
5

試す:

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

次に、次のように記述できます。

STATIC_ASSERT(a == b, a_not_equal_to_b);

これにより、より適切なエラー メッセージが表示される場合があります (コンパイラによって異なります)。

于 2008-10-06T14:09:18.893 に答える
3

一般的なポータブル オプションは次のとおりです。

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

ただし、この場合は機能しません。これらは#defines ではなく C 定数であるためです。

ケースを処理するための Linux カーネルのBUILD_BUG_ONマクロを確認できます。

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

conditionが true の場合、これは になります。((void)sizeof(char[-1]))これは違法であり、コンパイル時に失敗するはずです。それ以外の場合は、問題なく になり((void)sizeof(char[1]))ます。

于 2008-10-06T14:04:11.790 に答える
1
#define MODE0 0
#define MODE1 1
#define MODE2 2

#define STATE0 0
#define STATE1 4
#define STATE2 8

set_register(STATE1|STATE1); //set_register(5);
#if (!(5==(STATE1|STATE1))) //MY_ASSERT(5==(state1|mode1)); note the !
#error "error blah blah"
#endif

これは、1 行の MY_ASSERT(expr) ソリューションほどエレガントではありません。C コードをコンパイルする前に sed、awk、または m4 マクロ プロセッサを使用して、MY_ASSERT(expr) を複数行に展開する DEBUG コードを生成するか、本番用にそれらを削除する NODEBUG コードを生成できます。

于 2020-06-15T18:41:04.433 に答える