Google C++ Style Guide
5. Other C++ Features
A. Reference Arguments (참조자 인자)
참조자로 넘겨지는 모든 파라미터는 const 를 붙인다.
정의 :
C에서는 함수의 파라미터를 수정할 필요가 있을 때, 포인터를 사용한다, 예, int foo(int *pval). C++에서는 그 대안으로 파라미터를 참조자로 선언할 수 있다. int foo(int &val).
장점 :
참조자로 파라미터를 정의하면, (*pval)++과 같은 지저분한 코드를 피할 수 있다. 복사 생성자와 같은 함수들에게 필요하다. 참조자는 포인터와 달리 NULL값이 불가능하다.
단점 :
참조자는 포인터 의미가 아닌 변수 구문을 갖기에 혼동될 수 있다.
결론 :
함수 파라미터에서 모든 참조자는 const 이어야 한다.
void Foo(const string &in, string *out); |
input 파라미터가 변수이거나 const 참조자이고, output 파라미터를 포인터로 하는 것은 구글 코드에서 매우 강한 관례(convention)이다. Input 파라미터는 const 포인터일수도 있지만, 절대 non-const 참조자는 허용하지 않는다.
input 파라미터가 const인 경우에, 복사되지 않는다는 것을 강조하려면, 그 객체의 lifetime동안 존재해야 하고, 이를 주석으로 달아놓는다.
B. Function Overloading (함수 오버로딩)
어떤 오버로드 함수가 호출되는지 몰라도, 어떻게 동작하는지 이해할 수 있을 때에만 오버로드된 함수(생성자 포함)를 사용한다. ???
정의 :
const string& 을 갖는 함수와 다른 파라미터 const char*를 갖도록 오버로딩 한다.
class MyClass { public: void Analyze(const string &text); void Analyze(const char *text, size_t textlen); }; |
장점 :
오버로딩은 동일한 이름의 함수가 다른 파라미터를 갖도록 하여 코드를 직관적으로 만든다.
단점 :
함수가 단지 파라미터 타입에 의해서만 오버로드되면, C++의 복잡한 매칭 룰(matching rule)을 이해해야 한다. 또한 파생 클래스가 여러가지 함수를 오버라이딩한다면, 상속의 의미와 혼란을 일으킬 것이다.
결론 :
함수를 오버로딩하고 싶으면, 파라미터에 대한 정보를 가지고 네이밍할 수 있는지 검토한다. Append()보다는 AppendString(), AppendInt()가 낫다.
아래 설명하는 특별한 상황을 제외하고, 디폴트 함수 파라미터를 허용하지 않는다.
장점 :
함수는 종종 많은 디폴트 값을 사용하지만, 때때로 그 디폴트 값을 오버라이딩하길 원한다. 디폴트 파라미터는 예외에 대해서 많은 함수를 정의하지 않고 사용할 수 있게 해준다.
단점 :
때때로 API를 어떻게 사용하는지를 보고, 그 API를 이해한다. 이전 코드를 copy-and-paste하면 모든 파라미터가 드러나지 않기 때문에 디폴트 파라미터를 유지보수하기 어렵다. Copy-and-paste는 디폴트 파라미터가 새로운 코드에 적합하지 않을 때 문제가 발생한다.
결론 :
아래 제시된 코드를 제외하고 모든 파라미터는 명시적으로 기술되야 한다.
하나의 특별한 예외는 디폴트 파라미터가 변수 길이 파라미터 리스트를 시뮬레이션할 때이다.???
// Support up to 4 params by using a default empty AlphaNum. string StrCat(const AlphaNum &a, const AlphaNum &b = gEmptyAlphaNum, const AlphaNum &c = gEmptyAlphaNum, const AlphaNum &d = gEmptyAlphaNum); |
D. Variable-Length Array and alloca() (가변 배열과 alloca() )
가변 배열이나 alloca()를 사용하지 않는다.
장점 :
가변 배열은 보기에 자연스러워 보인다. 가변 배열과 alloca()는 매우 효율적이다.
단점 :
가변 배열과 alloca는 표준 C++이 아니다. 더 중요한 건, 데이터에 따라 스택의 공간을 할당하는 양이 다르다. 따라서 메모리 침범 버그를 발견하기 어렵다. “많은 머신에서 잘 돌아가지만, 불가사의하게 프로그램이 죽을 것이다.”
결론 :
vector나 string으로 대체할 수 없다면(EC++), 대신 scoped_ptr / scoped_array와 같은 안전한 할당자를 사용한다.
특별한 이유가 있을 경우, friend 클래스와 함수를 사용한다.
friend는 항상 클래스의 private 멤버의 사용을 찾기 쉽도록 하기 위해 같은 파일에 정의한다. Foo 클래스의 friend인FooBuilder를 선언해서 외부에 상태를 노출시키지 않고 Foo의 내부 상태를 조작할 수 있다. Unittest 클래스를 테스트 하려는 클래스의 friend로 만들면 유용하다.
단지 하나의 다른 클래스가 그 클래스의 멤버에 접근할 때, public 멤버를 만드는 것보다 friend 를 사용한다.
C++ 예외를 사용하지 않는다.
장점 :
- 예외는 어플리케이션의 상위 수준에서 ‘발생할 수 없는’ 에러를 처리할 수 있도록 한다.
- 예외는 다른 현대 언어에서 많이 사용된다. C++에서의 예외 사용은 Python, Java, C++과 일관성을 갖도록 한다.
- 일부 third-party C++ 라이브러리는 예외를 사용하고, 내부적으로 그것을 off시켜 다른 라이브러리와의 통합을 어렵게 한다.
- 예외는 생성자가 실패하는 유일한 방법이다. 팩토리 함수나 Init()메소드로 실험할 수 있지만 상대적으로 heap allocation이나 새로운 유효하지 않은 상태가 요구된다.
- 예외는 테스팅 프레임워크에서 정말로 편리하다.(handy)
단점 :
- 기존 함수에서 throw 구문을 추가할 때, 그 함수 상위로의 모든 호출 함수를 살펴봐야 한다. 예외 발생시 함수들이 제대로 clean up 되지 않을 수가 있다.
- 프로그램의 제어 흐름을 파악하기 힘들다. 함수가 기대하지 않는 곳에서 리턴할 것이다. 이것은 유지보수와 디버깅을 힘들게 한다.
- 예외 안전은 RAII(Resource Acquisition is Initialization)와 다른 코딩 연습을 요구한다. 예외를 허용하는 것은 그것이 가치가 없을지라도 비용이 발생한다.
- 컴파일 타임이 약간 증가하며 주소 공간 압박도 증가한다.
- 예외를 사용하면 개발자가 코드가 적합하지 않고 안전하지 못할 때 예외를 던지도록 부추긴다. 예를 들어 사용자 입력 에러를 예외로 던지면 안된다.
결론 :
예외의 장점에는 많은 비용이 따른다. 특히 새로운 프로젝트일 때. 그러나 기존 코드에 대해 예외를 사용하면 모든 의존된 코드에 영향을 준다. 예외가 새로운 프로젝트에 적용되었을 때, 그 새로운 프로젝트를 기존의 예외 없는 코드에 통합할 때 문제가 발생한다.
예외에 대한 대안으로 에러 코드를 사용하거나 assertion 을 사용하는 것이 낫다.
G. Run-Time Type Information(RTTI) (런타임 타입 정보)
런타임 타입 정보(RTTI)를 사용하지 않는다.
정의 :
RTTI는 프로그래머가 런타임에 C++ 클래스의 타입을 알 수 있게 한다.
장점 :
일부 유닛테스트에 유용하다. 예를 들어 새로 생성된 클래스가 동적 타입을 갖는지 검증하기 위한 팩토리 클래스를 테스트하는데 유용하다.
드문 경우에 테스트 외부에서도 유용하다.
단점 :
런타임에 타입을 알아야 한다는 건 디자인에 문제가 있다는 것을 의미한다.
결론 :
유닛 테스트를 제외하고 RTTI를 사용하지 않는다.
대안으로 가상 함수는 특정 서브 클래스의 타입에 따라 다른 코드의 경로를 실행한다. 작업이 객체 외부에 있다면 Visitor 디자인 패턴 같은 double-dispatch 솔루션을 고려한다.
이러한 아이디어들을 사용할 수 없다면, RTTI를 사용한다. 그러나 두 번 생각해라.
static_cast<>()와 같은 C++ 캐스팅을 사용한다. int y = (int) x; 혹은 int y = int(x); 와 같은 캐스팅은 사용하지 않는다.
정의 :
C++은 캐스팅 연산 타입을 구분하기 위해 C와는 다른 캐스팅 시스템이 있다.
장점 :
C 캐스팅의 문제는 연산이 모호하다. 때로는 conversion(예, (int)3.5 ), 때로는 cast(예, (int)”hello” )
C++캐스팅은 이 문제를 피한다. 게다가 캐스팅을 검색할 때 눈에 잘 띈다.
단점 :
코드가 지저분하다.
결론 :
C-style 캐스팅은 사용하지 말고, C++-style 캐스팅을 사용한다.
- 값을 변환(conversion)할 때, static_cast 를 사용한다.
- const를 제거하기 위해 const_cast를 사용한다.
- Int를 pointer 타입으로의 불안전한 변환을 할 때 reinterpret_cast를 사용한다. 사용 목적과, aliasing issue를 이해할 때에만 사용한다.
- 테스트 코드를 제외하고 dynamic_cast를 사용하지 않는다. 유닛 테스트 외부에서 런타임에 타입 정보를 알 필요가 있다면 디자인 결함이다.
로그를 위해서만 스트림(<<)을 사용한다.
정의 :
스트림은 printf()와 scanf()의 대체물이다.
장점 :
스트림을 사용하면 출력하려는 객체의 타입을 알 필요가 없다. 인자 리스트에 매칭되는 포맷 스트링에 대한 문제도 없다. 스트림은 관련 파일을 열고 닫는 자동 생성자와 파괴자가 있다. ??
단점 :
스트림은 pread()와 같은 기능을 하기 어렵다. 스트림은 (%1s 명령어)와 같은 연산자 reordering (국제화에 유용)을 지원하지 않는다.
결론 :
로깅 인터페이스를 제외하곤 스트림을 사용하지 않는다. 대신 printf 같은 루틴을 사용한다.
스트림을 사용하는 다양한 장점과 단점에 대한 토론이 있지만, 구글에서는 스트림을 사용하지 않는다.
J. Preincrement and Predecrement (전위증감 연산자)
iterator와 다른 템플릿 객체에 대해 ++i를 사용한다.
정의 :
변수가 증가(++i or i++)하거나 감소(--i or i--)할 때, 전위 연산자 또는 후위 연산자를 사용할지 결정해야 한다.
장점 :
리턴 값이 무시될 때, 전위 연산자(++i)는 (i++)보다 절대 덜 효율적이지 않으며, 종종 더 효율적이기 까지 하다. 후위 연산자는 i의 복사본을 요구하기 때문이다. 만약 i가 iterator이거나 다른 non-scalar 타입이라면 i의 복사 비용은 비싸다.
전위/후외 연산자가 같은 동작을 한다면, 왜 항상 전위 연산자를 사용하지 않을까?
단점 :
전통적으로 C에서는 후위 연산자를 사용했다. 특히 for 문에서. 영어처럼 주어(i)가 동사(++)의 앞에 두듯이 전위 연산자가 더 읽기 쉽다는 것을 발견했다.
결론 :
간단한 scalar(non-object)값에 대해서는 어떤 형태를 사용하던지 상관없지만, iterator와 다른 템플릿 타입에 대해서는 전위 연산자를 사용한다.
가능하면 const 사용을 강력히 추천한다.
정의 :
const 키워드를 사용해서 변수와 파라미터를 선언하는 것은 그 값이 변하지 않는다는 것을 의미한다 (예, const int foo). 클래스의 함수의 해당 클래스의 멤버 변수의 상태가 변경하지 않는다는 것을 나타내기 위해 const를 사용할 수도 있다 (예, class Foo { int Bar(char c) const; }; )
장점 :
변수가 어떻게 사용되는지 이해하기 쉽다. 컴파일러는 타입 체크가 쉬워지며 더 나은 코드를 만든다. 해당 변수가 수정되지 않는 다는 것을 한 눈에 알 수 있으며, 멀티 스레드 프로그램에서 lock을 사용하지 않고도 thread-safe하다
단점 :
const 는 바이러스성이다. 함수에 const 변수를 넘겨주려면, 함수 원형에 const로 선언해야 한다. (혹은 const_cast를 사용해야 한다). 이것은 특히 라이브러리 함수를 호출할 때 문제가 될 수 있다.
결론 :
const 변수, 데이터 멤버, 메소드, 파라미터는 컴파일 타임에 타입 체크를 하기 때문에 에러를 일찍 잡아낼 수 있다. 그래서 다음과 같은 상황일 때, const 사용을 강력히 추천한다.
- 함수가 참조자나 포인터에 의해 넘겨받은 파라미터를 수정하지 않으면, 그 파라미터는 const
- 가능할 때 언제라도 메소드는 const로 선언. 접근자는 거의 항상 const. 다른 메소드도 데이터 멤버를 수정하지 않으면 const.
- 데이터 멤버 변수가 생성된 이후에 수정될 필요가 없을 때는 const로 만드는 것을 고려한다.
그러나, const int * const * const x; 와 같이 const를 남발해선 안된다. 이 코드가 문법적으로 맞을지라도 const int** x; 처럼 선언하면 충분하다.
mutable 키워드를 사용해도 되지만, thread-safe하지 않기 때문에 신중히 사용한다.
const 위치 :
const int* foo보다 int const *foo를 선호하는 사람이 있다. 일관성을 이유로 더 가독성이 있다고 주장한다. const는 그 다음에 나오는 객체를 묘사한다는 것이다. 그러나 명사(int)앞에 형용사(const)가 오는 영어 문법에 맞기 때문에, const를 먼저 쓰는 것(전자)이 더 가독성이 좋다.
const int* foo와 같이 먼저 쓰는 것을 권하지만, 코드에서 일관성 있게 사용하면 된다.
C++ 빌트인 정수형 타입은 int형만 사용한다. 다른 사이즈가 필요하면 int16_t 처럼 <stdint.h>내의 정확한 사이즈의 정수형 타입을 사용한다.
정의 :
C++은 정수형 타입의 사이즈를 명시하지 않는다. 전형적으로 short를 16bit, int를 32bit, long은 32bit, long long은 64bit라고 생각한다.
장점 :
선언의 일관성
단점 :
C++에서 정수형 타입의 사이즈는 컴파일러와 아키텍처에 따라 다르다.
결론 :
<stdint.h>는 int16_t, uint32_t, int64_t 등과 같은 타입을 정의하며, 정수 사이즈가 필요할 때는 short, unsigned long long과 같은 것보다는 <stdint.h>에 정의된 타입을 사용 한다. C의 정수형 타입은 int 만을 사용한다. 사이즈를 계산할 때는 size_t와 ptrdiff_t와 같은 표준을 사용해도 좋다.
루프문처럼 크지 않은 정수형을 위해 int를 사용한다. 64-bit 정수형이 필요하면 int64_t 혹은 uint64_t를 사용한다.
표현하고자 하는 양이 숫자라기보다 bit 패턴이고, 2의 보수 오버플로우를 정의할 필요가 있는 경우를 제외하고 uint32_t와 같은 unsigned 정수 타입을 사용하면 안된다. 특히 숫자가 절대 음수값을 가질 수 없다는 것을 표현하기 위해 unsigned 타입을 사용해선 안된다. 대신 assertion을 사용한다.
On Unsigned Integers
책 저자를 비롯한 일부 사람들은 절대 음수가 아닌 숫자를 표현하기 위해 unsigned 타입을 사용할 것을 추천한다. 그러나 아래와 같은 버그를 만들어 낼 수 있다.
for (unsigned int i = foo.Length()-1; i >= 0; --i) ... |
위 코드는 절대 종료되지 않는다. 때로 gcc는 이 버그를 발견하고 경고할지도 모르지만, 항상 그렇지는 않다. Signed와 unsigned 변수를 비교할 때도 버그가 발생할 수 있다.
따라서, assertion을 사용해서 변수가 non-negative라고 문서화 하고, unsigned 타입은 사용하지 않는다.
M. 64-bit Portability (64비트 호환성)
64bit와 32bit 호환되게 코드를 작성한다. print, 출력, 구조체 정렬 등의 문제를 명심한다.
- 일부 타입에 대해 printf()는 32-bit와 64-bit 시스템에서 호환성이 없다. C99은 호환 가능한 포맷 지정자(specifier)를 정의하지만, MSVC 7.1은 이 지정자 일부는 이해하지 못하며, 표준도 일부 빠져있다. 그래서 어떤 경우에는 추한 버전을 정의해야 한다. (표준 include 파일인 inttypes.h의 스타일에서)
// printf macros for size_t, in the style of inttypes.h #ifdef _LP64 #define __PRIS_PREFIX "z" #else #define __PRIS_PREFIX #endif
// printf format string에서 % 다음에 이 macros 사용 // 32/64 bit 에서 모두 동작하려면, 아래와 같이 사용 // size_t size = records.size(); // printf("%"PRIuS"\n", size);
#define PRIdS __PRIS_PREFIX "d" #define PRIxS __PRIS_PREFIX "x" #define PRIuS __PRIS_PREFIX "u" #define PRIXS __PRIS_PREFIX "X" #define PRIoS __PRIS_PREFIX "o" |
Type | DO NOT use | DO use | Notes |
void * (or any pointer) | %lx | %p | |
int64_t | %qd, %lld | %"PRId64" | |
uint64_t | %qu, %llu, %llx | %"PRIu64", %"PRIx64" | |
size_t | %u | %"PRIuS", %"PRIxS" | C99 specifies %zu |
ptrdiff_t | %d | %"PRIdS" | C99 specifies %zd |
PRI* 매크로는 컴파일러에 의해 연결되는 독립적인 문자열을 확장한다. 그러므로 비상수 포맷 문자열을 사용한다면,이름보다는 포맷에 매크로의 값을 삽입한다. PRI* 매크로를 사용할 때 % 뒤에 문자열 길이 지정자를 포함시킬 수도 있다. 예를 들어, printf(“x = %30”PRIuS”\n”, x) 는 32-bit 리눅스에 확장될 때 printf(x = %30” “u” “\n”, x)로 될 것이고 컴파일러는 printf(“x = %30u\n”, x)로 처리할 것이다.
- sizeof(void *) != sizeof(int) 이 둘은 같지 않다. 포인터 사이즈는 intptr_t를 사용한다.
- 구조체 정렬(alignment)에 주의할 필요가 있다. (특히 구조체가 디스크에 저장되는 방식). 64-bit 시스템에서 int64_t/uint64_t를 가지는 클래스/구조체는 디폴트로 8-byte로 정렬되게 끝난다. 디스크에 구조체를 저장해야 할 때는 구조체 정렬을 바꿔야 한다. 예를 들어, gcc에서는 __attribute__((packed)). MSVC에서는 #pragma pack(), __declspec(align())등을 사용한다.
-64-bit 상수를 만들려면 LL이나 ULL 접미사를 사용한다.
int64_t my_value = 0x123456789LL; uint64_t my_mask = 3ULL << 48; |
- 32-bit, 64-bit 시스템에 다른 코드가 필요하면 #ifdef _LP64를 사용한다. (그러나 가능하면 피한다)
N. Preprocessor Macros (전처리 매크로)
매크로는 조심한다. 매크로보다는 inline 함수, enum, const 변수를 선호한다.
매크로는 당신이 보는 코드와 컴파일러가 보는 코드가 다르다는 것을 의미한다. 이것은 예기치 못한 동작으로 이어질 수 있으며, 특히 매크로가 전역 범위라면 더 그렇다.
다행히 C와는 달리 C++에서는 거의 필요하지 않다. 성능에 중요한 코드를 인라인 하기 위해 매크로를 사용하는 대신, inline 함수를 사용한다. 상수를 저장하기 위해 매크로를 사용하는 대신, const 변수를 사용한다. 긴 변수 이름을 단축하기 위해 매크로를 사용하는 대신, 참조를 사용한다. 조건적 컴파일 코드를 위해 매크로를 사용하는 대신, 조건적 컴파일을 사용하지 않는다(물론 #define 보호는 제외). 이런 매크로가 테스트를 더 어렵게 만든다.
매크로는 다른 기술들이 할 수 없는걸 할 수 있으며, 자신의 코드에서 보이기도 한다. 특히 저수준 라이브러리에서. 문자열을 만들거나 합치거나 하는 등의 특별한 기능은 언어상에서 제공하지 않는다. 매크로를 사용하기 전에, 매크로를 사용하지 않고 구현할 수 있는지 고려한다.
다음은 매크로 사용시 피해야 할 패턴이다.
- .h 파일에서 매크로를 정의하지 않는다
- 매크로를 사용하기 바로 전에 #define하고 사용한 바로 다음에 #undef 한다.
- 자신의 매크로 사용을 위해 기존의 매크로를 #undef하지 않는다. 유일한 이름을 쓴다.
- Unbalanced C++ construct를 확장하기 위해 매크로를 사용하지 않는다.
- 함수/클래스/변수 이름을 만들기 위해 ##를 사용하지 않는다. ??
정수형에는 0을, 실수형에는 0.0을, 포인터에는 NULL을, char에는 ‘\0’을 사용한다.
정수형에는 0을, 실수형에는 0.0을 사용한다. 논쟁의 여지가 없다.
포인터(주소 값)에 대해서는 0과 NULL에 대한 선택이 있다. Bjarne Stroustrup는 꾸미지 않은 0을 선호한다. 구글은 포인터처럼 보이기 때문에 NULL을 선호한다. 사실 gcc 4.1.0과 같은 일부 C++ 컴파일러는 ‘sizeof(NULL)은 sizeof(0)와 같지 않다’와 같은 유용한 경고를 위해 NULL에 대한 특별한 정의를 제공한다.
char에 대해서는 ‘\0’을 사용한다. 올바른 타입이며 가독성도 좋다.
가능하면 size(타입) 대신에 sizeof(변수명)를 사용한다.
변수 타입이 변할 수 있기 때문에 sizeof(변수명)을 사용한다.
Struct data; memset(&data, 0, sizeof(data)); |
memset(&data, 0, sizeof(Struct)); |
부스트(boost) 라이브러리는 승인된 것만 사용한다.
정의 :
Boost library collection은 인기 있는 peer-reviewed, free, open-source C++ 라이브러리 집합이다.
장점 :
부스트 코드는 품질이 높고, 이식성이 좋으며, type trait, 보다 나은 binder, 보다 나은 스마트 포인터와 같은 C++ 표준 라이브러리가 제공하지 못하는 중요한 부분을 제공한다.
단점 :
메타프로그래밍, 향상된 템플릿 기술 등과 같은 일부 부스트 라이브러리는 가독성을 해친다.
결론 :
코드 가독성과 유지보수를 위해 아래의 라이브러리만 허용한다.
- Call Traits : boost/call_traits.hpp
- Compressed Pair : boost/compressed_pair.hpp
- Pointer Container : boost/ptr_container
(C++03 표준(ptr_circular_buffer.hpp 와 ptr_unordered*)에 없는 serialization과 wrapper 제외)
- Array : boost/array.hpp
- The Boost Graph Library (BGL) : boost/graph
(serialization(adj_list_serialize.hpp)와 parallel / distributed 알고리즘과 데이터 구조(boost/graph/parallel/* 와 boost/graph/distributed/*). 제외)
- Property Map : boost/property_map
(parallel / distributed property maps (boost/property_map/parallel/* 제외).
- Iterator 일부 : boost/iterator/iterator_adaptor.hpp, boost/iterator/iterator_facade.hpp, andboost/function_output_iterator.hpp
추후 다른 부스트 기능들도 추가할 예정이다.
C++0x의 승인된 라이브러리와 확장 기능만을 사용한다. 현재 승인된 것이 없다.
정의 :
C++0x는 차세대 ISO C++ 표준으로 비공식 명칭이다. 새로운 표준은 핵심 언어에 몇 가지 사항을 추가하고, TR1 라이브러리의 대부분(일부 특별한 수학 함수 제외)을 반영하여 C++ 표준 라이브러리를 확장할 것이다. (Wikipedia)
장점 :
C++0x는 차세대 표준이며, 결국 대부분의 C++ 컴파일러에 의해 지원될 것이라고 기대한다.
단점 :
C++0x는 이전보다 상당히 복잡하며(1,300page vs 800page), 많은 개발자에게 친숙하지 않다. 다양한 기능들이 gcc, icc, clang, Eclipse등과 같은 툴에 의해 동일하게 구현이될지 예측할 수 없다.
Boost처럼 C++0x 확장은 가독성을 해친다. 다른 확장은 기존의 메커니즘으로 이용 가능한 기능들을 복제했다. 이는 혼동을 주고 변환에 대한 비용을 지불하게 한다.
결론 :
승인된 C++0x 라이브러리와 언어 기능만을 사용한다. 현재 승인된 기능은 없으며, 추후 개별적으로 승인될 것이다.