제네릭 선택

제네릭 선택 (Generic selection, C11부터)

제어 표현식(controlling expression)의 타입에 따라, 컴파일 시간에 여러 표현식 중 하나를 고르는 방법을 제공해요.

출처: cppreference

본문

문법 (Syntax)

(C11부터) _Generic ( controlling-expression , association-list )

여기서 association-list는 쉼표로 구분된 연관(association)들의 목록이고, 각 연관은 다음 문법을 가져요.

  • type-name : expression
  • default : expression

여기서

용어 설명
type-name 가변 수정되지 않은 완전한 객체 타입이에요 (즉, VLA나 VLA에 대한 포인터가 아닌 타입).
controlling-expression ( 쉼표 연산자가 아닌) 어떤 표현식이든 가능해요. default 연관을 쓰지 않으면 그 타입은 type-name 중 하나와 호환되어야 해요.
expression ( 쉼표 연산자가 아닌) 어떤 타입·값 범주의 표현식이든 가능해요.

association-list의 두 type-name이 호환 타입을 지정할 수는 없어요. default 키워드를 쓰는 연관은 하나만 있을 수 있어요. default를 쓰지 않는데 type-name 중 어느 것도 제어 표현식의 타입과 호환되지 않으면, 그 프로그램은 컴파일되지 않아요.

설명 (Explanation)

먼저, 제어 표현식의 타입은 lvalue 변환을 거쳐요. 이 변환은 타입 영역에서만 일어나요. 제어 표현식 타입의 최상위 cvr-한정자와 원자성을 버리고 배열→포인터/함수→포인터 변환을 적용해요. 부수 효과를 시작하거나 값을 계산하지는 않아요.

변환 후의 타입을 association-list의 type-name들과 비교해요.

타입이 어떤 연관의 type-name과 호환되면, 제네릭 선택의 타입·값·값 범주는 그 type-name에 대해 콜론 뒤에 나타나는 표현식의 그것들과 같아요.

type-name 중 어느 것도 제어 표현식의 타입과 호환되지 않고 default 연관이 제공되면, 제네릭 선택의 타입·값·값 범주는 default : 라벨 뒤의 표현식의 그것들과 같아요.

참고 (Notes)

제어 표현식과 선택되지 않은 선택들의 표현식은 결코 평가되지 않아요.

lvalue 변환 때문에, "abc"char[4]가 아니라 char*와 매칭돼요. 또 (int const){0}const int가 아니라 int와 매칭돼요.

함수 지정자와 void 표현식을 포함한 모든 값 범주가 제네릭 선택의 표현식으로 허용돼요. 선택되면 제네릭 선택 자체도 같은 값 범주를 가져요.

C99에서 도입된 <tgmath.h>타입 제네릭 수학 매크로는 컴파일러 특정 방식으로 구현됐어요. C11에서 도입된 제네릭 선택은 프로그래머가 비슷한 타입 의존 코드를 쓸 수 있게 해 줬어요.

제네릭 선택은 C++의 오버로딩과 비슷해요(컴파일 시간에 인자 타입에 따라 여러 함수 중 하나를 고르는 것). 차이는 제네릭 선택이 임의의 표현식 사이에서 고른다는 점이에요.

예시

#include <math.h>
#include <stdio.h>

// Possible implementation of the tgmath.h macro cbrt
#define cbrt(X) _Generic((X),     \
              long double: cbrtl, \
                  default: cbrt,  \
                    float: cbrtf  \
              )(X)

int main(void)
{
    double x = 8.0;
    const float y = 3.375;
    printf("cbrt(8.0) = %f\n", cbrt(x));    // selects the default cbrt
    printf("cbrtf(3.375) = %f\n", cbrt(y)); // converts const float to float,
                                            // then selects cbrtf
}

출력:

cbrt(8.0) = 2.000000
cbrtf(3.375) = 1.500000

결함 보고 (Defect reports)

DR 적용 대상 발표된 동작 올바른 동작
DR 481 C11 제어 표현식이 lvalue 변환을 거치는지가 명세가 불충분했다 거친다

더 알아보기

  • <tgmath.h>의 타입 제네릭 수학 매크로 문서를 보면 제네릭 선택이 실제로 어디서 쓰이는지 알 수 있어요.
  • C++의 함수 오버로딩과 비교하면 제네릭 선택의 위치가 더 분명해져요.
  • cppreference의 제네릭 선택 원문에서 최신 표준 변경을 확인할 수 있어요.