2017-01-31 16 views
3

범위 내에서 작업이 수행되면 동일한 범위에서 실행 취소해야한다는 요구 사항이 있다고 가정합니다. 예를 들어 중요한 부분에 들어가고 떠나는 것입니다. - 짝을 사용하는 사용자를 강제하기 위해매크로에서 열기/닫기 중괄호를 사용하여 C에서 페어링 적용

할 작업을 실행 취소, 매크로 한 쌍의 열고 닫는 중괄호 사용하는 정의 : 방법이 있습니다, 물론

#define BEGIN \ 
{ \ 
    do_something(); 

#define END \ 
    undo_something(); \ 
} 

을하는 "악성"코더 수 (예 : 여는 또는 닫는 중괄호를 추가하여) 매크로를 트릭하지만 일반적으로 BEGIN 뒤에 END이 와야한다는 것을 기억하는 데 도움이됩니다. 또한 예를 들어, 기존 BEGIN이 주석 처리 된 경우 컴파일러는 END도 제거해야 함을 나타내는 불만을 표시합니다.

본 내용은 여러 가지 내부 프로젝트입니다. 내가 말했듯이, 그것은 100 % 보호를 제공하지는 않지만이 방법이 어떤 식 으로든 해를 입을 수 있습니까? 이것은 잘 알려진 방법입니까?

+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 

편집 :

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이고 함수 당 한 번입니다. 실수

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

-

편집 :

좋아

, 이야기꾼의 예는 내가 본 적이없는 방법으로 그렇게 쉽게 내 코드를 나누기.

나는 한 줄에 여러 개의 선언문을 작성하지는 않는다.

간단한 경우를 위해 나는이 문제가 표시되지 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(); 
}