가변성 매크로를 만드는 방법 (가변 인수 수)


196

특정 숫자가 아닌 임의의 수의 매개 변수를 허용하는 매크로를 C로 작성하고 싶습니다.

예:

#define macro( X )  something_complicated( whatever( X ) )

X매개 변수의 수는 어디 입니까?

whatever과부하되어 2 또는 4 매개 변수로 호출 할 수 있기 때문에 이것이 필요합니다 .

매크로를 두 번 정의하려고했지만 두 번째 정의가 첫 번째 정의를 덮어 썼습니다!

내가 일하고있는 컴파일러는 g ++ (보다 구체적으로 mingw)입니다.


8
C 또는 C ++를 원하십니까? C를 사용하는 경우 C ++ 컴파일러로 컴파일하는 이유는 무엇입니까? 적절한 C99 variadic 매크로를 사용하려면 C ++에는 표준 variadic 매크로가 없으므로 C ++ 컴파일러가 아닌 C99 (gcc와 같은)를 지원하는 C 컴파일러로 컴파일해야합니다.
Chris Lutz

글쎄, 난 C ++ 가정이 점에서 C의 슈퍼 세트 ...입니다
하센

tigcc.ticalc.org/doc/cpp.html#SEC13 에는 가변성 매크로에 대한 자세한 설명이 있습니다.
Gnubie


3
미래 독자들을 위해 : C는 C ++의 서브 우편 이 아닙니다 . 그것들은 많은 것들을 공유하지만, 그것들이 서로의 부분 집합과 초 집합을 막는 규칙이 있습니다.
Pharap

답변:


295

C99 방식으로 VC ++ 컴파일러에서도 지원됩니다.

#define FOO(fmt, ...) printf(fmt, ##__VA_ARGS__)

8
나는 C99가 VA_ARGS 전에 ##을 요구한다고 생각하지 않는다 . VC ++ 일 수 있습니다.
Chris Lutz

98
VA_ARGS 이전의 ##의 이유 는 변수 인수 목록이 비어있는 경우 앞에 쉼표를 삼키기 때문입니다 . FOO ( "a")는 printf ( "a")로 확장됩니다. 이것은 gcc (그리고 아마도 vc ++)의 확장입니다. C99는 줄임표 대신 적어도 하나의 인수가 필요합니다.
jpalecek

110
##필요하지 않으며 휴대용이 아닙니다. #define FOO(...) printf(__VA_ARGS__)작업을 휴대용 방식으로 수행합니까? fmt파라미터 정의에서 생략 될 수있다.
alecov

4
IIRC에서, ##는 GCC의 특정 제로 매개 변수의 통과 허용
Mawg는 분석 재개 모니카 말한다

10
##-syntax는 llvm / clang 및 Visual Studio 컴파일러에서도 작동합니다. 따라서 이식성이 없지만 주요 컴파일러에서 지원합니다.
K. Biermann

37

__VA_ARGS__표준 방법입니다. 필요하지 않은 경우 컴파일러 관련 해킹을 사용하지 마십시오.

원래 게시물에 댓글을 달 수 없다는 것이 정말 짜증납니다. 어쨌든 C ++는 C의 상위 집합이 아닙니다. C ++ 컴파일러로 C 코드를 컴파일하는 것은 실제로 어리석은 일입니다. 도니가하지 않는 일을하지 마십시오.


8
"C ++ 컴파일러로 C 코드를 컴파일하는 것은 정말 어리석은 일입니다." => 모든 사람이 저를 포함하여 그렇게 생각하지는 않습니다. 예를 들어 C ++ 핵심 지침을 참조하십시오. CPL.1 : C ++보다 C 선호 , CPL.2 : C를 사용해야하는 경우 C 및 C ++의 공통 서브 세트를 사용하고 C 코드를 C ++로 컴파일하십시오 . "C-only-isms"가 호환되는 부분 집합에서 프로그래밍 할 필요가없는 것이 무엇인지 실제로 생각하고 C와 C ++위원회는 그 호환되는 부분 집합을 만들기 위해 열심히 노력해 왔습니다.
HostileFork는 SE를

4
@HostileFork Fair C ++ 사람들은 물론 C ++ 사용을 장려하고 싶습니다. 그러나 다른 사람들은 동의하지 않습니다. 리눅스 토발즈, 예를 들어, 분명히 거부했습니다 배수 식별자 교체하려고 리눅스 커널 패치 제안 class으로 klass는 C ++ 컴파일러로 컴파일 할 수 있도록합니다. 또한 당신을 놀라게 할 몇 가지 차이점이 있습니다. 예를 들어, 삼항 연산자는 두 언어 모두에서 동일한 방식으로 평가되지 않으며 inline키워드는 (다른 질문에서 배운 것처럼) 완전히 다른 것을 의미합니다.
Kyle Strand

3
운영 체제와 같은 플랫폼 간 시스템 프로젝트의 경우 C 컴파일러가 훨씬 일반적이기 때문에 엄격한 C를 준수하려고합니다. 임베디드 시스템에는 여전히 C ++ 컴파일러가없는 플랫폼이 있습니다. (통과 가능한 C 컴파일러 만있는 플랫폼이 있습니다!) C ++ 컴파일러는 특히 사이버 물리 시스템의 경우 긴장하게 만듭니다. 나는 그런 느낌을 가진 유일한 임베디드 소프트웨어 / C 프로그래머가 아니라고 생각합니다.
downbeat

2
@downbeat 프로덕션에 C ++을 사용하는지 여부에 상관없이 C ++로 컴파일하면 정적 분석을위한 마법의 힘을 얻을 수 있습니다. C 코드베이스에 대해 쿼리하려는 경우 특정 유형이 특정 방식으로 사용되는지에 대해 궁금한 경우 type_traits 를 사용하는 방법을 배우면 대상 도구를 작성할 수 있습니다. C의 정적 분석 도구에 대해 많은 비용을 지불하는 것은 약간의 C ++을 사용하여 수행 할 수있는 방법과 이미 알고있는 컴파일러를 사용하여 수행 할 수 있습니다.
HostileFork는 SE

1
나는 리눅스의 문제에 대해 이야기하고 있습니다. (방금 "Linux Torvalds"라는 말을 보았습니다!)
낙관적

28

나는 그것이 가능하지 않다고 생각합니다, 당신은 개별적으로 인수가 필요하지 않는 한 이중 괄호로 가짜를 만들 수 있습니다.

#define macro(ARGS) some_complicated (whatever ARGS)
// ...
macro((a,b,c))
macro((d,e))

21
가변성 매크로를 사용하는 것이 가능하지만 큰 괄호를 사용하는 것이 좋습니다.
David Rodríguez-dribeas

2
Microchip의 XC 컴파일러는 가변 매크로를 지원하지 않으므로이 이중 괄호 트릭은 최선의 방법입니다.
gbmhunter 2

10
#define DEBUG

#ifdef DEBUG
  #define PRINT print
#else
  #define PRINT(...) ((void)0) //strip out PRINT instructions from code
#endif 

void print(const char *fmt, ...) {

    va_list args;
    va_start(args, fmt);
    vsprintf(str, fmt, args);
        va_end(args);

        printf("%s\n", str);

}

int main() {
   PRINT("[%s %d, %d] Hello World", "March", 26, 2009);
   return 0;
}

컴파일러가 가변 매크로를 이해하지 못하는 경우 다음 중 하나를 사용하여 PRINT를 제거 할 수도 있습니다.

#define PRINT //

또는

#define PRINT if(0)print

첫 번째는 PRINT 명령어를 주석 처리하고 두 번째는 NULL if 조건으로 인해 PRINT 명령어를 방지합니다. 최적화가 설정되면 컴파일러는 다음과 같이 실행 된 명령어를 제거하지 않아야합니다. if (0) print ( "hello world"); 또는 ((void) 0);


8
#define PRINT // PRINT를 //로 대체하지 않습니다
bitc

8
#define PRINT if (0) print는 호출 코드가 PRINT 호출을위한 자체 else-if를 가질 수 있기 때문에 좋은 생각이 아닙니다. #define PRINT if (true); else print
bitc

3
표준 "아무것도하지 말아라"는 {} while (0)
vonbrand

if코드 구조를 고려한 "이 작업을 수행하지 마십시오" 의 올바른 버전은 다음과 같습니다 . if (0) { your_code } else매크로 확장이 종료 된 후 세미콜론 else입니다. while버전 외모 좋아 : while(0) { your_code } 에 문제 do..while버전은 코드에 있다는 것입니다 do { your_code } while (0)한 번 이루어집니다 보장. 세 경우 모두 your_code비어 있으면 적절 do nothing gracefully합니다.
Jesse Chisholm

4

g ++에 대해서는 여기에 설명되어 있지만 C99의 일부이므로 모든 사람에게 효과적입니다.

http://www.delorie.com/gnu/docs/gcc/gcc_44.html

빠른 예 :

#define debug(format, args...) fprintf (stderr, format, args)

3
GCC의 가변 매크로는 C99 가변 매크로가 아닙니다. GCC 에는 C99 가변 매크로가 있지만 C99는 C ++의 일부가 아니므로 G ++에서 지원하지 않습니다.
Chris Lutz

1
실제로 g ++는 C99 파일을 C99 매크로로 컴파일합니다. 그러나 '-pedantic'으로 컴파일 된 경우 경고가 표시됩니다.
Alex B

2
C99가 아닙니다. C99는 VA_ARGS 매크로를 사용합니다 ).
qrdl

1
C ++ 11 __VA_ARGS__은 확장 기능뿐만 아니라 이전 버전의 컴파일러 에서도 지원 하지만을 지원합니다.
Ethouris

1
printf ( "hi")에서는 작동하지 않습니다. var args가없는 곳 이 문제를 해결하는 일반적인 방법은 무엇입니까?
BTR Naidu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.