2017-01-31 6 views
3

Предположим, что у меня есть требование, чтобы, если операция выполняется в пределах области действия, она должна быть отменена в той же области. Примером этого является ввод и выход из критического раздела.Использование открытых/закрытых фигурных скобок в макросах для обеспечения согласования в C

Чтобы принудить пользователей использовать в паре делать - отменять операции, пара макросов определяется, что использовать открывающие и закрывающие фигурные скобки:

#define BEGIN \ 
{ \ 
    do_something(); 

#define END \ 
    undo_something(); \ 
} 

Конечно, есть способы, что «злой» кодировщик может обмануть эти макросы (например, добавив открывающую или закрывающую скобу), но в целом это помогает запомнить, что за BEGIN должен следовать END. Более того, если, например, прокомментирован существующий BEGIN, компилятор будет жаловаться, указывая, что также необходимо удалить END.

Я видел, что это несколько внутренних проектов. Как я уже сказал, он не предлагает 100% защиты, но может ли эта практика оказаться вредной? Это известная практика?

+0

даже количество ошибок может остаться незамеченным. –

+0

Как я уже сказал, есть много способов, по которым ошибки могут быть незамеченными, но я спрашиваю, может ли это на самом деле быть вредным каким-либо образом (т. Е. Создать код неожиданным образом). – Kostas

+0

Почему бы вам просто не использовать имена 'begin()' и 'end()' для 'do_something' и' undo_something'? Или 'Begin()' и 'End()', если вы хотите, чтобы они выделялись больше, а ваш палец для заглавных букв нуждался в упражнениях. – rici

ответ

4

Если я правильно понял вопрос, который вы хотите, чтобы обеспечить использование BEGIN/END.

Вы можете сделать это с помощью печально известного goto:

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

Затем, если BEGIN используется без END части вы получите сообщение об ошибке:

error: label ‘check_label_end’ used but not defined 

EDIT:

Как отметил @ KlasLindbäck в комментариях, эта версия ограничивает нас e от BEGIN/END до одной функции.

Вы можете передать имя метки для того, чтобы использовать несколько блоков:

#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 »до ​​одной функции. –

+0

Для того, чтобы это было практично, вам нужно создать отдельные имена ярлыков для каждого использования макроса таким образом, чтобы ** ** ** пары находили друг друга. Я не уверен, что это действительно уменьшает вероятность написания ошибки по сравнению с отсутствием специальной техники. – MikeMB

+1

Это классный трюк! Единственное, что я вижу, это то, что сгенерированный код сделает два ненужных перехода в BEGIN, если компиляторы не оптимизируют их (я думаю, что они этого не делают). – Kostas

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

Вы можете использовать этот макрос как:

FOO({ 
    //your code here 
}) 

-

Edit:

Хорошо, пример Рассказчика разбивает мой код так легко в пути я никогда не видел.

Я думаю, что я редко пишу несколько деклараций в одной строке.

Comma оператор и функция не называет не ломаются, как 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(); 
}