2011-04-12 2 views
14

M (0)과 N (0)의 결과가 다른 이유는 무엇입니까?C 전 처리기, 재귀 매크로

#define CAT_I(a, b) a ## b 
#define CAT(a, b) CAT_I(a, b) 

#define M_0 CAT(x, y) 
#define M_1 whatever_else 
#define M(a) CAT(M_, a) 
M(0);  // expands to CAT(x, y) 

#define N_0() CAT(x, y) 
#define N_1() whatever_else 
#define N(a) CAT(N_, a)() 
N(0);  // expands to xy 
+0

어 ... 여기 정확히 달성하려는 것은 무엇입니까 ... 그리고 어떤 목적으로? – t0mm13b

+15

나는 무엇인가를 이루고 싶지 않고, 뭔가를 할 때 이것을 알아 차렸고 그 이유에 대해 궁금합니다. 그것은 내가 이해할 수 없을 때 나를 귀찮게한다. :). – imre

답변

17

사실에서 추가 매개 변수 브래킷 CAT(M_, a)주의로 정의되는 반면, 매크로는 N(a) CAT(N_,a)()있다 , 그것은 언어 표준에 대한 귀하의 해석에 달려 있습니다. 예를 들어, MCPP 하에서 엄격 언어 표준 텍스트 따르는 전처리 구현은, 제 수율 CAT(x, y);뿐만 [추가 바꿈이 결과로부터 제거되었다]

C:\dev>mcpp -W0 stubby.cpp 
#line 1 "C:/dev/stubby.cpp" 
     CAT(x, y) ; 
     CAT(x, y) ; 
C:\dev> 

의 C a known inconsistency있다 ++ 언어 명세 (결함 목록이 C를위한 곳이 어디인지는 알 수 없지만 C 사양에는 동일한 불일치가있다.) 사양은 최종 CAT(x, y) 매크로 대체 할 수 없습니다. 그 의도는 매크로 대체되어야한다는 의도 일 수도 있습니다. 그것이 "비 교체"말씨와 의사 코드를 생성 할 수있는 시도 사이의 작은 차이가 있다고 여러 WG14 사람들에 의해 이해 된 1980 년대로 돌아 가기

:

링크 된 결함 보고서를 인용합니다.

"야생에서"현실적인 프로그램이이 지역을 탐험 할 의사가 없으며 불확실성을 줄이려는 시도가 구현이나 프로그램의 적합성 상태를 변경할 위험이 없다는 것이위원회의 결정이었습니다. 그래서


, 왜 우리는 가장 일반적인 처리기 구현과 N(0)보다 M(0)에 대한 다른 동작을받을 수 있나요? M 교체에서 CAT 번째 호출이 CAT의 최초 호출에 의한 토큰의 전체로 구성 대신 CAT(M, 0)으로 대체 할 정의

M(0) 
CAT(M_, 0) 
CAT_I(M_, 0) 
M_0 
CAT(x, y) 

M_0 경우, 여분 무한 재귀 것이다. 전 처리기 사양은 명시 적으로 매크로 교체를 중지하여이 "엄격하게 재귀"대체를 금지하므로 CAT(x, y) 매크로가 대체되지 않습니다. CAT 번째 호출이 토큰으로부터 부분적으로 형성되고, 여기

N(0) 
CAT(N_, 0)  () 
CAT_I(N_, 0) () 
N_0    () 
CAT(x, y) 
CAT_I(x, y) 
xy 

:

그러나 N의 교체에 CAT 번째 호출 CAT의 제 호출 따른 토큰 부분적 이루어져 CAT의 첫 번째 호출과 부분적으로 다른 토큰 인 의 ()의 호출로 인한 결과입니다. 대체는 엄격히 재귀 적이 아니므로 두 번째 호출 CAT이 대체되면 무한 재귀를 생성 할 수 없습니다.

1 :

+0

흥미 롭습니다 ... VC++ 및 온라인 Comeau 컴파일러의 전 처리기는 모두 N (0)을 "xy"로 확장합니다. – imre

+0

또한이 재귀 제한을 해결하고 마지막 CAT을 평가하는 것이 가능할 수 있습니까? (다른 대체 CAT를 정의하는 것 외에) – imre

+1

'N_0 '에 제공된'()'이 모든 매크로 확장 외부에서 왔기 때문에 * 새로운 * 매크로 확장으로 간주되므로 희미한 메모리가 있으므로 "blue 페인트 "는'CAT()'에서 벗어나 다시 한 번 확장 될 수 있습니다. 이것은 mcpp의 버그 일 수 있습니다. FWIW gcc는 Comeau와 VC++에 동의합니다. – zwol

0

는 발견하지 못했다 수도 뭔가 될 것 같다하지만, M (a)를 사용 ....

+0

알아. 따라서 N_0은 함수 스타일 (0 인수) 매크로로 정의됩니다. 그리고 어떤 이유로 그것은 재귀 평가에서 차이를 만드는 것처럼 보입니다. 그러나 나는 그 이유를 정확히 모릅니다. 그게 내 질문이다. – imre

4

그냥 순서를 따릅니다.

M(0); // expands to CAT(x, y) TRUE 
CAT(M_, 0) 
CAT_I(M_, 0) 
M_0 
CAT(x, y) 

2)

)에만 반복적으로 매크로를 교체 할 필요가

N(0); // expands to xy TRUE 
CAT(N_, 0)() 
CAT_I(N_, 0)() 
N_0() 
CAT(x, y) 
CAT_I(x, y) 
xy 

.

## 처리기 연산자에 대한 참고 사항 : 두 인수가 함께 ## 처리기 연산자를 사용하여 '접착'할 수있다; 이렇게하면 두 개의 토큰이 전처리 된 코드에서 연결될 수 있습니다.

표준 매크로 확장과 달리 전통적인 매크로 확장에는 재귀를 방지 할 수있는 조항이 없습니다. 개체 텍스트 매크로가 대체 텍스트에 인용되지 않은 것으로 나타나면 재검색 과정에서 다시 바뀝니다. 이렇게 무한히 적용됩니다. GCC는 재귀 매크로를 확장 할 때이를 감지하고 오류 메시지를 내고 문제가되는 매크로 호출 후에 계속합니다. (gcc online doc)

+1

음 ... 나는 아직도 그것을 얻지 못합니다. 두 시퀀스는 모두 같은 CAT (x, y)에 도달합니다. - 왜 한 경우에는 멈추고 다른 하나의 경우에는 멈추지 않습니까? – imre

+0

제임스 맥넬리스 (James McNellis)와 같은 표준의 해석에 의존하는 재귀라고 생각합니다. 좋은 질문 imre. –

+1

@imre :'M (0)'의 경우, 두 번째'CAT (...)'호출은 처음'CAT (...)'호출에서 완전히 발생하므로 엄격히 재귀 호출입니다. 'N (0)'의 경우, 두 번째'CAT (...)'호출은 첫 번째'CAT (...)'호출에서 부분적으로 만 발생하고 부분적으로는 그 뒤에 나오는 다른 토큰 (')'대체 목록에 'N'). 따라서, 전체적으로 재귀적인 것은 아닙니다. –