2016-01-07 15 views
5

メモリの制約がある組み込みアプリケーションのファームウェアを開発しています。私は受け取ったときに処理する必要のある一連のコマンドを持っています。各コマンドは異なる「バケット」の下にあり、各「バケット」は有効なコマンド番号の範囲を取得します。これを実現するために、以下に示す2つのENUMを作成しました。私のコマンドハンドラ関数は、コマンドコードを受信ENUMをビットマップとして使用してCで検証する方法

enum 
{ 
    BUCKET_1 = 0x100, // Range of 0x100 to 0x1FF 
    BUCKET_2 = 0x200, // Range of 0x200 to 0x2FF 
    BUCKET_3 = 0x300, // Range of 0x300 to 0x3FF 
    ... 
    ... 
    BUCKET_N = 0xN00 // Range of 0xN00 to 0xNFF 
} cmd_buckets; 

enum 
{ 
    //BUCKET_1 commands 
    CMD_BUCKET_1_START = BUCKET_1, 
    BUCKET_1_CMD_1, 
    BUCKET_1_CMD_2, 
    BUCKET_1_CMD_3, 
    BUCKET_1_CMD_4, 
    //Add new commands above this line 
    BUCKET_1_CMD_MAX, 

    //BUCKET_2 commands 
    CMD_BUCKET_2_START = BUCKET_2, 
    BUCKET_2_CMD_1, 
    BUCKET_2_CMD_2, 
    BUCKET_2_CMD_3, 
    //Add new commands above this line 
    BUCKET_2_CMD_MAX, 

    //BUCKET_3 commands 
    ... 
    ... 
    ... 

    //BUCKET_N commands 
    CMD_BUCKET_N_START = BUCKET_N 
    BUCKET_N_CMD_1, 
    BUCKET_N_CMD_2, 
    BUCKET_N_CMD_3, 
    BUCKET_N_CMD_4, 
    //Add new commands above this line 
    BUCKET_N_CMD_MAX, 
}cmd_codes 

は、それはコマンドがそれを処理する前に有効になっているかどうかを確認する必要があります。私はこのためにビットマップを使用する予定です。実行時に処理からコマンドを有効または無効にすることができます。私は各グループにintを使うことができます(グループごとに32のコマンドを与えます、私は0xN00から0xN20が有効なコマンドコードであり、範囲内の他のコードは無駄であることに気づきます)。コマンドコードが無駄になっても、デザインデータの選択は、コンソール上で生データを見るときにコマンドコードのグループに簡単に伝えるという利点があります。

多くの開発者が 'cmd_codes'列挙型にコマンドを追加できます(必要に応じて新しいバケットを 'cmd_buckets'列挙型に追加することもできます)ので、各バケットのコマンドコードの数が32(ビットマップはintです)。私は実行時間ではなくコンパイル時にこれをキャッチしたい。以下のように各BUCKET_N_CMD_MAXの値をチェックし、コンパイル時エラーを投げる以外に、よりよい解決法がありますか?

#if (BUCKET_1_CMD_MAX > 0x20) 
#error ("Number of commands in BUCKET_1 exceeded 32") 
#endif 

#if (BUCKET_2_CMD_MAX > 0x20) 
#error ("Number of commands in BUCKET_2 exceeded 32") 
#endif 

#if (BUCKET_3_CMD_MAX > 0x20) 
#error ("Number of commands in BUCKET_3 exceeded 32") 
#endif 
... 
... 
... 
#if (BUCKET_N_CMD_MAX > 0x20) 
#error ("Number of commands in BUCKET_N exceeded 32") 
#endif 

これをよりうまく設計する方法もありますか?

ありがとう、私はあなたの時間と忍耐を感謝します。

+0

コメントは作成できます。しかし、私はあなたが質問Stack Overflowの_Code Review_フォーラムのためのより適切であると思う。コードレビューに誰もこれを移すことができますか? –

+1

@PaulOgilvie質問は、スタックオーバーフローに適しているようです。コードレビューには、完全で実用的なコード例が必要です。 – Lundin

+1

この例は意味をなさない。 'BUCKET_1 = 0x100'という定数があり、' CMD_BUCKET_1_START = BUCKET_1'を割り当てます。したがって、末尾のenumは0x101、0x102、...を取得し、BUCKET_1_CMD_MAXは0x106になります。 0x106は常に0x20より大きいので、静的なアサートは常にトリガします。実例を投稿してください。 – Lundin

答えて

3

まず、コードのバグを修正してください。コメントに記載されているように、定数BUCKET_1 = 0x100があり、CMD_BUCKET_1_START = BUCKET_1を割り当てます。したがって、末尾のenumは0x101、0x102、...、BUCKET_1_CMD_MAXの値は0x106になります。 0x106は常に0x20より大きいので、静的なアサートは常にトリガします。

#define BUCKET_1_CMD_N (BUCKET_1_CMD_MAX - CMD_BUCKET_1_START) 
#define BUCKET_2_CMD_N (BUCKET_2_CMD_MAX - CMD_BUCKET_2_START) 
... 

上記と仮定すると、固定され、その後、あなたが単一で数多くのチェックを置き換えることができます。これは以下のように、それは実際には、代わりに列挙内の項目の合計数をチェックするようにすることを

修正マクロ。ない大きな改善が、少なくともそれは、コードの繰り返しを減らす:1つの定数が大きすぎる場合は、GCCから

#define BUCKET_MAX 32 // use a defined constant instead of a magic number 

// some helper macros:  
#define CHECK(n) BUCKET_ ## n ## _CMD_N 
#define STRINGIFY(n) #n 

// the actual macro: 
#define BUCKET_CHECK(n) \ 
    _Static_assert(CHECK(n) <= BUCKET_MAX, \ 
       "Number of commands in BUCKET_" STRINGIFY(n) "_CMD_N exceeds BUCKET_MAX."); 


// usage: 
int main (void) 
{ 
    BUCKET_CHECK(1); 
    BUCKET_CHECK(2); 
} 

出力:

error: static assertion failed: "Number of commands in BUCKET_1_CMD_N exceeds BUCKET_MAX." 
note: in expansion of macro 'BUCKET_CHECK' 

EDIT

バグを組み合わせた場合チェックマクロで修正すると、次のようになります。

+0

また、バグ修正とアサーションマクロをマージすることもできますが、 – Lundin

+0

CHECKヘルパーマクロを導入した理由がわかりません。何も追加しないことで可読性が低下します。また、あなたは解決策が実際にはコードの繰り返しを増やすと思います。なぜなら、今度は 'BUCKET_1_CMD_N' #definesと' BUCKET_CHECK'マクロ呼び出しのリストの両方を追加する必要があるからです。 – kfx

+0

@kfx "追加リスト"はありません。プログラムを動作させるために必要な機能です。しかし、私が上記のコメントに書いたように、BUCKET_CHECKマクロとマージすることができます。読みやすくするために: 'BUCKET_ ## n ## _CMD_N <= BUCKET_MAX'が読みやすくならば、そのフォームを使ってください...すべての行を1行に書くこともできます... – Lundin

1

まず、プリプロセッサコマンドはそのようには機能しません。 Cプリプロセッサは、#defineステートメントで指示された名前だけを "参照"することも、コンパイラフラグとして渡すこともできます。 enumの一部またはconstキーワードの一部として定義された定数を参照することはできません。プリプロセッサの代わりにコマンドを検証するには、_Static_assertを使用する必要があります。

コマンドについては、私は範囲0..0x20で番号がすべてのコマンド持つことを示唆している:

enum { 
    BUCKET_1_CMD_1, 
    BUCKET_1_CMD_2, 
    ... 
    BUCKET_1_CMD_MAX, 
}; 
enum { 
    BUCKET_2_CMD_1, 
    BUCKET_2_CMD_2, 
    ... 
    BUCKET_2_CMD_MAX, 
}; 

を次にあなたはすべてのコマンドが有効な範囲内にあるかどうかを確認するために、単一のガード値が必要になります。

#define MAX_COMMAND 0x20 
_Static_assert(BUCKET_1_CMD_MAX <= MAX_COMMAND, "too many bucket 1 commands"); 
_Static_assert(BUCKET_2_CMD_MAX <= MAX_COMMAND, "too many bucket 2 commands"); 

は、バケットと一緒にビット単位またはそれらを "オフセット"、コマンドを使用するには:

enum { 
BUCKET_1 = 0x100, 
BUCKET_2 = 0x200, 
}; 
... 
int cmd = BUCKET_2 | BUCKET_2_CMD_1; 
+0

これはうまくいきますが、 'cmd_codes'型とそのコンパイラの型チェック(それは問題かもしれません)を失うという副作用があります。 – user694733

+0

ご回答ありがとうございます。私は '_Static_assert()'を使用しています。@ Lundinのアプローチを使用しています。 – CCoder

関連する問題