-
모듈 외부로 노출되는 인터페이스가 아니라면 모듈 내부 코드의 매개변수와 반환값은 언제나 유효해야 합니다.
-
매개변수는 기본적으로
Null이 허용되지 않습니다. -
반환값은
Null이 허용됩니다.
-
Precondition에 가정하여 작성하는 코드라면
Assert및STATIC_ASSERT를 모든 곳에 사용합니다. -
표준
assert와static_assert대신 별도로 구현한Assert(exp),STATIC_ASSERT(exp)를 사용합니다. -
재사용하지 않는 코드일 경우 함수로 작성하는 것을 최대한 지양합니다.
-
const를 최대한 사용합니다. -
값 수정이 없는 함수는 모두
const키워드를 붙입니다. -
#include <>을 항상#include ""보다 위에 작성합니다. -
struct나class에서 값 변경을 막기 위해 변수를const나참조(&)로 선언하지 않습니다. -
Magic Number가 없도록 상수 변수나enum,#define을 사용합니다. -
한 줄의 식이 너무 길어지거나 복잡한 경우 중간 변수를 적극적으로 활용합니다.
const int AVX_BYTE = 32;
const bool bIsAligned = (reinterpret_cast<uintptr_t>(ptrArray) % AVX_BYTE) == 0;
const int IS_DISASSEMBLED_MASK = 0x80000000;
const bool bIsMarked = (reinterpret_cast<uintptr_t>(ptrArray[i]) & IS_DISASSEMBLED_MASK) != 0;
if (bIsAligned && bIsMarked == false)
{
// Something
}- 빈 줄과 주석을 통해 코드의 섹션을 적극적으로 분리합니다.
void strrev(char* str)
{
Assert(str != NULL);
// Find end of string
char* right;
for (right = str; *right != '\0'; right++)
{
}
right -= 1;
// Reverse
for (char* left = str; left < right; left++, right--)
{
// swap
char* tmp = *left;
*left = *right;
*right = tmp;
}
}-
함수의 매개변수가 복잡하거나 많아질 경우
struct로 매개변수를 전달합니다. -
NULL이나 널 문자를 조건문으로 확인할 땐if (retAddr == NULL)처럼 명확히 표기합니다. -
지역변수의 선언은 되도록 사용하기 직전에 사용될 코드와 붙여 선언합니다.
-
메모리 할당
new에 대해 실패하는 경우는 검사하지 않고 의도적으로Crach를 발생시킵니다. -
특수한 상황이 아니라면
예외를 던지지 않습니다. -
delete이후 곧바로NULL을 대입합니다. -
되도록 추상화 하지 않은 raw한 api를 사용합니다.
-
내부구현에 대한 코드는
private,protected로 선언하거나namespace::detail등으로 숨깁니다.
namespace detail
{
template <typename T>
void InternalFunction()
{
// Something
}
}
template <typename T>
class MyClass
{
public:
void PublicFunction()
{
detail::InternalFunction();
}
};NULL을 사용하거나out매개변수를 사용할 일이 없다면 매개변수는 항상&(레퍼런스)를 사용합니다.
-
들여쓰기는
Tab이 아닌Space를 사용합니다. (4칸) -
중괄호를 열고 닫을 때는 언제나 새로운 줄에 작성합니다. (
struct,enum예외)
while (true)
{
// Something
}- 중괄호 안에 코드가 한 줄만 있더라도 중괄호를 작성합니다.
if (expression)
{
Assert(false);
}- 변수는 한 줄에 한 변수만 선언합니다.
int x;
int y;
bool bExitFlag;- 포인터(
*)나 레퍼런스 기호(&)는 자료형에 붙입니다.
int& mCount;
int* mCount;- 생성자의
Initialize List는 한 줄에 하나의 변수만 초기화 하도록 작성합니다.
Human::Human(std::string name)
: mHeight(175)
, mName(name)
, mAge(20)
{
}Iterator에 대한 증감문은 전위 연산자를 사용합니다.
for (std::vector<int>::iterator It = vec.begin(); It != vec.end(); ++It)
{
}-
한 줄이 너무 길어져도 각 매개변수에 주석을 달기 위함이 아닌 한 다음 줄로 넘기지 않습니다.
-
#define을 활용한include guard대신#pragma once를 사용합니다. -
템플릿 전달인자가 중첩되는 경우
<,>와 자료형 사이에 공백을 추가합니다.
std::vector< std::vector< int > > vec;namespace는 들여쓰기를 하지 않습니다.
namespace MyNamespace
{
class MyClass
{
};
}-
Class와Struct,Enum은 파스칼 포기법을 따릅니다.(ex. HumanFactory, ComposeUnit) -
지역변수와 매개변수는 카멜 표기법을 따릅니다.
(ex. numArray, buffRecvBytes) -
public함수는 파스칼 표기법을 따릅니다. -
private함수는 카멜 표기법을 따릅니다. -
멤버변수 이름은
m으로 시작해야 합니다.(ex. mSize, mServerPort) -
#define으로 정의된 상수는 대문자로 쓰되 밑줄로 단어를 구분합니다.(ex. REG_PORT_MAX) -
bool으로 정의된 변수는 변수 이름을b로 시작합니다.(ex. bIsEmpty, mbExpired) -
인터페이스의 이름은
I로 시작합니다.(ex. ICharacter) -
매개변수 값으로
Null이 허용된다면 해당 매개변수 이름은Opt로 끝납니다. -
결과를 반환 받을 변수의 주소를 매개변수로 받아 해당 주소로 값을 반환하는 경우에는 매개변수의 이름이
out으로 시작합니다. 반환하는 값이 포인터일 경우에는outPtr로 시작합니다.
bool TryBlocking(int option, int* outResultSize, void** outPtrResult);-
재귀함수는 이름이
Recursive로 끝납니다. -
함수의 이름은
동사로 시작합니다.(ex. SearchPosition(), GetIndex()) -
goto문의 라벨 이름은 대문자로 쓰되 밑줄로 단어를 구분합니다.(ex. CLEANUP_ARGS:, END_SEARCH_LOOP:) -
enum class의 이름은E로 시작합니다.(ex. EColor, EState) -
File Descriptor이나Handle은 변수 이름 앞에h를 붙입니다.(ex. hFile, hSocket) -
Event는 변수 이름 앞에ev를 붙입니다.(ex. evShutdown, evComplete)**
- 클래스 멤버의 배치 순서는 다음을 따릅니다.
{
friend <Class>
public <Method>
protected <Method>
private <Method>
protected <Variable>
private <Variable>
};- 모든 멤버변수는
public이 아닌protected나private입니다.
해당 프로젝트는 Javadoc의 주석 스타일을 따릅니다.
프로젝트는 doxygen을 사용하여 문서화 됩니다.
- 외부에 노출되는 함수나 클래스, 변수에 대한 주석은 아래와 같이 작성합니다.
(JavaDoc에 따라 첫 번째 줄은 간단한 설명(@brief) 입니다.)
/** This is a brief description of the function.
*
* @details This is a detailed description of the function.
* It can span multiple lines.
*
* @param param1 Description of param1.
* @param param2 Description of param2.
*
* @note This is a note.
* @warning This is a warning.
* @todo This is a todo.
* @see SomeOtherFunction(), SomeOtherClass
* @bug This is a bug.
* @return Description of the return value.
*/
int Function(int param1, int param2);- 함수나 변수의 선언에 대한 주석은
/**,*/를 사용하여 바로 위에 작성합니다.
/** Parse the message and process it.
*
* At the end of processing, deallocate the message to the memory pool it was allocated.
*/
bool ProcessMessage(int clientIdx);
/** The number of elements in the array. */
int mCount;
/** Queue of received messages pending to be processed
*
* At the end of processing,
* it should be returned to the memory pool it was allocated from */
std::vector<MsgBlock_t*> msgBlockPendingQueue;- 구현 내부에 대한 주석은 기본적으로
//를 사용하여 작성합니다.
주석 내용이 길어질 경우/**,*/를 사용하여 작성합니다.
// Receive up to [MESSAGE_LEN_MAX] bytes in the MsgBlock allocated by the mMesssagePool
// And add them to the client's message pending queue.
// It is processed asynchronously in the main loop.
int nTotalRecvBytes = 0;
int nRecvBytes;
do {
MsgBlock_t* newRecvMsgBlock = mMsgBlockPool.Allocate();
STATIC_ASSERT(sizeof(newRecvMsgBlock->msg) == MESSAGE_LEN_MAX);
nRecvBytes = recv(client.hSocket, newRecvMsgBlock->msg, MESSAGE_LEN_MAX, 0);//<는 해당 줄의 주석이 다음 줄의 코드에 대한 주석임을 나타냅니다.
int x; //< This is a comment.- 연관된 함수나 변수는 그룹화하여 주석을 작성합니다.
///@{와///@}를 사용하여 그룹을 나타냅니다.
/** @name GroupName
* @brief This is a group of functions.
*
* This is a detailed description of the group.
* It can span multiple lines.
*/
///@{
int Function1();
/** This is a detailed description of the function. */
int Function2();
size_t mSize;
///@}-
switch문의default에 도달할 일이 없다면Assume(0)을 기입하여 컴파일러 최적화 힌트를 제공합니다. -
Assert문은Release빌드에서Assume으로 치환되어 컴파일러 최적화 힌트를 제공합니다. -
함수 결과를 리턴할 때
RVO나NRVO를 위해하나의
return문 만 사용하거나 하나의 변수만return하도록 작성합니다. -
동일한 사이즈에 대하여 자주 할당하는 경우,
new와delete를 메모리풀로 직접 구현하여 사용합니다. -
SIMD와특수화를 위해 특정 사이즈 단위의 정렬/연산을 사용합니다. (ex._memset128(),_memcpy256_aligned(),for(; i < n; i += 256)) -
SIMD와 컴파일러 최적화를 위해2의 거듭제곱 수를 적극적으로 사용합니다. -
std::vector와 같은 컨테이너를 사용할 경우, 선언 후 곧바로reserve를 호출하여 사용할 크기를 한 번에 할당합니다.