2017-01-31 9 views
3

操作がスコープ内で実行される場合、同じスコープで取り消されなければならないという要件があるとします。その例は、クリティカルセクションに入ったり出たりすることです。ペアリングを使用するユーザーを強制するためにマクロで開いている/閉じる中括弧を使ってCでペアを強制する

ん - 操作を取り消し、マクロのペアが定義されて開閉括弧を使用します。

もちろん
#define BEGIN \ 
{ \ 
    do_something(); 

#define END \ 
    undo_something(); \ 
} 

、その「悪意のある」コーダができる方法があります。 (例えば、中かっこを追加するなどして)これらのマクロをトリックしますが、これは一般にBEGINの後にENDが続かなければならないことを思い出すのに役立ちます。さらに、例えば、既存のBEGINがコメントアウトされている場合、コンパイラは、ENDも削除する必要があることを示す文句を言います。

私はいくつかの内部プロジェクトを見てきました。私が言ったように、それは100%の保護を提供していませんが、この練習は何らかの形で有害であると証明することができますか?これはよく知られた練習ですか?

FOO({ 
    //your code here 
}) 

-

編集:

+0

のミスさえ量が検出できないことがあります。 –

+0

私が言ったように、エラーが検出されない方法はたくさんありますが、実際に何らかの形でそれが実際に有害なものになるかどうかを尋ねています。 – Kostas

+0

'do_something'と' undo_something'に 'begin()'と 'end()'という名前を使うのはなぜですか?また、大文字と小文字を区別するためには 'Begin()'と 'End()'を使います。 – rici

答えて

4

私は質問を理解していれば、あなたがBEGIN/ENDの使用を強制します。

あなたはgoto悪名高い使用していることを行うことができます。

error: label ‘check_label_end’ used but not defined 

EDIT:

BEGINEND部分なしで使用されている場合は、

#define BEGIN \ 
do { \ 
    goto check_label_end; \ 
label_begin: \ 
    puts("begin"); \ 
} while (0); 

#define END \ 
do { \ 
    puts("end"); \ 
    break; \ 
check_label_end: \ 
    goto label_begin; \ 
} while (0); 

を、あなたがエラーを受け取りますコメントで@KlasLindbäckによって指摘されているように、このバージョンは私たちを制限しますeはBEGIN/ENDであり、関数ごとに1回です。

あなたはいくつかのブロックを使用するためにラベル名を渡すことができます:

#define BEGIN(op) \ 
do { \ 
    goto check_label_end_##op; \ 
label_begin_##op: \ 
    puts("begin"); \ 
} while (0); 

#define END(op) \ 
do { \ 
    puts("end"); \ 
    break; \ 
check_label_end_##op: \ 
    goto label_begin_##op; \ 
} while (0); 

BEGIN(undo) 
... 
END(undo) 

BEGIN(something_else) 
... 
END(something_else) 
+1

Nifty。これは、関数ごとに 'BEGIN' /' END'の使用を1回制限することに注意してください。 –

+0

これが実際的であるためには、意図した**ペアがお互いを見つけるような方法で、マクロの使用ごとに異なるラベル名を生成する方法が必要です。特別なテクニックをまったく使用しない場合に比べてバグを書く機会が本当に少なくなるかどうかはわかりません。 – MikeMB

+1

これはクールなトリック感謝です!私が見る唯一のコンセプトは、コンパイラがそれらを最適化しない限り、BEGIN中に生成されたコードが2つの不要なジャンプを行うことです(私はそうではないと思います)。 – Kostas

0
#define FOO(body) {\ 
    do_something();\ 
    body\ 
    undo_something();\ 
} 

あなたが好きこのマクロを使用

さて、ストーリーテラーの例では、私は見たことがないように、そう簡単に自分のコードを壊します。

私はめったに1行に複数の宣言を書いていないと思います。

コンマ演算子と関数は、単純なケースではa=(1,2);またはmemcpy(&a,&b,1);

+3

[これは実用的ではありません](http://ideone.com/noMaKK) - 単一のコンマで終了します。 – StoryTeller

+0

@StoryTellerを '__VA_ARGS__'に切り替えると、簡単に修正できます:) – Quentin

+0

@StoryTeller私はそのようなコードをたくさん書いていました。自分の趣味のプロジェクトでは決して問題はありません。 – cshu

2

のように壊れることはありません呼び出すこれはあなたのコードを壊すところ、私は問題が表示されていないが、あなたはscenarious構築することができます。

BEGIN 
... 
for(...) { 
    END 
    ... 
    BEGIN 
} 
... 
END 
0

I BEGIN/ENDが他のブロックと同じ方法で(インデントされて)使用されるならば、深刻な問題はありません。

明白な注意点は、return/longjmp/gotoがパターンを破ることですが、すべてのコーダーがこれを知っている限り、それほど大きな問題ではありません。

例:

void foo() { 
    ... 
    BEGIN 
    ... 
    if (some_condition) { 
     return; // Cleanup code skipped 
    } 
    ... 
    END 
    ... 
} 
+1

公正であるために、 'longjmp/goto'は、不用意に使用された場合、どのパターンが破損しても非常に良好です。 – StoryTeller

2

これを行うための賢明な方法はマクロを通じて、しかし機能を通じてではありません。たとえば、次のように

typedef void task_t (void); 

inline void do_critical_stuff (task_t* task) 
{ 
    enter_critical_section(); 
    task(); 
    leave_critical_section(); 
} 

それとも、完全に可変の何かを探している場合:

inline void do_stuff (begin_t* begin, task_t* task, end_t* end) 
{ 
    begin(); 
    task(); 
    end(); 
} 
関連する問題