C / C ++ 매크로의 쉼표


103

이와 같은 매크로가 있다고 가정 해 보겠습니다.

#define FOO(type,name) type name

다음과 같이 사용할 수 있습니다.

FOO(int, int_var);

그러나 항상 그렇게 간단하지는 않습니다.

FOO(std::map<int, int>, map_var); // error: macro "FOO" passed 3 arguments, but takes just 2

물론 우리는 할 수 있습니다 :

 typedef std::map<int, int> map_int_int_t;
 FOO(map_int_int_t, map_var); // OK

인체 공학적이지 않습니다. Plus 유형 비 호환성을 처리해야합니다. 이 문제를 매크로로 해결하는 방법을 알고 계십니까?


나는 당신이 문자를 문자로 만들기 위해 의미를 가진 문자를 이스케이프해야한다고 생각합니다.
Jite

적어도 C ++에서는 어디에나 typedef를 넣을 수 있습니다. 그래서 왜 "사전에"라고 말하는지 잘 모르겠습니다.
Vaughn Cato

답변:


108

각괄호도 나타낸다 (또는 발생) 할 수 있기 때문에, 비교 연산자 <, >, <=>=는 괄호하는 것처럼, 매크로 확장 각괄호 내부 쉼표를 무시할 수 없다. (대괄호와 중괄호는 일반적으로 균형 잡힌 쌍으로 발생하지만 이는 또한 문제입니다.) 매크로 인수를 괄호로 묶을 수 있습니다.

FOO((std::map<int, int>), map_var);

문제는 매개 변수가 매크로 확장 내에서 괄호로 묶여있어 대부분의 컨텍스트에서 유형으로 읽히지 못하게한다는 것입니다.

이 문제를 해결하는 좋은 방법은 C ++에서 함수 유형을 사용하여 괄호로 묶인 유형 이름에서 유형 이름을 추출 할 수 있다는 것입니다.

template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
FOO((std::map<int, int>), map_var);

함수 유형을 형성하면 추가 괄호를 무시하므로 유형 이름에 쉼표가 포함되지 않은 괄호를 포함하거나 포함하지 않고이 매크로를 사용할 수 있습니다.

FOO((int), int_var);
FOO(int, int_var2);

물론 C에서는 유형 이름이 괄호 밖에 쉼표를 포함 할 수 없기 때문에 이것이 필요하지 않습니다. 따라서 다국어 매크로의 경우 다음과 같이 작성할 수 있습니다.

#ifdef __cplusplus__
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
#else
#define FOO(t,name) t name
#endif

굉장합니다. 그러나 이것에 대해 어떻게 알게 되었습니까? 나는 수많은 트릭을 시도해 왔으며 함수 유형이 문제를 해결할 것이라고 생각조차하지 못했습니다.
윌 Custode

@WilliamCustode 내가 회상하는 것처럼 가장 성가신 구문 분석 문제와 관련하여 함수 유형 및 함수 선언의 문법을 연구하고 있었기 때문에 중복 괄호가 해당 컨텍스트의 유형에 적용될 수 있다는 것을 알고 있다는 것은 우연이었습니다.
ecatmur

템플릿으로 작업 할 때이 방법에서 문제를 발견했습니다. 내가 원했던 코드가 다음과 같다고 가정 해 봅시다. template<class KeyType, class ValueType> void SomeFunc(FOO(std::map<KeyType, ValueType>) element) {}여기에이 솔루션을 적용하면 매크로 뒤의 구조체가 종속 유형이되고 이제 유형에 typename 접두사가 필요합니다. 추가 할 수 있지만 유형 추론이 손상되었으므로 이제 함수를 호출하려면 유형 인수를 수동으로 나열해야합니다. 결국 쉼표에 대한 매크로를 정의하는 temple의 방법을 사용했습니다. 예쁘지 않을 수도 있지만 완벽하게 작동했습니다.
Roger Sanders

대답에 작은 문제 : 그것은 쉼표 내부 무시됩니다 주장 [] 하고 {}그들은 단지와 함께 작동하지 않습니다, ()슬프게도. 참조 : 그러나 균형을 맞추기 위해 대괄호 나 중괄호가 필요하지 않습니다 ...
VinGarcia

불행히도 이것은 MSVC에서 작동하지 않습니다 : godbolt.org/z/WPjYW8 . MSVC가 여러 괄호를 추가하는 것을 허용하지 않고 구문 분석에 실패한 것 같습니다. 우아하지는 않지만 더 빠른 (템플릿 인스턴스화가 적음) 해결책은 쉼표로 된 인수를 래퍼 매크로로 래핑하는 것 #define PROTECT(...) argument_type<void(__VA_ARGS__)>::type입니다. 인수 전달은 이제 여러 매크로를 통해서도 쉽게 가능하며 단순한 유형의 경우 PROTECT를 생략 할 수 있습니다. 그러나 함수 유형은 다음과 같이 평가 될 때 함수 포인터가됩니다
Flamefire

119

괄호를 사용할 수없고 Mike의 SINGLE_ARG 솔루션이 마음에 들지 않으면 COMMA를 정의하십시오.

#define COMMA ,

FOO(std::map<int COMMA int>, map_var);

이것은 또한 다음과 같이 일부 매크로 인수를 문자열 화하려는 경우에도 도움이됩니다.

#include <cstdio>
#include <map>
#include <typeinfo>

#define STRV(...) #__VA_ARGS__
#define COMMA ,
#define FOO(type, bar) bar(STRV(type) \
    " has typeid name \"%s\"", typeid(type).name())

int main()
{
    FOO(std::map<int COMMA int>, std::printf);
}

인쇄합니다 std::map<int , int> has typeid name "St3mapIiiSt4lessIiESaISt4pairIKiiEEE".


16
#define COMMA 와우, 당신은 저에게 몇 시간의 작업을 저장했습니다 ... 왜 몇 년 전에이 생각을하지 않았는지. 이 아이디어를 공유해 주셔서 감사합니다. 이를 통해 서로 다른 인수 개수로 함수를 설정하는 매크로를 만들 수도 있습니다.
moliad

28
공포를위한 플러스 1
namezero

1
@kiw 만약 당신 #define STRVX(...) STRV(__VA_ARGS__)#define STRV(...) # __VA_ARGS__, 그러면 std::cout << STRV(type<A COMMA B>) << std::endl;인쇄 type<A COMMA B>하고 std::cout << STRVX(type<A COMMA B>) << std::endl;인쇄 할 것 type<A , B>입니다. ( STRV"가변 캐릭터 라인 화"로하고, STRVX"확장 된 가변 캐릭터 라인 화"에 대한 것이다.)
하지-A 사용자

1
@ not-a-user 예,하지만 가변 매크로를 사용 COMMA하면 처음에 매크로가 필요하지 않습니다 . 그게 내가 끝낸 것입니다.
kiw

나는 그것을 사용하지 않을 것이지만, 웃기는 것에 +1.
Rafael Baptista 2016

58

전처리 기가 가변 매크로를 지원하는 경우 :

#define SINGLE_ARG(...) __VA_ARGS__
#define FOO(type,name) type name

FOO(SINGLE_ARG(std::map<int, int>), map_var);

그렇지 않으면 좀 더 지루합니다.

#define SINGLE_ARG2(A,B) A,B
#define SINGLE_ARG3(A,B,C) A,B,C
// as many as you'll need

FOO(SINGLE_ARG2(std::map<int, int>), map_var);

오, 이런 ... 왜? 괄호로 묶지 않는 이유는 무엇입니까?

15
@VladLazarenko : 항상 임의의 코드 조각을 괄호 안에 넣을 수는 없기 때문입니다. 특히 선언자에서 유형 이름을 괄호로 묶을 수는 없습니다. 이것이 바로이 인수가되는 것입니다.
Mike Seymour

2
... 그리고 또한 매크로 정의 를 수정할 수있을뿐 아니라 매크로 정의 를 호출하는 모든 위치 를 수정할 수 없기 때문입니다. 예를 들어 같은 이름의 함수에서 업무를 인계하기 위해 매크로를 추가 할 때 발생합니다.
BeeOnRope

32

그냥 정의 FOO

#define UNPACK( ... ) __VA_ARGS__

#define FOO( type, name ) UNPACK type name

그런 다음 항상 유형 인수를 괄호로 묶어 호출하십시오.

FOO( (std::map<int, int>), map_var );

물론 매크로 정의에 대한 주석에서 호출을 예시하는 것이 좋습니다.


이것이 왜 그렇게 멀리 떨어지는 지 잘 모르겠지만 Mike Seymours보다 훨씬 더 좋은 솔루션입니다. 빠르고 간단하며 사용자에게 완전히 숨겨져 있습니다.
iFreilicht 2016

3
@iFreilicht : 1 년이 조금 지난 후에 게시되었습니다. ;-)
건배와 hth. - 알프

5
또한 있기 때문에 이해하기 어렵다 어떻게 그리고 왜 그것을 작동
VinGarcia

@VinGarcia, 왜 / 어떻게 작동하는지 설명 할 수 있습니까? 호출 할 때 괄호가 필요한 이유는 무엇입니까? 이렇게 UNPACK사용하면 ) UNPACK type name어떻게 되나요? 에서 type사용할 때 유형을 올바르게 가져 오는 이유 는 무엇 ) UNPACK type name입니까? 도대체 여기서 무슨 일이 일어나고 있습니까?
사용자

어떤 @user는, 어쩌면 환호와 HTH는 대답 할 수 없다
VinGarcia

4

이를 수행하는 방법은 최소한 두 가지가 있습니다. 먼저 여러 인수를받는 매크로를 정의 할 수 있습니다.

#define FOO2(type1, type2, name) type1, type2, name

그렇게하면 더 많은 인수를 처리하기 위해 더 많은 매크로를 정의하게 될 수 있습니다.

둘째, 인수를 괄호로 묶을 수 있습니다.

#define FOO(type, name) type name
F00((std::map<int, int>) map_var;

그렇게하면 여분의 괄호가 결과의 구문을 망칠 수 있습니다.


첫 번째 솔루션의 경우 매크로가 오버로드되지 않기 때문에 각 매크로의 이름이 달라야합니다. 두 번째로 유형 이름을 전달하는 경우 변수 (또는 typedef)를 선언하는 데 사용될 가능성이 매우 높으므로 괄호로 인해 문제가 발생합니다.
James Kanze

4

이것은 P99에서 가능합니다 .

#include "p99/p99.h"
#define FOO(...) P99_ALLBUTLAST(__VA_ARGS__) P99_LAST(__VA_ARGS__)
FOO()

위의 코드는 인수 목록에서 마지막 쉼표 만 효과적으로 제거합니다. 확인하십시오 clang -E(P99에는 C99 컴파일러가 필요함).


3

간단한 대답은 할 수 없다는 것입니다. 이것은 <...>템플릿 인수 에 대한 선택의 부작용입니다 . <>매크로 메커니즘이 괄호 처리처럼 처리하도록 확장 될 수 있도록 언밸런스 상황에 나타납니다. (일부위원회 위원들은 다른 토큰에 대해 주장 (^...^)했지만를 사용하여 대부분의 문제를 설득 할 수 없었습니다 <...>.)


2
(^...^)이것은 하나의 행복한 얼굴입니다 :)
CygnusX1
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.