속성 지정자 시퀀스(Attribute specifier sequence)(C23)

속성 지정자 시퀀스(Attribute specifier sequence)(C23)

컴파일러마다 제각각이던 확장 기능(예: GNU/IBM의 __attribute__((...)), Microsoft의 __declspec())을, C23부터는 **속성(attribute)**이라는 하나의 표준 문법으로 통일해 다룰 수 있게 됐어요. 타입·객체·표현식 등에 구현 정의 속성을 붙이는 방법이에요.

출처: cppreference

본문

문법

[[attr]] [[attr1, attr2, attr3(args)]] [[attribute-prefix::attr(args)]]

형식적으로는 다음 문법을 가져요.

[[ attribute-list ]] (C23부터)

여기서 attribute-list는 0개 이상의 attribute-token이 쉼표로 구분된 시퀀스예요.

standard-attribute (1)
attribute-prefix :: identifier (2)
standard-attribute ( argument-list(optional) ) (3)
attribute-prefix :: identifier ( argument-list(optional) ) (4)

여기서 attribute-prefix는 식별자이고, argument-list는 괄호·대괄호·중괄호가 균형을 이루는 토큰들의 시퀀스(balanced-token-sequence)예요.

  1. 표준 속성. 예: [[fallthrough]]
  2. 이름 공간(namespace)이 있는 속성. 예: [[gnu::unused]]
  3. 인자가 있는 표준 속성. 예: [[deprecated("reason")]]
  4. 이름 공간과 인자 목록을 모두 가진 속성. 예: [[gnu::nonnull(1)]]

동작 방식(Explanation)

속성은 구현 정의 언어 확장을 위한 통일된 표준 문법을 제공해요. GNU/IBM 언어 확장의 __attribute__((...)), Microsoft 확장의 __declspec() 같은 것들을 하나의 문법으로 다룰 수 있게 되죠.

속성은 C 프로그램의 거의 어디에서나 쓸 수 있고, 거의 모든 것에 적용할 수 있어요. 타입·변수·함수·이름·코드 블록·번역 단위 전체까지요. 다만 각각의 특정 속성은 구현이 허용하는 위치에서만 유효해요. [[expect_true]]if에만 쓸 수 있는 속성이 될 수 있지, 클래스 선언에는 못 쓸 수 있어요. [[omp::parallel()]]은 코드 블록이나 for 루프에는 적용되지만 int 타입에는 적용되지 않을 수 있어요. (이 두 속성은 예시일 뿐 실제로 있는 속성은 아니에요. 실제 표준·비표준 속성은 아래 "표준 속성"에서 확인하세요.)

선언에서 속성은 선언 전체 앞과 선언되는 개체 이름 바로 뒤 양쪽에 나타날 수 있어요. 이 경우 둘은 결합돼요. 대부분의 다른 상황에서는 속성이 바로 앞에 오는 개체에 적용돼요.

연속된 두 좌대괄호 토큰([[)은 속성 지정자를 도입할 때나 속성 인자 안에서만 나타날 수 있어요.

아래 나열한 표준 속성 외에도, 구현은 구현 정의 동작을 가진 임의의 비표준 속성을 지원할 수 있어요. 구현이 모르는 모든 속성은 오류 없이 무시돼요.

모든 표준 속성은 표준화를 위해 예약돼 있어요. 즉 모든 비표준 속성에는 구현이 제공하는 attribute-prefix가 붙어요. 예: [[gnu::may_alias]], [[clang::no_sanitize]].

표준 속성

C 표준이 정의하는 속성은 다음뿐이에요. attr 형태의 이름을 가진 모든 표준 속성은 __attr__로도 쓸 수 있고 의미는 변하지 않아요.

[[deprecated]](C23) [[deprecated("reason")]](C23) 이 속성으로 선언된 이름이나 개체의 사용이 허용되지만, 어떤 이유로 권장되지 않는다는 것을 나타내요.
[[fallthrough]](C23) 이전 case 레이블에서의 낙하(fall through)가 의도적임을 알려서, fall-through에 경고하는 컴파일러가 진단하지 않도록 해요.
[[nodiscard]](C23) [[nodiscard("reason")]](C23) 반환 값을 버리면 컴파일러가 경고를 내도록 권장해요.
[[maybe_unused]](C23) 사용되지 않는 개체에 대한 컴파일러 경고를 억제해요(있을 경우).
[[noreturn]](C23) [[_Noreturn]](C23)(deprecated) 함수가 반환하지 않는다는 것을 나타내요.
[[unsequenced]](C23) 함수가 무상태(stateless)·무효과(effectless)·멱등(idempotent)·독립(independent)임을 나타내요.
[[reproducible]](C23) 함수가 무효과·멱등임을 나타내요.

속성 테스트(Attribute testing)

__has_c_attribute( attribute-token )

attribute-token이 가리키는 속성 토큰의 존재 여부를 검사해요.

표준 속성의 경우, 그 속성이 작업 초안(working draft)에 추가된 연도와 월로 확장돼요(아래 표 참고). 공급업체 특정 속성의 존재 여부는 0이 아닌 정수 상수로 판정돼요.

__has_c_attribute#if#elif의 표현식에서 확장될 수 있어요. #ifdef, #ifndef에서는 정의된 매크로로 취급되지만, 그 외의 곳에서는 쓸 수 없어요.

attribute-token 속성 표준
deprecated [[deprecated]] 201904L (C23)
fallthrough [[fallthrough]] 201904L (C23)
maybe_unused [[maybe_unused]] 201904L (C23)
nodiscard [[nodiscard]] 202003L (C23)
noreturn _Noreturn [[noreturn]] [[_Noreturn]] 202202L (C23)
unsequenced [[unsequenced]] 202207L (C23)
reproducible [[reproducible]] 202207L (C23)

예제

여기서는 한 함수 선언에 여러 속성을 붙이는 두 가지 문법을 보여 줘요.

[[gnu::hot]] [[gnu::const]] [[nodiscard]]
int f(void); // declare f with three attributes

[[gnu::const, gnu::hot, nodiscard]]
int f(void); // the same as above, but uses a single attr
             // specifier that contains three attributes

int f(void) { return 0; }

int main(void)
{
}

첫 선언은 속성 지정자 세 개를 따로 쓰고, 두 번째는 한 속성 지정자 안에 세 속성을 쉼표로 묶었어요. 둘 다 같은 의미예요.

더 알아보기

  • 표준 속성 각각의 페이지: [[deprecated]], [[fallthrough]], [[nodiscard]], [[maybe_unused]], [[noreturn]], [[unsequenced]], [[reproducible]].
  • C++ 문서의 속성 지정자 시퀀스: 동일한 개념의 C++ 버전.