[C] 인자 개수에 따라 다르게 동작하는 매크로 함수
매크로 함수를 만드는데, 인자 개수에 따라 다른 동작을 하고 싶습니다. 어떻게 하면 좋을까요? 가장 간단한 방법은 인자 개수별로 매크로 함수를 따로 만드는 것입니다.
#define func_1(a) //something... #define func_2(a, b) //something... #define func_3(a, b, c) //something... #define func_4(a, b, c, d) //something...
이 방식은 인자 개수가 몇 개인지 세어서 개수에 맞는 함수를 호출해야만 한다는 문제가 있습니다. 이를 해결하려면 C++의 함수 오버로딩처럼 일종의 ‘제네릭’한 매크로 함수를 만들어야 합니다. 인자 개수에 따라 다르게 확장되는 매크로 함수를 정의하면 가능합니다.
#define VA_GENERIC(_1, _2, _3, _4, x, ...) x
이 함수는 다음과 같이 사용합니다.
#define func(...) \ VA_GENERIC(__VA_ARGS__, func_4, func_3, func_2, func_1)(__VA_ARGS__)
예를 들어 func(1, 2, 3)
은 다음과 같이 매크로 확장됩니다.
func(1, 2, 3)
→ VA_GENERIC(1, 2, 3, func_4, func_3, func_2, func_1)(1, 2, 3)
→ func_3(1, 2, 3)
보시다시피 인자 개수에 맞춰 정확한 함수를 자동으로 호출합니다. 위 예제는 인자 개수를 최대 4개까지만 지원하는데 VA_GENERIC
을 조금만 수정하면 얼마든지 늘릴 수 있습니다.
고급 예제
가변 인자 함수의 인자 개수 자동으로 계산하기
매크로 함수가 아니라 일반적인 가변 인자 함수는 인자 개수를 자동으로 구해주지 않습니다. 첫 번째 인자로 인자 개수를 직접 계산해서 넣어주거나 최소한 첫 번째 인자로부터 총 인자 개수를 구할 수 있어야 합니다. 표준 라이브러리의 printf
는 첫 번째 인자로 포맷 문자열을 받아 이를 보고 인자 개수를 계산합니다. 보통은 다음과 같이 인자 개수를 직접 넣어주죠.
int add_int(int argc, ...) { va_list ap; int sum = 0; va_start(ap, argc); for (int i = 0; i < argc; i++) { int n = va_arg(ap, int); sum += n; } va_end(ap); }
위의 VA_GENERIC
매크로를 사용하면 첫 번째 인자 argc
를 자동으로 구해줄 수 있습니다.
#define NUM_VA_ARGS(...) VA_GENERIC(__VA_ARGS__, 4, 3, 2, 1) #define ADD_INT(...) add_int(NUM_VA_ARGS(__VA_ARGS__), __VA_ARGS__)
모두 참인지 검사하기
C에서 스칼라(정수, 부동소수점, 포인터) 값은 참/거짓으로 변환하여 논리 연산자의 피연산자로 쓸 수 있습니다. 여러 스칼라 값을 받아서 모두 다 참인지 계산할 수 있을까요? 각 값의 타입이 다를 수 있기 때문에 일반적인 가변 인자 함수를 쓰려면 각 값의 타입을 첫 번재 인자로 넣어 주는 등의 번거로운 작업이 필요하지만, VA_GENERIC
을 쓰면 아름답게 해결됩니다.
#define AT_1(a) (a) #define AT_2(a, ...) ((a) && AT_1(__VA_ARGS__)) #define AT_3(a, ...) ((a) && AT_2(__VA_ARGS__)) #define AT_4(a, ...) ((a) && AT_3(__VA_ARGS__)) #define ALL_TRUE(...) \ VA_GENERIC(__VA_ARGS__, AT_4, AT_3, AT_2, AT_1)(__VA_ARGS__) ALL_TRUE(-1, 2, 3) // true ALL_TRUE(0, -5.7) // false ALL_TRUE(123ull, (void *)0) // false