ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • C++ coding Guide-line 1
    Clean Code 2024. 5. 17. 18:00

    프로그래밍을 잘하기 위해서는 coding guide-line 혹은 coding convention을 잘 준수해야 한다.

    실제로 Clean code라는 개념과 관련책도 있다.

    Clean code를 작성하기 위해서는 코드의 구조를 잘잡고 개발 가이드라인을 잘 준수해야 한다!!

    코드의 상태가 너무 안좋으면 나중에 보는 사람들이 읽고 수정하는데 문제가 발생할 수 있다.

     

    Clean Code(클린 코드) : 네이버 도서

    네이버 도서 상세정보를 제공합니다.

    search.shopping.naver.com

    관심 있으면 이 책을 읽어보자. (나도 읽어보고 싶다..)

    Clean code의 저자는 주석 없이 코드를 작성할 수 있다고 이야기를 한다.

    General C++ guide line

    가장 일반적인 Coding guide line을 살펴보자.

    일반적으로, Google에 잘 정리되어 있으며, 필자의 경우 Google기반으로 작성된 ROS1, ROS2 Document를 참고한다.

     

    Google Guide line

     

    Google C++ Style Guide

    Google C++ Style Guide Background C++ is one of the main development languages used by many of Google's open-source projects. As every C++ programmer knows, the language has many powerful features, but this power brings with it complexity, which in turn ca

    google.github.io

     

     

    ROS1 Guide line

     

    CppStyleGuide - ROS Wiki

    ROS C++ Style Guide This page defines a style guide to be followed in writing C++ code for ROS. This guide applies to all ROS code, both core and non-core. For Python, see the PyStyleGuide and for Javascript, see the ROS JavaScript Style Guide For general

    wiki.ros.org

     

     

    PyStyleGuide - ROS Wiki

    PyStyleGuide This page defines a style guide to be followed in writing Python code for ROS. This guide applies to all ROS code, both core and non-core. For C++, see the C++ Style Guide and for Javascript, see the ROS JavaScript Style Guide Coding Style Pyt

    wiki.ros.org

     

    ROS2 Guide Line (Humble)

     

    Code style and language versions — ROS 2 Documentation: Humble documentation

    In order to achieve a consistent looking product we will all follow externally (if possible) defined style guidelines for each language. For other things like package layout or documentation layout we will need to come up with our own guidelines, drawing o

    docs.ros.org

     

    1. Naming

    어떻게 이름을 지을것인지(이름 짓기), 어떤 타입으로 기술할 것인지 (기술 방법) 정해야한다.

    이름을 짓는것은 명확한 방법이 제시되지 않기 때문에 쉽지 않다.

    이름은 간결하고 명확하게 정해야한다.

    이름을 작성하는 방법은 여러가지가 있다.

    다음 예시를 보자.

    /*
    	num apple의 예시 (크게 4가지)
    */
    
    const int num_apple; // snake_case
    const int NUM_APPLE; // SCREAMING_CASE
    const int NumApple; // PascalCase(UpperCamelCase)
    const int numApple; // camelCase
    const int num-apple; // Kebab-case 주로 yml과 url에서 사용.

     

    이름을 명확히 짓는것은 개발의 첫 걸음이다.

    만약 변수 명이 명확하지 않고 다음과 같으면 어떤 역할을 하는지 알 수 없다.

    이는 코드의 가독성을 해친다.

    int a = 0; // 이해할 수 없는 단어.
    uint temp = 0; // 의미가 명확하지 않음.
    // 부사나 형용사는 변수를 표현하는데 사용할 수 없음.
    double incredibly_accurate_score = 0.0F;

     

    축약어도 팀원과 사전 협의가 되지 않았다면, 무작정 사용하면 안된다. (일관성을 가져야 함)

    image 변수의 경우도 보면, image, img등 다양하게 사용되고 있다.

    축약어가 다양할 경우 문서로 관리하자.

    int kf_ind = 0; // Kalman filter인지 keyframe인지 알 수 없다.

     

    동일한 타입의 기술 방법을 사용해야한다.

    예를 들어 변수는 변수만의 기술 방법함수는 함수만의 기술 방법을 채택해야 한다.

    어떠한 경우에도 어겨서는 안된다!!

    위에도 언급했듯이 snake_case, SCREAMING_CASE, PascalCase, camelCase등이 있으며, 이를 가지고 다음을 기술해야한다.

    • variable
    • constant
    • function
    • struct
    • class
    • enum
    • type
    • namespace
    • file

    누군가 만들어놓지 않았다면, 새로 규칙을 만들거나 Google, ROS1, ROS2의 규칙을 따르자! (이를 자동화해주는 툴도 있다.)

    그럼 하나씩 살펴보자.

     

    - variable

    • 특정 데이터를 담는 그릇.
    • 구체적으로 명사의 형태로 기술.
    • image, img같은 혼란을 야기하는 기술을 하지 않음.
    • 수식, 인덱스 같은 경우 local scope에 한하여 의미 없는 기술 가능.
    • 전역변수는 사용하지 않으면 좋지만, 꼭 필요한 경우 g_variable로 사용. (헝가리안 기술 사용)
    int image_frame_size = 0;
    
    for (int i = 0; i < vector_size; ++i) // 한시적 허용
    
    int g_total_score; // 전역변수

     

    - constant

    • 기본적인 명명법은 variable과 동일
    • C의 경우 #define을 사용했지만, modern C++의 경우 사용하지 않음.
    • constexpr를 사용.
    constexpr int BUFFER_SIZE = 100;

    const가 아닌 constexpr를 사용하는 이유는 다음과 같다.

     

    const: run-time 중에 값이 결정. (코드를 직접 돌리지 않으면 error를 알 수 없다.)

    constexpr: compile-time 중에 값이 결정. (Compile단에서 error를 검출할 수 있다.)

     

    - function

    • 동사 + 명사 형태로 기술.
    • 동작하는 것이기 때문에 함수의 기능을 명확히 기술.
    • 한 함수는 한가지 동작만 하도록 구현. (여러개를 구현하지 말자!)
    int get_current_time(); // 동사 + 명사 형태로 명확한 기술.

     

    - struct

    • 데이터를 담는 객체.
    • 구체적, 명사의 형태로 기술.
    struct Pixel
    {
        uint16_t x;
        uint16_t y;
    };

     

     

    - class

    • 구체적, 명사의 형태로 기술.
    • 해당 클래스의 기능을 명확하게 인지할 수 있는 이름으로 기술.
    • 멤버변수의 경우 접미어로 '_'를 추가. (헝가리안 기술은 사용하지 않음.)
    class FeatureDetector
    {
        int feature_size_;
        int m_feature_size; // 가이드라인마다 다르지만, 헝가리안은 사용하지 않는다.
    }

     

    - enum

    • 객체의 state를 표현.
    • 구체적, 명사의 형태로 기술.
    • enum class를 사용.
    enum class TimeScale
    {
      NANO = 0,
      MILLI = 1,
      MICRO = 2
    };

     

    - type

    • 구체적, 명사의 형태로 기술.
    template<typename DataType>
    DataType sum(const DataType& lhs, const DataType& rhs)
    {
        return lhs + rhs;
    }

     

    - namespace

    • 구체적, 명사의 형태로 기술.
    • 모듈의 기능을 분리 및 나눌때. 계층을 나눌때 사용. (Hierarchy)
    • 다중 네임스페이스를 통한 모듈 분리를 적극 권장
    • using namespace로 namespace를 걷어내는것은 위험하다. (특정 local 지역에서 사용하는 경우는 괜찮음.)
    • 네임스페이스 단위로 폴더 계층을 나눈다.
    // 좋은 예시
    namespace sensor
    {
    namespace lidar
    {
    class Parser
    {
    };
    }
    namespace Camera
    {
    class Parser
    {
    };
    }
    }

     

    - file

    • 구체적, 명사의 형태로 기술.
    • 특별히 약속된 경우를 제외하곤 class당, .hpp, .cpp 두개의 파일을 할당하여 사용.
    • 파일의 이름은 class의 이름과 동일.
    // ../time/stamp.hpp 선언부만 존재
    namespace time
    {
    class Stamp
    {
        Stamp();
    };
    } // namespace time
    
    // ../time/stamp.cpp 구현부만 존재
    time::Stamp::Stamp()

     

    Formatting

    들여쓰기를 어떻게 할지, Line Length는 어떻게 할지 기타등등의 formatting이 필요하다.

    하지만 이를 일일히 하기 쉽지 않으므로 대표적으로 Clang format으로 자동화 할 수 있다.

    Clang formatting에 대해 잘 알고 사용한다면 좋은 코드를 작성할 수 있다.

     

    - Line Length

    Google style: 80개

    ROS2 style: 100개

    이는 한 화면에서 전체적으로 볼 수 있는 크기를 지정했다고 한다.

     

    - Non-ASCII Characters

    • 반드시 'UTF-8' 형식을 사용.
    • 어길시 개발 환경에 따라서 문자가 잘못 표시되는 경우가 생김.

     

    - Indent

    • C/C++의 경우 2space, Python의 경우 4space를 일반적으로 사용.
    • 들여쓰기 종류엔 tab, space가 있지만, tab의 경우 IDE나 개발 환경에 따라 코드의 포멧이 달라짐.
    • tab을 사용하지  않고 space 사용을 권장.

    - Brace

    • 대괄호[], 중괄호{}, 소괄호(), 겹화살괄호<<>>, 홑화살괄호<>.
    • 중괄호의 경우 일반적으로 한 줄 점유.
    • 중괄호 사이 내용이 없다면, 한 줄 기술 가능.

    - Declarations and Definitions

    • 함수 반환 타입은 함수의 이름과 같은 줄에 기술, 함수 인자는 줄 제한이 넘지 않으면 한 줄에 기술.
    // 예시
    class Camera
    : public Sensor(), Base()
    {
    public:
      Camera();
      PointCloud get_current_data(uint8_t sensor_id);
      void calculate_data_difference(
        const PointCloud& source_data, const PointCloud& target_data);
    };

     

    - Conditionals

    • 모든 조건은 소괄호로 묶여야 함. (컴파일러에 따라서는 동작이 달라질 가능성이 있음.)
    • 연산자와 조건 소괄호 사이에는 1space 공란 필요.
    • 조건문에는 반드시 중괄호 기술이 필요.
    if (x1 == x2 && y1 == y2) // Bad
    if ((x1 == x2) && (y1 == y2)) // Good
    
    if (...)
    {
    }
    else
    {
    }

     

    - Loops & Switch Statements

    • 모든 조건은 소괄호로 묶여야 함.
    • 연산자와 조건 소괄호 사이에는 1space 공란 필요.
    • 조건문에는 반드시 중괄호 기술이 필요.
    • switch문 각 구역에 괄호 사용.
    • 열거 값에 해당하지 않는 조건이 있을 때, 반드시 switch문에 default를 사용.
    • 루프 본체가 비었을 때 continue를 사용.
    for (int i = 0; i < COUNT; ++i)
    {
      ...
    }
    
    while (condition)
    {
      ...
    }
    
    while (condition)
    {
      continue;
    }
    
    switch (image_type)
    {
      case ImageType::GRAY:
      {
        ...
        break;
      }
      case ImageType::COLOR:
      {
        ...
        break;
      }
      case default:
      {
        ...
        break;
      }
    }

     

    - Pointer & Reference Expressions

    • 포인터(*)와 참조자(&)는 변수 타입에 붙여서 기술.
    • 변수를 캐스팅 하는 경우는 변수에 붙여서 기술.
    • 멤버 접근시에 사용하는 '.', '->'에는 앞 뒤로 공란을 두지 않음.
    const auto data = my_class->get_param().data;
    const auto* data = *my_class->get_param().data;
    const auto& data = &my_class->get_param().data;
    
    int* variable;
    int& variable;
    int** variable;
    int* variable(
        constexpr int* input_data,
        constexpr int& input_param,
    )

     

    - Class

    • public, protected, private 순으로 구역을 사용하며 들여쓰기를 하지 않음.
    • 첫 구역을 제외하고 구역 사이에 수직 빈칸 하나씩 존재.
    class Lidar
    : public Sensor(), Base()
    {
    public:
      Lidar();
      PointCloud get_current_data(int sensor_id);
    
    protected:
      void calculate_data_difference(
        const PointCloud& source_data, const PointCloud& target_data);
    
    private:
      const int sensor_id = 0;
    };

     

    - Constructor Initializer Lists

    • 생성자 초기화 목록은 한 인자당 한 줄 점유.
    • 함수 정의 및 선언에서도 동일한 규칙 적용.
    Camera Camera(
      const Intrinsics& intrinsics,
      const Extrinsics& extrinsics)
    : intrinsics_(intrinsics), extrinsics_(extrinsics) {}

     

    - Horizontal Whitespace

    • 수평 빈칸은 1space를 의미.
    • 줄 맞추기를 하지 않는다. (특수한 경우 사용하지 않으며, Github에서 commit시 불필요한 code change가 발생.)
    • 주석 또한 각 들여쓰기 기준과 동일한 위치에 기술.
    • 불필요한 빈칸을 사용하지 않음.
    const double roll = 0.0; // Rotation x
    const double pitch = 0.0; // Rotation y
    const double yaw = 0.0; // Rotation z

     

    - Vertical Whitespace

    • 수평 빈칸은 빈줄을 의미.
    • 불필요한 수평 빈칸을 사용하지 않음.
    • 사전 논의되지 않은 2개 이상의 수평 빈칸을 사용하지 않음.
    • 줄 길이 제한이 있을 경우 연산자, 콤마, 괄호 이후 줄바꿈 사용 가능.
    • 줄 바꿈 이후엔 1들여쓰기를 기술(계층 구조에 따름)
    • 파일 마지막에는 반드시 EoF로 1 수평 빈칸을 기술.
    const bool condition = (x == y) &&  // 연산자 이후에 줄바꿈을 해야 한다.
      (y == z);

     

    'Clean Code' 카테고리의 다른 글

    _Uncrustify_C++  (1) 2024.05.29
    코딩 가이드 라인  (0) 2023.10.05
Designed by Tistory.