0. 언리얼 코딩 표준 공식 문서
1. 코딩 표준이란?
- 표준 코드 작성 스타일로 이름 규칙, 작성 방법 등 프로그래밍 시 지켜야 하는 요소들에 대한 가이드라인
- 코딩 스타일, 코딩 컨벤션이라고도 함
- 기존에 확립된 표준 및 모범 사례를 준수하여 유지보수 가능한 코드를 작성하도록 하는 것이 사용 목적
- 절대적으로 좋은 코딩 표준은 없으나 프로젝트에 지정된 코딩 표준은 반드시 따라야 함
- 언리얼은 자체 코딩 표준이 있으며 이는 권고가 아닌 반드시 지켜야 하는 것으로 몇 가지 사항은 지키지 않으면 프로젝트 작동에 영향을 미칠 수도 있음 (ex. new 키워드가 금지되었는 데 사용하는 경우 등)
1-1 코딩 규칙이 필요한 이유
- 소프트웨어 총 수명 비용 중 80%가 유지보수에 소모됨. 즉, 유지보수는 매우 중요함
- 그러나 소프트웨어 수명이 다할 때까지 유지보수 담당자는 계속 변하게 됨
- 따라서 코딩 규칙을 사용해 가독성을 높이면 유지보수를 위해 코드를 파악할 때 빠르고 완벽히 이해 가능
- 또한 공통된 코딩 규칙을 알고 있으면 팀작업 시 이해가 쉬울 뿐만 아니라 효율성이 높아짐
- 마지막으로 크로스 컴파일러 호환성을 위해서도 코딩 규칙이 필요함.
2. 클래스 체계
public 부분을 먼저 작성하고 private 부분을 작성한다.
- 클래스는 작성자보다 읽는 사람을 염두하고 조직해야 함
- 읽는 사람의 대부분이 클래스의 public 인터페이스를 사용함
- 따라서 먼저 public 부분을 구현하고 그다음에 private 부분을 구현해야 함
3. 명명 규칙
- 일반적인 명명 규칙에는 다음의 3가지 방법이 있음
1. 파스칼 케이싱 (모든 합성어의 첫 글자를 대문자 처리) ex. UnrealEngine, UMyGameInstance
2. 카멜 케이싱 (첫 합성어는 소문자, 나머진 대문자) ex. unrealEngine
3. 스네이크 케이싱 (합성어 사이에 언더바 사용) ex. unreal_engine
- 언리얼에서는 파스칼 케이싱만을 사용함 (타입, 함수, 변수 선언 시 합성어 첫 글자는 대문자만 허용)
ex. Health 및 UPrimitiveComponent는 올바르지만 (파스칼 케이싱 사용)
lastMouseCoordinates(카멜 케이싱) 또는 delta_coordinates(스네이크 케이싱)은 올바르지 않음
3-1 언리얼 네이밍 컨벤션 세부 규칙
- 타입 이름에는 대문자 접두사를 포함해 변수 이름과 구분
ex. FSkin > 타입 이름 / Skin > FSkin 타입의 인스턴스
- 템플릿 클래스에는 접두사 T를 포함할 것
ex. TAttribute
- UObject를 상속하는 클래스는 접두사 U를 포함
ex. UActorComponent
- AActor를 상속하는 클래스는 접두사 A를 포함
ex. AActor
- SWidget을 상속하는 클래스는 접두사 S를 포함
ex. SCompoundWidget
- 추상적 인터페이스 클래스는 접두사 I를 포함
ex. IAnalyticsProvider
- 에픽의 콘셉트가 유사한 클래스 타입(TModels 타입 특성에 첫 번째 아규먼트로 사용)에는 접두사 C 포함
ex. template <typename Concept, typename... Ts>
- 열거형은 접두사 E 포함
ex.
enum class EColorBits
{
ECB_Red,
ECB_Green,
ECB_Blue
};
- bool 변수는 접두사 b를 포함 (bool 변수의 경우에만 예외적으로 소문자를 사용)
ex. bHasFadedIn
- 그 외 언리얼 상속이 아닌 일반 C++ 클래스나 구조체등의 대부분 클래스는 접두사 F 포함
- Typedef는 해당 타입에 적합한 접두사를 사용함 (그 외 템플릿 관련 내용은 공식 문서 참고)
ex. 구조체의 typedef는 F, UObject의 typedef인 경우 U 사용
- 매크로 이름은 모두 대문자로 구성하되 단어와 단어 사이는 언더스코어(_)로 분리하며 접두사 UE_사용
ex. #define UE_AUDIT_SPRITER_IMPORT
- 각 변수에 대한 코멘트 제공을 위해 모든 변수는 한 줄에 하나씩, 자체적인 줄에서 선언
- bool을 반환하는 모든 함수는 true/false 식의 질문을 해야 함
ex. IsVisible(), ShouldClearBuffer()
- 함수 파라미터가 레퍼런스로 전달/함수를 그 값에 쓸 경우 함수 파라미터 이름에 접두사 Out 사용
- In, Out등 bool 값의 파라미터를 쓰는 경우 bOutResult와 같이 In/Out 접두사 앞에 'b' 붙임 (소문자 주의)
- 값을 반환하는 함수는 함수명 만으로도 명확하게 반환 값을 설명해야 함
ex. bool CheckTea(FTea Tea); < 이 함수의 반환값이 대체 무슨 의미인지 정확히 알 수 없음 (X)
bool IsTeaFresh(FTea Tea); < 함수명만 보고도 이것이 true인 경우 차가 신선한 상태임을 알 수 있음(O)
포터블 C++ 코드
- C++에서 사용하던 일반적인 int와 같은 자료형을 더욱 세분화/명시화 하여 사용함
- 주요 타입
bool - 부울 값(부울 크기 추정 금지). BOOL 은 컴파일되지 않습니다.
TCHAR - character(문자) (TCHAR 크기 추정 금지)
uint8 - unsigned byte(부호 없는 바이트) (1바이트)
int8 - signed byte(부호 있는 바이트) (1바이트)
uint16 - unsigned shorts(부호 없는 short) (2바이트)
int16 - signed short(부호 있는 short) (2바이트)
uint32 - unsigned int(부호 없는 int) (4바이트)
int32 - signed int(부호 있는 int) (4바이트)
uint64 - unsigned quad word(부호 없는 쿼드 단어) (8바이트)
int64 - signed quad word(부호 있는 쿼드 단어) (8바이트)
float - 단정밀도 부동 소수점(4바이트)
double - 배정밀도 부동 소수점(8바이트)
PTRINT - 포인터를 가질 수 있는 정수(PTRINT 크기 추정 금지)
표준 라이브러리 사용 지양 (STL 사용 X)
- 언리얼 C++에서는 STL 사용을 지양하는 대신 자체 라이브러리를 사용함
- 굳이 STL을 사용해야 한다면 쓸수는 있지만 호환성 등을 생각하면 사용하지 않는 것이 좋음
Const 정확도
- 언리얼 C++은 const의 사용을 지향하며 모든 코드에 대해 const 정확도를 맞춰야함 (공식 문서 예시 참고)
최신 C++ 언어 문법
- 언리얼 엔진은 기본적으로 C++ 20에서 컴파일 되며 빌드 시 요구되는 최소 언어 버전은 C++17임
- 컴파일 시간 어서트가 필요한 경우 static_assert 키워드를 사용할 수 있음
- override, final의 사용이 강력히 권장됨
- nullptr만을 사용해야하며 C 스타일의 NULL 매크로의 사용이 금지됨 (C++/CX에만 허용)
- 이터레이터, 변수를 람다에 바인딩 하는 경우, 템플릿 코드에서의 사용 외에는 auto 사용을 지양, 초기화 하려는 타입은 항상 명시해주어야 함 (C++ 17에서의 구조체 바인딩도 금지)
- 람다, 반복자 등을 사용할 때 코드 가독성과 유지보수성 향상을 위해 auto 가능 범위 기반 for문 사용이 추천됨
- 람다 함수의 경우 자유롭게 사용 가능
- 자동 캡처보다는 명시적(explicit) 캡처를 사용해야 함
- 열거형을 사용할 때는 enum class를 사용함
- 이동 시맨틱 사용시 std::move 대신 MoveTemp를 사용해 명시적으로 호출함
- 생성자 구문에서 값을 초기화하는 것이 지향됨
- 서드 파티 코드를 명시해야 라이브러리 새 버전으로 변경 사항을 병합하는 작업을 쉽게 할 수 있음
코드 포맷
- 중괄호는 새 줄에 중괄호를 작성하며 return 값만 있는 단일 구문 블록에 대해서도 항상 중괄호를 포함시킴
- if - else의 경우에도 마찬가지로 항상 중괄호를 사용함
탭 및 들여쓰기
- 코드 들여쓰기 시 스페이스바가 아닌 탭만을 사용하며, 탭의 크기는 스페이스 기준 4자임
Switch 문
- 다음 케이스로 넘어갈 때는 break, return문을 사용해 명시적으로 알려주어야 함
- 항상 디폴트 케이스를 만들어 두며 디폴트 케이스 역시 뒤에 새로운 케이스가 추가될 경우를 대비해 break문을 넣음
물리적 종속성
- 파일명의 경우 기존 명명규칙과 달리 접두사를 제외하고 사용하여 원하는 파일을 식별하는데 필요한 글자수를 줄임
- 헤더에 #pragma once를 사용해 불필요한 복수의 inclue를 방지
- include시 최대한 세밀하고 적게 사용하는 것이 지향되며 전방선언으로 대체 가능한 경우 include를 사용하지 않음
- inline 함수는 사용하지 않는 파일에 대해서도 리빌드를 강제하므로 남용하지 않는 것을 지향됨 (사소한 접근자이거나 확실한 이득이 있는 것으로 보일 때만 사용)
캡슐화
- 클래스 멤버는 public/protected로 사용되어야 하는 특별한 이유가 있지 않다면 항상 private로 선언함
- 더 이상 파생시킬 클래스가 아닌 경우 명시적으로 final을 사용하는 것을 지향함
일반적인 스타일 문제 일부 (나머지는 공식 문서 참고)
- 스트링 리터럴은 항상 TEXT() 타입을 사용
- 모든 .cpp 및 .h 파일은 파일 끝에 빈 줄 하나를 만들어야 gcc와 함께 올바르게 작동됨
- 복잡한 표현식을 사용하는 경우 중간 변수를 하나 선언하고 그것을 활용해 간소화함.
- 특정 타입에 대한 모든 포인터나 레퍼런스에 빠르게 Find in Files 를 사용할 수 있도록 포인터와 레퍼런스의 스페이스는 그 오른쪽에 딱 한 칸만 두어야 함
ex. FShaderType* Ptr
'Unreal > [강의] 언리얼 강의 3-1' 카테고리의 다른 글
5강 언리얼 오브젝트 리플렉션 시스템(=프로퍼티) 1 (0) | 2024.11.15 |
---|---|
4강 언리얼 오브젝트 (0) | 2024.11.11 |
3강 언리얼 C++ 기본 타입과 문자열 (0) | 2024.11.09 |
1강 언리얼 기본 설정 (0) | 2024.10.30 |
0강 강의 소개 (0) | 2024.10.30 |