본문 바로가기

C/C++

[Google C++ Style Guide] 8. Formatting

Google C++ Style Guide

 

8. Formatting

코딩 스타일과 포맷팅은 매우 제멋대로지만모든 사람이 같은 스타일을 사용한다면 프로젝트는 보다 쉬워진다포맷팅 규칙에 동의하지 않는 사람도 있겠지만금방 익숙해질 것이며 모든 사람의 코드를 보고 이해하기 쉽도록 한다.

 

A.     Line Length (라인 길이)

각 라인은 80자를 넘지 않는다.

 

이 룰은 논쟁의 여지가 있지만기존 코드는 모두 이를 고수하고 있으며일관성이 중요하다.

 

장점 :

이 룰을 좋아하는 사람들은 그들의 윈도우 창을 강제로 리사이징하는 것은 무례하고더 길 필요가 없다고 주장한다.일부는 다수 코드 윈도우 창을 나란히 세워 사용하기 때문에 윈도우 창을 더 넓히고 싶어하지 않는다최대 윈도우창의 가로 사이즈를 고려해서 작업 환경을 세팅한다. 80행이면 전통적인 표준이다.

 

단점 :

변화를 제안하는 사람은 더 넓은 라인이 가독성이 좋다고 주장한다. 80행 제한은 1960년대 메인프레임으로의 퇴보이며현대 장비는 더 긴 라인을 쉽게 보여줄 수 있는 와이드 스크린을 갖는다.

 

결론 :

최대 80 자로 한다.

 

예외 : 주석이 예제 명령어나 80라인보다 긴 URL을 포함하면 cut/paste를 쉽게 하기 위해 80자를 넘을 수 있다.

예외 : 긴 경로를 갖는 #incude 80자를 초과해도 좋다.

예외 : 최대 길이를 초과하는 #define guard에 대해서는 걱정할 필요가 없다.

 

 

B.      Non-ASCII Characterss

비 아스키 문자는 거의 사용하지 않으며, UTF-8 포맷팅을 사용해야 한다.

 

 

C.      Spaces vs. Tabs

스페이스만을 사용한다한번에 2 스페이스 들여쓰기 한다..

 

들여쓰기를 위해서 스페이스를 사용한다탭을 사용하지 않는다탭 키를 눌렀을 때 스페이스로 바뀌도록 에디터를 설정한다.

 

 

D.     Function Declarations and Definitions (함수 선언과 정의)

리턴 타입은 함수 이름과 같은 라인에적합하다면파라미터도 같은 라인에 작성한다.

 

함수는 아래와 같이 작성한다.

ReturnType ClassName::FunctionName(Type par_name1Type par_name2) {

  DoSomething();

  ...

}

 

파라미터가 한 라인에 들어가기 너무 많다면,

ReturnType LongClassName::ReallyReallyReallyLongFunctionName(

    Type par_name1,  // 4 space 들여쓰기

    Type par_name2,

    Type par_name3) {

  DoSomething();  // 2 space 들여쓰기

  ...

}

 

몇 가지 주의할 사항 :

-       리턴 타입은 항상 함수 이름과 같은 라인에 있어야 한다.

-       ‘( ‘는 항상 함수 이름과 같은 라인에 있어야 한다.

-       함수 이름과 ‘( ‘ 사이에 공백은 없어야 한다.

-       괄호( )와 파라미터 사이에 공백이 없어야 한다.

-       ‘ { ‘는 항상 마지막 파라미터와 같은 라인의 끝에 있어야 한다.

-       ‘ } ‘는 그 자체로 마지막 라인이 되거나, ‘ { ‘와 같은 라인에 있어야 한다.

-       ‘ ) ‘와 ‘ { ‘ 사이에 공백(a space)이 있어야 한다.

-       모든 파라미터는 선언부와 구현부에서 동일한 이름을 사용한다.

-       모든 파라미터는 가능한 정렬되어야 한다.

-       디폴트 들여쓰기는 2 스페이스이다.

-       개행된(wrapped) 파라미터들은 4 스페이스 들여쓰기를 한다.

 

함수가 const라면 const 키워드는 마지막 파라미터와 같은 라인에 있어야 한다.

 

//  라인에 작성

ReturnType FunctionName(Type parconst {

  ...

}

 

// 여러 라인에 걸쳐 작성

// const 키워드는 마지막 파라미터와 같은 라인

ReturnType ReallyLongFunctionName(Type par1,

                                      Type par2const {

  ...

}

 

 

일부 파라미터가 사용되지 않으면함수 정의부에서 변수 이름을 주석 처리 한다.

 

// Always have named parameters in interfaces.

class Shape {

 public:

  virtual void Rotate(double radians) = 0;

}

 

// Always have named parameters in the declaration.

class Circle : public Shape {

 public:

  virtual void Rotate(double radians);

}

 

// Comment out unused named parameters in definitions.

void Circle::Rotate(double /*radians*/) {}

 

// Bad – 나중에 구현해야 할때변수가 무엇을 의미하는지 불분명하다.

 void Circle::Rotate(double) {}

 

 

E.      Function Calls (함수 호출)

길이에 맞으면 한 라인에 작성하고그렇지 않으면 파라미터를 괄호로 감싼다.

                                           

함수 호출은 다음의 포맷을 따른다.

 

bool retval = DoSomething(argument1argument2argument3);

 

파라미터가 한 라인에 맞지 않으면다수 라인으로 쪼개야 하며첫번째 파라미터에 정렬한다.

 

bool retval = DoSomething(argument1,

                             argument2,

                             argument3,

                             argument4);

 

함수 시그너처가 너무 길어서 최대 라인 길이를 초과한다면모든 파라미터를 연속된 라인에 둔다.

 

if (...) {

  ...

  ...

  if (...) {

    DoSomethingThatRequiresALongFunctionName(

        very_long_argument1,  // 4 space 들여쓰기

        argument2,

        argument3,

        argument4);

  }

 

 

F.      Conditionals (조건문)

괄호 내에 공백을 두지 않는 것을 선호한다. else 키워드는 새로운 라인에 둔다.

 

기본적인 조건문에는 2가지 포멧이 있다괄호와 조건문 사이에 공백을 포함하는 것과 그렇지 않은 것이다.

 

가장 흔한 포맷은 공백이 없는 것이다공백을 포함해도 되지만일관성은 유지해야 한다파일을 수정할 때는 기존의 포맷을 사용하고새로운 코드를 작성한다면 프로젝트에서 사용하는 포맷을 사용한다선호하는 것이 없으면 공백 없이 사용한다.

if (condition) {  // no spaces inside parentheses

  ...  // 2 space indent.

else {  // The else goes on the same line as the closing brace.

  ...

}

 

괄호 내에 공백을 추가해도 된다.

if ( condition ) {  // spaces inside parentheses - rare

  ...  // 2 space indent.

else {  // The else goes on the same line as the closing brace.

  ...

}

 

주목할 점은 if와 ‘ ( ‘사이에는 공백이 있어야 한다. ‘ ) ‘와 ‘ {  ‘ 사이에도 공백이 있어야 한다.

if(condition)     // Bad – IF 다음에 공백이 없다.

if (condition){   // Bad - { 앞에 공백이 없다.

if(condition){    // Doubly bad.

 

if (condition) {  // Good - proper space after IF and before {.

 

 

간단한 조건문은 가독성을 위해 한 라인에 작성될 수도 있다라인이 간결하고 else 문이 없을 경우에만 사용한다.

if (x == kFooreturn new Foo();

if (x == kBarreturn new Bar();

 

else문이 있다면 위와 같이 사용하지 않는다.

 

// Not allowed - IF문은 else문이 있을  한줄에 작성하지 않는다.

if (xDoThis();

else DoThat();

 

일반적으로 ‘ { ‘는 한 라인 문장일 때는 필요하지 않지만사용해도 상관 없다복잡한 조건문이나 루프문은 가독성을 위해 ‘ { ‘를 사용한다어떤 프로젝트에서는 if문은 항상 ‘ { ‘이 있어야 한다고 요구할 수도 있다.

 

if (condition)

  DoSomething();  // 2 space indent.

 

if (condition) {

  DoSomething();  // 2 space indent.

}

 

그러나, if-else 구문 중에 한 부분만 ‘ { ‘를 사용하면 안된다.

 

// Not allowed – ‘ { ‘ if문에만 있다.

if (condition) {

  foo;

else

  bar;

 

// Not allowed - ‘ { ‘ else문에만 있다.

if (condition)

  foo;

else {

  bar;

}

 

// Curly braces around both IF and ELSE required because

// one of the clauses used braces.

if (condition) {

  foo;

else {

  bar;

}

 

 

G.     Loops and Switch Statements (루프문과 스위치문)

switch문은 블록을 위해 중괄호({ })를 사용하며빈 루프 몸체에는 ‘ { } ‘  continue를 사용한다.

 

Switch문 내의 case 블록은 중괄호를 가질 수도아닐 수도 있다중괄호를 사용하려면아래와 같이 사용한다.

 

조건이 열거자(enum) 값이 아니라면 switch 문은 항상 default case가 있어야 한다. (enum 값인 경우 컴파일러는 처리되지 않는 값에 대해 경고를 할 것이다). default case가 절대 실행되지 않아야 한다면간단히 assert를 한다.

switch (var) {

  case 0: {  // 2 space indent

    ...      // 4 space indent

    break;

  }

  case 1: {

    ...

    break;

  }

  default: {

    assert(false);

  }

}

 

빈 루프 몸체는 { } 또는 continue를 사용하며싱글 세미콜론을 사용하지 않는다.

 

while (condition) {

  // false가될때까지반복

}

for (int i = 0i < kSomeNumber; ++i) {}  // Good - empty body.

while (conditioncontinue;  // Good - continue logic 없다는 것을 의미

 

 

while (condition);  // Bad - do/while 처럼 보인다

 

 

H.     Pointer and Reference Expressions (포인터와 참조자 표현식)

Period(.) 이나 화살표(->) 주위에 공백은 없다포인터 연산자 다음에 공백은 없다

 

다음은 포인터와 참조자를 바르게 사용하는 예이다.

x = *p;   //포인터 연산는 * & 다음에 공백을 갖지 않는다.

p = &x;

x = r.y;  //멤버에 액세스하는 점(.)이나 화살표(->) 주위에 공백이 없다.

x = r->y;

 

포인터 변수나 파라미터를 선언할 때타입이나 변수 이름에 인접하게 asterisk(*)를 위치시킨다.

// These are fine, * 앞에 공백

char *c;

const string &str;

 

// These are fine, * 뒤에 공백.

charc;    // but remember to do "char* c, *d, *e, ...;"!

const stringstr;

 

char * c;  // Bad - *  사이드에 공백

const string & str;  // Bad - &  사이드에 공백

하나의 파일에서 일관성 있게 사용하며기존 파일 수정 시 파일 내의 스타일을 사용한다.

 

I.       Boolean Expressions

Boolean 표현식이 표준 라인 길이를 넘는다면일관성 있게 쪼갠다.

 

예를 들어논리 AND 연산자는 항상 라인의 끝에 있어야 한다.

if (this_one_thing > this_other_thing &&

    a_third_thing == a_fourth_thing &&

    yet_another && last_one) {

}

 

위 예에서 논리 AND 연산자 &&는 모두 라인의 끝에 있다구글 코드에서는 이것이 흔히 사용된다.

불 표현식을 괄호로 감싸는 것은 자유롭게 사용한다그것이 적절히 사용되면 가독성을 올려주기 때문이다.

and와 compl과 같은 단어로 된 연산자보다 &&와 ~ 같은 구두점(punctuation) 연산자를 사용한다.

                         

J.      Return Values (리턴 값)

return 표현식에 불필요한 괄호는 사용하지 않는다.

 

아래와 같이 return expr 에서 괄호를 사용한다.

return result;                   // 간단한 경우괄호가 없다

return (some_long_condition &&  // 복잡한 표현식일 경우 가독성을 위해

another_condition);      // 괄호를 사용한다.

 

return (value);                // You wouldn't write var = (value);

return(result);                // return 함수가 아니다!

 

K.      Variable and Array Initialization (변수와 배열 초기화)

또는 ( )로 초기화 한다.

                

= ( )를 선택적으로 사용할 수 있다다음은 모두 옳은 표현이다.

int x = 3;

int x(3);

string name("Some Name");

string name = "Some Name";

 

L.      Preprocessor Directives (전처리 지시자)

전처리 지시자는 들여쓰지 않는다대신 라인의 처음에서 시작한다.

 

전처리 지시자가 들여쓰기 되어 있는 코드 내부에 있을지라도지시자는 라인의 처음에서 시작한다.

// Good - directives at beginning of line

  if (lopsided_score) {

#if DISASTER_PENDING      // Correct - Starts at beginning of line

    DropEverything();

#endif

    BackToNormal();

  }

 

// Bad - indented directives

  if (lopsided_score) {

    #if DISASTER_PENDING // Wrong! The "#if" should be at beginning of line

    DropEverything();

    #endif                // Wrong!  Do not indent "#endif"

    BackToNormal();

  }

 

M.    Class Format (클래스 포맷)

public, protected, private 순서를 갖으며각각 스페이스 들여쓴다.

 

클래스 선언의 기본 포맷은 다음과 같다.

class MyClass : public OtherClass {

 public:      // Note the 1 space indent!

  MyClass();  // Regular 2 space indent.

  explicit MyClass(int var);

  ~MyClass() {}

 

  void SomeFunction();

  void SomeFunctionThatDoesNothing() {

  }

  void set_some_var(int var) { some_var_ = var; }

  int some_var() const { return some_var_; }

 

 private:

  bool SomeInternalFunction();

 

  int some_var_;

  int some_other_var_;

  DISALLOW_COPY_AND_ASSIGN(MyClass);

};

 

 

N.     Constructor Initializer Lists (생성자 초기화 리스트)

생성자 초기화 리스트는 모두 한 라인에 작성하거나여러 라인에 작성할 경우 4 space 들여쓰기 한다.

 

아래와 같이 초기화 리스트를 작성한다.

MyClass::MyClass(int var) : some_var_(var), some_other_var_(var + 1) {}

 

MyClass::MyClass(int var)

    some_var_(var),              // 4 space indent

      some_other_var_(var + 1) {  // lined up

  DoSomething();

}

 

O.     Namespace Formatting (네임스페이스 포맷)

네임스페이스의 내용은 들여쓰기 하지 않는다.

 

아래와 같이 사용한다.

namespace {

void foo() {  // Correct.  No extra indentation within namespace.

}

}  // namespace

 

다음과 같이 들여쓰기 하지 않는다.

namespace {

  void foo() {

}

}  // namespace

 

중첩으로 사용할 때각 네임스페이스는 자신의 라인에 둔다.

namespace foo {

namespace bar {

 

P.      Horizontal Whitespace (수평 빈 공간)

위치에 따라 수평 빈 공간을 사용한다라인 끝에는 절대 빈 공간을 두지 않는다.

 

General (일반)

  생략...

Loops and Conditionals (루프와 조건문)

  생략... 

Operators (연산자)

  생략...

Templates and Casts (템플릿과 캐스트)

  생략...

 

Q.     Vertical Whitespace (수직 빈 공간)

수직 빈 공간은 최소화 한다.

  

경험상빈 라인은 다음과 경우에 유용하다 :

-       함수 시작과 끝에 있는 빈 라인은 가독성에 거의 도움을 주지 않는다.

-       if-else  chain 내부에 있는 빈 라인은 가독성에 도움을 준다.