2008-12-05 6 views
2

업데이트 : 분명히 매크로가 아닌 템플릿이나 기본 클래스를 사용하여이 작업을 수행하고 싶을 것입니다. 불행히도 여러 가지 이유로 템플릿이나 기본 클래스를 사용할 수 없습니다. 순간 이 전 처리기 매크로를 #include로 대체하려면 어떻게합니까?


나는 다음과 같이 필드와 다양한 클래스의 메소드의 무리를 정의하는 매크로를 사용하고 있습니다 :

class Example 
{ 
    // Use FIELDS_AND_METHODS macro to define some methods and fields 
    FIELDS_AND_METHODS(Example) 
}; 

FIELDS_AND_METHODS이 stringizing 및 token-를 사용하는 멀티 라인 매크로입니다 운영자 붙여 넣기.

내가 여기

class Example 
{ 
    // Include FieldsNMethods.h, with TYPE_NAME preprocessor symbol 
    // defined, to achieve the same result as the macro. 
    #define TYPE_NAME Example 
    #include "FieldsNMethods.h" 
}; 

내가 클래스 (매크로에 이전 매개 변수)의 이름을 #define한다 것은 다음과 같은 종류의, 그리고 FieldsNMethods.h 파일이 대체하고자는의 내용을 포함 원래 매크로. 그러나 저는 #include 때문에 런타임에 코드를 단계별로 디버깅 할 수 있습니다.

그러나 FieldsNMethods.h 파일의 TYPE_NAME 전 처리기 기호에 '문자열을 붙여서'토큰을 붙여 넣는 데 문제가 있습니다.

예를 들어, 나는 FieldsNMethods.h에서 클래스의 소멸자를 정의하려면, 그래서 이것은 다음과 같이 TYPE_NAME의 값을 사용해야합니다 :

~TYPE_NAME() 
{ 
    //... 
} 

그러나 TYPE_NAME

는 그 값으로 대체.

내가 시도하고있는 것은 무엇입니까? 나는 매크로 정의에 있지 않기 때문에 문자열 화 및 토큰 붙여 넣기 연산자를 직접 사용할 수 없습니다.

답변

6

이것은 템플릿을 요구합니다.

class Example<class T> 
{ 
    ...class definition... 
}; 

질문의 마지막 부분에 직접 대답 - "나는 매크로 정의에 아니에요 주어진 더, 내가 붙여 넣기 및 stringizing 운영자가 작동하도록 어떻게 어떤"- 당신은 할 수 없습니다 "입니다 ". 이러한 연산자는 매크로에서만 작동하므로 매크로 호출을 작성하여 작동하게해야합니다.

추가는 :

@mackenir은 "템플릿 옵션이 아니다"고 말했다. 템플릿이 왜 옵션이 아닌가? 이 코드는 템플릿을 구식의 사전 표준, 사전 템플릿 방식으로 시뮬레이션하고 있으므로 많은 고통과 슬픔을 느끼게합니다. 템플릿을 사용하면 고통을 피할 수 있지만 전환 작업이있을 수 있습니다.

@mackenir는 "매크로가 작동하도록 만드는 방법이 있습니까?"라고 물었습니다. 예, 할 수는 있지만 템플릿을 사용해야합니다. 더 안정적이고 유지 관리가 가능합니다. 매크로로 작동하게하려면 포함 된 헤더의 매크로 호출 매크로에서 코드의 함수 이름을 가져야합니다. 당신이 올바르게 작동하도록하기 위해 간접 수준을 통과해야합니다

#define PASTE_NAME(x, y) PASTE_TOKENS(x, y) 
#define PASTE_TOKENS(x, y) x ## y 

#define TYPE_NAME Example 
int PASTE_NAME(TYPE_NAME, _function_suffix)(void) { ... } 

간접의이 수준은 모두 토큰 화 및 stringizing 사업자 자주 필요한 관용구이다.


@mackenir의 추가 의견은 지속적인 문제를 나타냅니다. 콘크리트로 만들어 보겠습니다. 순간

나는 다음과 같이 필드와 다양한 클래스의 메소드의 무리를 정의하는 매크로를 사용하고 있습니다 :

class Example 
{ 
    // Use FIELDS_AND_METHODS macro to define some methods and fields 
    FIELDS_AND_METHODS(Example) 
}; 

FIELDS_AND_METHODS이 stringizing를 사용하는 멀티 라인 매크로입니다 및 토큰 붙여 넣기 연산자.

나는

class Example 
{ 
    // Include FieldsNMethods.h, with TYPE_NAME preprocessor symbol 
    // defined, to achieve the same result as the macro. 
    #define TYPE_NAME Example 
    #include "FieldsNMethods.h" 
}; 

확인 일의 다음과 같은 종류의이 대체하고 싶습니다. 이를 구체화하려면 멀티 라인이고 토큰 붙여 넣기를 사용하는 FIELDS_AND_METHODS(type) 매크로가 필요합니다 (필자는 문자열 화를 다루지 않을 것입니다 - 동일한 기본 메커니즘이 적용될 것입니다). 운이

#define FIELDS_AND_METHODS(type) \ 
    type *next; \ 
    type() : next(0) { } \ 
    type * type ## _next() { return next; } 

이 그 포인터를 반환, 그 유형에 대한 생성자와 (이 경우 Example_next) 방법 유형 '인수 유형에 대한 포인터'의 멤버를 선언합니다.

그래서 이것은 매크로 일 수 있으며 '#include'가 동일한 작업을 수행하도록 매크로를 교체해야합니다.

fieldsNmethods.h의 내용이된다 : 헤더가 다중 포함 경비를 포함하지 않을 것이라고

#ifndef TYPE_NAME 
#error TYPE_NAME not defined 
#endif 
#define FNM_PASTE_NAME(x, y) FNM_PASTE_TOKENS(x, y) 
#define FNM_PASTE_TOKENS(x, y) x ## y 

TYPE_NAME *next; 
TYPE_NAME() : next(0) { } 
TYPE_NAME * FNM_PASTE_NAME(TYPE_NAME, _next)() { return next; } 

#undef FNM_PASTE_NAME 
#undef FNM_PASTE_TOKENS 

참고; 그것의 존재 이유는 그것이 여러 번 포함되도록 허용하는 것입니다. 또한 여러 개의 포함을 허용하도록 도우미 매크로를 정의하지 않습니다 (다시 정의하면 동일 할 것이므로 '양성'이며 오류를 일으키지 않으므로) 매크로에 기본 네임 스페이스 컨트롤로 FNM_이라는 접두사를 붙입니다. 이것은 C 프리 프로세서에서 기대할 수있는 코드를 생성합니다. G ++는 빈 객체 파일을 생성하지 않습니다 (선언 된 유형이 예제 코드에서 사용되지 않았기 때문에).

질문에 설명 된 것을 제외하고는 호출 코드를 변경할 필요가 없습니다. 나는 질문이 SPOT "진리의 단일 포인트"를 사용하여 개선해야한다고 생각 원칙 (또는 DRY "자신을 반복하지 마십시오") :

#define TYPE_NAME Example 
class TYPE_NAME 
{ 
    // Include FieldsNMethods.h, with TYPE_NAME preprocessor symbol 
    // defined, to achieve the same result as the macro. 
    #include "FieldsNMethods.h" 
}; 
+1

감사합니다. 템플리트는 옵션이 아닙니다. FieldsNMethods.h의 문자열 및 토큰 붙여 넣기 연산자와 동일한 효과를 얻을 수있는 방법이 있다는 것을 의미합니까? – mackenir

+1

클래스는 공개 된 API이며 템플릿을 만들거나 기본 클래스를 사용하여 클래스를 오염시키지 않습니다. 또한 C++/CLI 클래스이므로 개인 상속은 옵션이 아닙니다. 추가 정보를 주셔서 감사합니다, 나는 그것을 밖으로 시도 할 것입니다. – mackenir

+1

'pollute'나는 'API 사용자를 혼란스럽게 할 수있는 방식으로 구현 세부 정보를 노출합니다.' – mackenir

1

아니, 당신은 즉시 클래스 또는 함수 정의를 정의 할 수 없습니다 . 직접 입력하거나 전처리기에 정의하여 지정해야합니다.

일반적으로 이와 같은 클래스를 생성 할 필요가 없으며 모든 유형을 입력하거나 일종의 코드 생성을 사용하여 컴파일하기 전에 클래스 정의가 만들어집니다. 때로는 별도의 코드 생성 단계가 있습니다 (예 : 현재 Visual Studio에서 사전 및 사후 처리 단계를 정의 할 수 있음).

이제 다른 데이터 유형에 대해 다른 클래스의 클래스를 만들어야 할 경우 템플릿을 사용합니다. 그런 방식으로 다른 이름의 도장 클래스를 만들 수는 없습니다.

마지막 질문 하나 : 왜 이것을하고 있습니까?나는 C++에서 유용하게 보일만한 위치에있는 적이 한번도 없었습니다. 그리고 언어를 이해하는 것이 언어를 구현할 수있는 곳에서는 말이죠.

당신은 다른 매크로로 stringifying을 포장한다
+0

Im은 매크로를 매개 변수화 할 수있는 것과 같은 방법으로 #include를 효과적으로 '매개 변수화'합니다. 따라서 진행중인 세대는 없습니다. 그것은 모두 (실제로, 전에) 컴파일 시간입니다. – mackenir

3

당신은 매크로의 추가 레이어를 추가 할 필요가 FieldsNMethods.h

에서

#define MAKE_STR_X(_v) # _v 
#define MAKE_STR(_v) MAKE_STR_X(_v) 

char *method() { return MAKE_STR(TYPE_NAME); } 
+0

감사합니다. 그 약속은 들리지만 나는 그것을 시도 할 것입니다. – mackenir

5

(이 때문에 프리 프로세서가 작동하는 방법의 필요) :

#define STRINGIZE(x) STRINGIZE2(x) 
#define STRINGIZE2(x) #x 

#define TOKENPASTE(x, y) TOKENPASTE2(x, y) 
#define TOKENPASTE2(x, y) x ## y 

매크로가있는 경우 전 처리기는 일반적으로 매크로 대체를 수행하기 전에 인수를 반복적으로 확장합니다. 그러나 문자열 화 연산자 # 또는 토큰 붙여 넣기 연산자 ##에 인수가 사용되면 이 아닌이 확장됩니다. 따라서 매크로의 추가 레이어가 필요합니다. 첫 번째 레이어는 인수를 확장하고 두 번째 레이어는 문자열 또는 붙여 넣기를 수행합니다.

인수를 여러 번 확장해야하는 경우 (예 : #define A B, #define B C, #define C D, STRINGIZE(A)) # 또는 ## 연산자를 적용하기 전에 더 많은 계층을 추가해야합니다.