Google C++ Style Guide
본 장에서는 Google C++ 스타일 가이드에 대해 요약하고 있습니다.
cpplint는 정적 코드 체커 프로그램입니다. cpplint를 이용하면 작성한 소스코드가 Google C++ 스타일 가이드를 잘 따르는지 체크할 수 있습니다.
cf. vscode cpplint extension
Contents
Background
C++는 구글 오픈 소스 프로젝트에서 많이 사용되는 주요 프로그래밍 언어들 중 하나입니다. 모든 C++ 프로그래머가 알고 있듯이 C++는 매우 강력한 기능을 제공하고 있습니다. 그러나 강력한 기능을 제공하기 위한 언어의 복잡성 때문에 때로는 버그를 생산하기도 하고, 읽기 어려운 코드가 된다거나 유지 보수가 어려워지는 경우가 많습니다.
이 가이드의 목적은 C++ 코드를 작성하면서 무엇을 해야하고, 또 무엇을 하면 안되는지 설명하는 데에 있습니다. 이러한 규칙들은 코드를 관리 가능하게 하고, 프로그래머들이 C++의 기능을 생산적으로 사용할 수 있게 할 것입니다.
Header file
Self-contained Headers
The #define Guard
다중 참조를 막기 위해 #define을 이용해야 합니다. 포맷은 __H을 이용합니다. 예를 들어 foo/src/bar/baz.h 파일은 다음의 가드를 이용할 수 있습니다.
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
Include What You Use
Forward Declarations
Inline Functions
Names and Order of Includes
소스파일을 작성할 때 C 시스템, C++ 표준 라이브러리와 같이 많은 헤더 파일을 참조하게 됩니다. 헤더 파일을 참조할 때 다음의 순서로 작성해야 합니다.
- dir2/foo2.h.
- A blank line
- C system headers (more precisely: headers in angle brackets with the .h extension), e.g., <unistd.h>, <stdlib.h>.
- A blank line
- C++ standard library headers (without file extension), e.g., , .
- A blank line
- Other libraries' .h files.
- Your project's .h files.
#include "foo/server/fooserver.h"
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/server/bar.h"
Scoping
Namespaces
Unnamed Namespaces and Static Variables
Nonmember, Static member, and Global Functions
Local Variable
가능하면 지역 변수의 초기화는 선언과 가까워야 합니다. 변수를 선언하고 나서 값을 할당하기 보다는 선언과 동시에 초기화하는 것이 좋습니다.
int i;
i = f(); // Bad -- initialization separate from declaration.
int j = g(); // Good -- declaration has initialization.
Static and Global Varaiable
thread_local Variables
Classes
Doing Work in Constructors
생성자에서 가상 함수를 호출하는 것을 피해야 합니다.
Implicit Conversions
변환 생성자 (Conversion Constructor)를 호출할 때 암시적 변환을 사용하지 말아야 합니다. explicit 키워드를 이용하여 명시적으로 생성자를 호출해야 합니다.
class Foo {
explicit Foo(int x, double y);
...
};
void Func(Foo f);
Func({42, 3.14}); // Error
Copyable and Movable Types
Structs vs. Classes
Structs vs. Pairs and Tuples
Operator Overloading
Access Control
Declaration Order
Functions
Inputs and Outputs
Write Short Functions
Function Overloading
Default Arguments
Trailing Return Type Syntax
Google-Specific Magic
Ownership and Smart Pointers
cpplint
Other C++ Features
Rvalue Reference
Friends
Exceptions
noexcept
Run-Time Type Information (RTTI)
Casting
Streams
Preincrement and Predecrement
Use of const
Use of constexpr
Integer Types
64-bit Portability
Preprocessor Macros
0 and nullptr/NULL
sizeof
Type Deduction (including auto)
Class Template Argument Deduction
Designated Initializers
Lambda Expressions
Template Metaprogramming
Boost
std::hash
Other C++ Features
Nonstandard Extensions
Aliases
Naming
General Naming Rule
설령 다른 팀 사람들이 코드를 읽더라도 이해할 수 있는 명확한 이름을 사용해야 가독성을 높일 수 있습니다.
우선 객체의 목적이나 의도를 설명할 수 있는 이름을 사용해야 합니다. 이름이 길어지는 것을 두려워 할 필요는 없습니다. 새로운 프로그래머가 코드를 직관적으로 이해할 수 있도록 하는 것이 이름의 길이보다 훨씬 더 중요합니다.
- 약어 사용을 최대한 피해야 합니다 (위키피디아에 나열된 약어는 대부분 사용할 수 있습니다)
- 단어에 사용되는 문자를 임의대로 줄이지 않아야 합니다
class MyClass {
public:
int CountFooErrors(const std::vector<Foo>& foos) {
int total_number_of_foo_errors = 0; // Overly verbose given limited scope and context
for (int foo_index = 0; foo_index < foos.size(); ++foo_index) { // Use idiomatic `i`
...
++total_number_of_foo_errors;
}
return total_number_of_foo_errors;
}
void DoSomethingImportant() {
int cstmr_id = ...; // Deletes internal letters
}
private:
const int kNum = ...; // Unclear meaning within broad scope
};
여기서 일반적으로 널리 알려진 약어는 사용해도 괜찮습니다. 예를 들어 반복문에서 사용되는 i 변수나 템플릿 파라미터에 사용되는 T와 같은 변수는 사용해도 좋습니다.
class MyClass {
public:
int CountFooErrors(const std::vector<Foo>& foos) {
int n = 0; // Clear meaning given limited scope and context
for (const auto& foo : foos) {
...
++n;
}
return n;
}
void DoSomethingImportant() {
std::string fqdn = ...; // Well-known abbreviation for Fully Qualified Domain Name
}
private:
const int kMaxAllowedConnections = ...; // Clear meaning within context
};
File Names
파일 이름은 반드시 모두 소문자로 사용해야 하고 underscore() 또는 dash (-)를 포함할 수 있습니다. underscore와 dash 중 어떤 것을 사용해도 좋으므로 프로젝트에서 사용하고 있는 규칙을 따르면 됩니다 .만약 프로젝트에서 사용중인 일관된 규칙이 없다면 ""를 사용하는 것이 좋습니다.
맞게 작성된 파일 이름들은 다음과 같습니다.
-
my_useful_class.cc
-
myusefulclass.cc
-
myusefulclass_test.cc // _unittest 와 _regtest 는 삭제되었습니다
C++ 파일의 확장자는 .cc를 사용해야 하고 헤더 파일은 .h 확장자를 사용해야 합니다.
이미 /usr/include 에 정의된 파일 이름을 사용해서는 안됩니다.
파일 이름은 의도와 목적을 설명할 수 있도록 구체적이어야 합니다. 단순히 log.h 보다는 http_server_log.h가 더 낫습니다.
Type Names
타입 이름에 사용된 단어의 시작은 underscore가 없는 대문자여야 합니다. 예를 들어 MyExcitingClass, MyExcitingEnum 등이 될 수 있습니다.
// classes and structs
class UrlTable { ...
class UrlTableTester { ...
struct UrlTableProperties { ...
// typedefs
typedef hash_map<UrlTableProperties *, std::string> PropertiesMap;
// using aliases
using PropertiesMap = hash_map<UrlTableProperties *, std::string>;
// enums
enum class UrlTableError { ...
Variable Names
변수 이름은 undercore가 포함된 소문자로 작성해야 합니다. 구조체가 아닌 클래스의 멤버 변수는 trailing underscore를 붙여서 구분합니다
std::string table_name; // OK
Class Data Memebers
class TableInfo {
...
private:
std::string table_name_; // OK - underscore at end.
static Pool<TableInfo>* pool_; // OK
};
Constant Names
프로그램 동작중에 고정된 값을 가지는 constexpr 또는 const로 선언된 변수는 k로 시작하는 mixed case 이름을 사용해야 합니다.
const int kDaysInAWeek = 7;
const int kAndroid8_0_0 = 24; // Android 8.0.0
Function Names
대문자로 시작하는 mixed case 이름을 사용합니다.
AddTableEntry()
DeleteUrl()
OpenFileOrDie()
Namespace Names
underscore를 사용한 소문자를 사용합니다. 네임스페이스 간 충돌을 피하기 위해 top-level 네임스페이스는 프로젝트 또는 팀 이름을 사용해야 합니다.
Enumerator Names
Enumerator는 macro가 아닌 constants처럼 정의합니다. 예를 들어 ENUM_NAME 대신 kEnumName과 같이 사용해야 합니다.
enum class UrlTableError {
kOk = 0,
kOutOfMemory,
kMalformedInput,
};
2009년 1월 이전까지는 enumerator를 macro처럼 사용했으나, 실제 macro와 이름 충돌이 발생하는 경우가 있었습니다. 새로운 코드를 작성할 때는 constant-style 이름을 사용해야 합니다.
Macro Names
Exceptions to Naming Rule
Comments
Comment Style
한 가지 방법만 통일성 있게 사용한다면 // 또는 /* */ 을 사용할 수 있습니다. (// 가 더 일반적)
Formatting
Line Length
코드를 작성할 때 한 라인의 길이는 80 글자를 넘으면 안됩니다. 사실 이 규칙이 다소 논란이 된다는 것을 알고 있지만 우리는 통일된 일관성이 더 중요하다고 생각하고 있습니다.
다음의 경우에는 80 글자를 넘을 수 있습니다.
- 주석을 80 글자 단위로 나누면 가독성을 심각하게 저해시키는 경우 (e.g. 프로그램 명령어 설명 또는 특정 URL과 같이 복사-붙여넣기가 필요한 경우)
- 80 글자를 넘는 raw-string 문자열이 있는 경우
- 헤더 파일 가이드
- using-declaration
Non-ASCII Chracters
Spaces vs. Tabs
오직 스페이스만 사용해야 합니다. 인덴트는 2 스페이스를 사용합니다.
tab을 사용해서는 안되며, tab 키를 스페이스로 자동변환하도록 에디터를 설정하는 것이 좋습니다.
Function Declarations and Definitions
함수의 반환 타입은 함수와 같은 라인에 있어야 합니다.
ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {
DoSomething();
...
}
만약 한 줄에 다 넣기 어려운 경우 다음과 같이 쓸 수 있습니다.
ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2,
Type par_name3) {
DoSomething();
...
}
함수의 이름이 너무 길어서 파라미터를 다 적을 수 없는 경우는 다음과 같이 씁니다.
ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
Type par_name1, // 4 space indent
Type par_name2,
Type par_name3) {
DoSomething(); // 2 space indent
...
}
Floating-point Literals
Functions Calls
Braced Initializer List Format
Conditions
if-else와 같은 조건문은 다음과 같이 사용합니다. 잘못된 예시는 다음과 같습니다.
if(condition) { // Bad - space missing after IF
if ( condition ) { // Bad - space between the parentheses and the condition
if (condition){ // Bad - space missing before {
if(condition){ // Doubly bad
if (int a = f();a == 10) { // Bad - space missing after the semicolon
좋은 예시는 다음과 같습니다
if (condition) { // no spaces inside parentheses, space before brace
DoOneThing(); // two space indent
DoAnotherThing();
} else if (int a = f(); a != 3) { // closing brace on new line, else on same line
DoAThirdThing(a);
} else {
DoNothing();
}
Loops and Switch Statements
switch (var) {
case 0: { // 2 space indent
... // 4 space indent
break;
}
case 1: {
...
break;
}
default: {
assert(false);
}
}
while (condition) {
// Repeat test until it returns false.
}
for (int i = 0; i < kSomeNumber; ++i) {} // Good - one newline is also OK.
while (condition) continue; // Good - continue indicates no logic.
'💻 programming > clean code' 카테고리의 다른 글
gitignore 파일 생성하기 (cmake, python, c++, c#) (0) | 2021.08.05 |
---|---|
[Modern CMake] target_link_library, link_library (임시) (0) | 2021.04.15 |
댓글