back

가변 인자 매크로의 모든 것 - 미래

2달 전 작성

이 글은 시리즈 글의 일부입니다.


앞서 소개한 gcc 의 확장은 이미 표준화 위원회에 여러번 제안되었습니다. 그리고 C11 에서도 거절된 것을 보면 앞으로도 딱히 추가될 것으로 보이지는 않습니다(위원회 화이팅!).

반면 약간 다른 형태의 제안이 C++ 표준화 위원회(ISO/IEC JTC1/SC22/WG21)를 통해 제안되었고 C 와 C++ 사이의 호환성 유지를 위해 C 표준화 위원회(ISO/IEC JTC1/SC22/WG14)에도 전달되었습니다.

새로운 제안에서는 , ## __VA_ARGS__ 같은 특정 패턴을 인식하는 방식이 아니라 __VA_OPT__ 라는 새로운 방식을 도입해 보다 유연한 구조를 만들었습니다. __VA_OPT__() 부분에 들어간 토큰 들은 가변 인자 부분에 아무 것도 제공되지 않는 경우 사라지도록 약속하고 있습니다.

예를 들어,

#define debug(msg, ...) printf("%s:%d:" msg "\n", __FILE__, __LINE__ __VA_OPT__(,) __VA_ARGS__)

이렇게 ,__VA_OPT__() 안에 넣어두면 __VA_ARGS__ 에 대응하는 인자가 하나라도 있다면 , 가 살아남고 그렇지 않으면 삭제됩니다. 꽤나 유연하지요? gcc 확장은 여분의 쉼표를 제거하는 목적으로만 사용 가능하지만, 이 방식은 보다 복잡한 형태를 갖는 다른 경우에도 쓸 수 있습니다.

#define init(var, ...) struct s var __VA_OPT__(= {) __VA_ARGS__ __VA_OPT__(})

이제

init(foo, 1, 2, 3);

이렇게 적어준 코드는 가변 인자 부분에 내용이 있으므로 __VA_OPT__() 안의 내용이 살아남아

struct s foo = { 1, 2, 3 };

로 확장되는 반면,

init(foo)

이렇게 적어주면 __VA_OPT__() 안의 내용이 사라져서

struct s foo;

이런 결과물이 됩니다. 비록 (밑줄 문자로 시작하는) 지저분한 명칭 __VA_OPT__ 를 추가하긴 하지만 개인적으로는 gcc 의 확장보다는 활용도도 높고 오해의 여지도 없다고 생각합니다. 다만 문제는 현재 이를 구현해 사용하고 있는 주요 컴파일러가 전무해 충분한 지지를 받을 수 있을지가 미지수입니다. 하지만 C++ 표준화 과정에서 논의되고 있는 부분인만큼 beluga 에 gcc 확장을 구현할 때 추후 __VA_OPT__() 를 반영하기 쉽도록 고려하였습니다.

이제 시리즈의 마지막이 될 다음 글에서 C90 전처리기였던 beluga 에 C99 지원을 위해 가변 인자 매크로와 gcc 확장을 추가했던 경험을 공유해 보고자 합니다. 표준의 세세한 내용이 어떻게 구현을 고려하여 작성되었고, 또 실제 구현될 때 어떤 문제가 있을 수 있는지 설명해 보았습니다.