Skip to content

[UA] Правила кодування

1therealcloud edited this page Jul 14, 2025 · 1 revision

Зміст

Загальні моменти

Ширина відступу (indentation) — 4 пробіли.

Максимальна довжина рядка — 120 символів.

Відступ завжди становить один крок ширини (4 пробіли):

ПОГАНО

    GatherInfo(expression, description, arg0, arg1, file, line,
               function, assertionInfo);

ДОБРЕ

    GatherInfo(expression, description, arg0, arg1, file, line,
        function, assertionInfo);

Відкриваючі та закриваючі дужки секції мають бути на одному рівні:

ПОГАНО

    if (lastErr==ERROR_SUCCESS) {
        *buffer = 0;
        return;
    }

ДОБРЕ

    if (lastErr==ERROR_SUCCESS)
    {
        *buffer = 0;
        return;
    }

Модифікатори доступу відступаються так само, як мітки goto — на рівні класу, без додаткового відступу:

ПОГАНО

    class ObjectComparer
    {
    public:
        ObjectComparer();
        int Compare(const Object &a, const Object &b);
        private: int InternalCompare(const Object &a, const Object &b);
    };

ДОБРЕ

    class ObjectComparer
    {
    public:
        ObjectComparer();
        int Compare(const Object &a, const Object &b);
    private:
        int InternalCompare(const Object &a, const Object &b);
    };

Вміст простору імен не повинен мати відступів:

ПОГАНО:

    namespace XRay
    {
        namespace Math
        {
            #define PI 3.14159265359
        }
    }

ДОБРЕ

    namespace XRay
    {
    namespace Math
    {
    #define PI 3.14159265359
    }
    }

Якщо рядок дуже довгий і його потрібно розбити, продовження має мати відступ у один рівень (один shift-width):

ПОГАНО

    if (lineLength>1 && (screenSize.Vertical<bufferSize.Vertical
                          || explicitLines))
    {
        printf("this is a test section that will show how to handle "
               "long lines, such as %s which is %d lines long",
               "this", 2);
        SetWindowText( hWnd,
                       "Button Name" );
    }

ДОБРЕ

    if (lineLength>1 && (screenSize.Vertical<bufferSize.Vertical
        || explicitLines))
    {
        printf("this is a test section that will show how to handle "
            "long lines, such as %s which is %d lines long", "this", 2);
        SetWindowText(hWnd, "Button Name");
    }

if/for/while оператор, який займає більше одного рядка, завжди повинен мати дужки; те саме стосується блоку then/loop, якщо він займає більше одного рядка:

ПОГАНО

    if (!ErrorAfterDialog)
        MessageBox(NULL, "Fatal error occured\n\n"
            "Press OK to abort program execution", "Fatal error",
            MB_OK|MB_ICONERROR|MB_SYSTEMMODAL);

ДОБРЕ

    if (!ErrorAfterDialog)
    {
        MessageBox(NULL, "Fatal error occured\n\n"
            "Press OK to abort program execution", "Fatal error",
            MB_OK|MB_ICONERROR|MB_SYSTEMMODAL);
    }

Прототипи функцій мають бути відформатовані так само, як і виклики функцій:

ПОГАНО

    void Debug::Backend(const char *reason,
                        const char *expression,
                        const char *arg0,
                        const char *arg1,
                        const char *file,
                        int line,
                        const char *function,
                        bool &ignoreAlways);
    {
        // function code
    }

ПОГАНО

    void Debug::Backend(const char *reason, const char *expression, const
        char *arg0, const char *arg1, const char *file, int line, const char *
        function, bool &ignoreAlways);

ДОБРЕ

    // declaration
    static void Backend(const char *reason, const char *expression,
        const char *arg0, const char *arg1, const char *file, int line,
        const char *function, bool &ignoreAlways);

    // implementation
    void Debug::Backend(const char *reason, const char *expression,
        const char *arg0, const char *arg1, const char *file, int line,
        const char *function, bool &ignoreAlways)
    {
        // function code
    }

    // if you need to comment parameters
    void Debug::Backend(
        const char *reason, // error reason
        const char *expression, // failed expression
        const char *arg0, // first argument
        const char *arg1, // second argument
        const char *file,
        int line,
        const char *function,
        bool &ignoreAlways) // ignore errors of this type
    {
        // function code
    }

Оператори switch повинні мати case на одному рівні відступу, без пробілу перед двокрапкою :.

Якщо case займає один рядок, після двокрапки повинен бути один пробіл:

ПОГАНО

    switch (vendor)
    {
        case Vendor::Intel :
            ...
        case Vendor::AMD :
            ...
    }

ДОБРЕ

    switch (vendor)
    {
    case Vendor::Intel:
        ...
    case Vendor::AMD:
        ...
    }

ДОБРЕ

    switch (controlId)
    {
    case IDC_TOGGLESTATS:
        app->drawStats = !app->drawStats;
        break;
    case IDC_RESETCAMERA:
        app->ResetCamera();
        break;
    case IDC_LIGHT_SIZE:
    {
        auto r = hud->GetSlider(IDC_LIGHT_SIZE)->GetValue()*0.01f;
        app->spotLightObj->SetLightRadius(r);
        break;
    }
    }

ДОБРЕ

    switch (vendorId)
    {
    case 0x756e6547: vendor = Vendor::Intel; break;
    case 0x68747541: vendor = Vendor::AMD; break;
    default: vendor = Vendor::Unknown; break;
    }

Немає пробілу між ім’ям функції та відкриваючою дужкою, немає пробілу між відкриваючою дужкою та першим параметром, після коми — один пробіл:

ПОГАНО

    Msg ("hello %s\n", "world");
    Msg( "hello world\n" );
    Msg("hello world\n","world");

ДОБРЕ

    Msg("hello world\n", "world");

Один пробіл після зарезервованих слів перед відкриваючою дужкою:

ПОГАНО

    if(OnDialog)

ДОБРЕ

    if (OnDialog)

Частина then в операторі if повинна бути на окремому рядку. Причина: це дозволяє налагоджувати if і бачити, коли він виконується.

ПОГАНО

    if (OnDialog) OnDialog(true);

ДОБРЕ

    if (OnDialog)
        OnDialog(true);

Оператори else if повинні бути на тому ж рівні відступу, що й початковий if.

Причина: це схоже на структуру оператора switch.

ПОГАНО

    if (!strcmp(argv[1],"--help")) PrintUsage();
    else if (!strcmp(argv[1], "--run")) {
        RunApplication();
        PrintResults();
    } else PrintError();

ДОБРЕ:

    if (!strcmp(argv[1], "--help"))
        PrintUsage();
    else if (!strcmp(argv[1], "--run"))
    {
        RunApplication();
        PrintResults();
    }
    else
        PrintError();

Оператори return не повинні містити дужок і не повинні мати пробіл після них:

ПОГАНО

    return (0);

ДОБРЕ

    return 0;

ПОГАНО

    return ;

ДОБРЕ

    return;

Не використовуй зайві дужки в виразах:

ПОГАНО

    if ((a!=b) || (c!=d))
        ...

ДОБРЕ

    if (a!=b || c!=d)
        ...

Не виконуй виклик return наприкінці функції, що повертає void:

ПОГАНО

    void foo()
    {
        bar();
        return;
    }

ДОБРЕ

    void foo()
    {
        bar();
    }

Не розділяй секції коду більше ніж одним порожнім рядком.

Оголошення класів/структур/енумів та ініціалізації повинні відкривати { на наступному рядку:

ПОГАНО

    struct Size {
        int Width;
        int Height;
    };

ДОБРЕ

    struct Size
    {
        int Width;
        int Height;
    };

ДОБРЕ (no members)

    struct Dummy {};

ПОГАНО

    Size size = {
        LayoutHelper::AdjustLength(Clamp(length, 0, 1024)),
        LayoutHelper::CalculatePreferredHeight(length, scaleFactor.y)
    };

ДОБРЕ

    Size size =
    {
        LayoutHelper::AdjustLength(Clamp(length, 0, 1024)),
        LayoutHelper::CalculatePreferredHeight(length, scaleFactor.y)
    };

ДОБРЕ (ініціалізація поміщається в один рядок)

    Size size = {1024, 16};

Віддавай перевагу коментарям у стилі C++ (//), а не стилю C (/* ... */).

ДОБРЕ

    /* draw overlay statistics */
    DrawStats();

ДОБРЕ (better)

    // draw overlay statistics
    DrawStats();

Коментарі мають бути вирівняні по коду, до якого вони відносяться, або розташовані на один пробіл після кінця рядка.

ПОГАНО

    /*
     * draw overlay statistics
     */
    DrawStats();

ПОГАНО

    //
    // draw overlay statistics
    //
    DrawStats();

ДОБРЕ

    /* draw overlay statistics */
    DrawStats();

ДОБРЕ (better)

    // draw overlay statistics
    DrawStats();

ДОБРЕ

    DrawStats(); // draw overlay statistics

Коментарі, що займають більше одного рядка, мають дотримуватися наступних правил:

ПОГАНО

    //
    // last render stage: draw overlay engine performance statistics
    // (input, render, sound, ai, physics)
    //
    DrawStats();

ДОБРЕ

    // last render stage: draw overlay engine performance statistics
    // (input, render, sound, ai, physics)
    DrawStats();

Здоровий глузд повинен допомагати при виборі між циклами for або while.

ПОГАНО

    int i = 0;
    while (i<10)
    {
         i++;
         ...
    }

ДОБРЕ

    for (int i = 0; i<10; i++)
        ...

ПОГАНО

    for (ptr = ptr->next; ptr; ptr = ptr->next)
        ...

ДОБРЕ

    while (ptr = ptr->next, ptr)
       ...

У циклах for та while без тіла оператор повинен закінчуватися роздільником операторів (;) на тому ж рядку.

ПОГАНО

   for(i = 0; Test(i); i++)
       ;

ДОБРЕ

   for (i = 0; Test(i); i++);

Ставте один пропуск перед і після оператора присвоєння.

ПОГАНО

    int a=0;
    x= x+1;
    capacity*=2;

ДОБРЕ

    int a = 0;
    x = x+1;
    capacity *= 2;

Не ставте пробіл перед роздільником операторів (;), ставте один пробіл після нього.

ПОГАНО

    for (i = 0 ;i<10 ;i--, j *= 2) ;

ДОБРЕ

    for (i = 0; i<10; i--, j *= 2);

Не ставте пробіл після операторів інкременту (++) та декременту (--). Інкремент ставте після значення, а не перед ним.

ПОГАНО

    i --;
    ++j;

ДОБРЕ

    i--;
    j++;

Тривіальні вирази з операторами >, >=, <, <=, ==, != не повинні мати пробілів навколо них.

Приклади: a, array[i].var[j], sizeof(Foo), x+1

Вказівники (->) не вважаються тривіальними виразами для операторів > >= < <=, бо важко читати x->y>z, тоді як x->y==z читати легше.

ПОГАНО

    if (x > 5)

ДОБРЕ

    if (x>5)

ПОГАНО

    if (x+1>5)

ДОБРЕ

    if (x+1 > 5)

ПОГАНО

    if (f(x, y) > g(y, z))

ДОБРЕ

    if (f(x, y)>g(y, z))

ПОГАНО

    if (x->y == 5)

ДОБРЕ

    if (x->y==5)

ПОГАНО

    if (x->y>=5)

ДОБРЕ

    if (x->y >= 5)

Не збільшуй, не присвоюй і не змінюй значення змінної всередині виклику функції:

ПОГАНО

    CalcXY(i++, 2);

ПОГАНО

    SetX(i = 3);

ДОБРЕ (if i is not a global)

    CalcXY(i, 2);
    i++;

ДОБРЕ (if i is a global that CalcXY() uses)

    i++;
    CalcXY(i-1, 2);

ДОБРЕ

    if (i++)
        a[i++] = 4;

ДОБРЕ (if i is not a global)

    i = 3;
    SetX(i);

У циклах for, коли є функція, яка отримує наступний елемент, її слід викликати один раз (в умові кроку):

ПОГАНО:

    for (int i = 0, ch = GetChar(); ch=='\r'; ch = GetChar(), i++)
        HandleResult(ch);

ДОБРЕ:

    for (int i = 0; (ch = GetChar())=='\r'; i++)
        HandleResult(ch);

При присвоєнні у логічному виразі (if/for/while) використовуйте наступний шаблон, щоб уникнути попереджень компілятора:

ПОГАНО:

    for (int i = 0; ch = GetChar(); i++)
        HandleResult(ch);
    if (x = ComputeNum())
        return x;

ДОБРЕ:

    for (int i = 0; ch = GetChar(), ch; i++)
        HandleResult(ch);
    if (x = ComputeNum(), x)
        return x;

Під час ініціалізації рядка та встановлення нульового термінатора потрібно використовувати 0:

ПОГАНО:

    string[0] = '\0';
    if (string[3]=='\0')
    {
    }

ДОБРЕ:

    *string = 0;
    if (!string[3])
    {
    }

Іменування (Naming conventions)

Правила іменування:

  • Обирайте легко читабельні імена ідентифікаторів. Наприклад, змінна horizontalAlignment зрозуміліша, ніж alignmentHorizontal.

  • Не використовуйте угорську нотацію, крім наступних випадків:

    • Код, пов’язаний з UI, наприклад: btnOpen, lblMessage, chkRecursive, cbGameType
    • Інтерфейсні класи: IClient, IGameLevel, IGamePersistent тощо
    • Типи шаблонів: TBase, TResult

Стилі написання імен:

  • класи/структури/перерахування: PascalCase

    • Математичні примітиви можуть бути у нижньому регістрі: vector2f, angle3f, matrix44f
    • Використання псевдонімів типів (using declarations) теж у нижньому регістрі: uint, int16, uint64
  • Функції: PascalCase

  • Публічні та захищені поля: PascalCase

    • Математичні примітиви можуть використовувати lowercase: vector2f.x, angle3f.yaw, matrix44f.m03
  • Приватні поля: camelCase

  • Локальні змінні: camelCase

  • Глобальні змінні: PascalCase

  • Іменування просторів імен (namespaces): PascalCase

Не додавайте ключове слово inline для функцій, визначених у класі — такі функції автоматично є inline згідно зі стандартом.

При визначенні інтерфейсного класу деструктор слід оголосити чисто віртуальним (pure virtual) і додати його пусту inline-реалізацію після визначення класу:

ДОБРЕ

    class IServer
    {
    public:
        virtual ~IServer() = 0;
    };
    
    inline IServer::~IServer() {}

Не позначайте інтерфейсні класи атрибутами лінкування.

При перевизначенні віртуальної функції використовуйте специфікатор override у її декларації.

ПОГАНО

    class Server : public IServer
    {
        ...
        virtual void OnClientConnected(IClient *client);
        ...
    };

ДОБРЕ

    class Server : public IServer
    {
        ...
        virtual void OnClientConnected(IClient *client) override;
        ...
    };

Перевірка значень, що повертаються функціями: Функції, які повертають bool або вказівник, будуть перевірятися без явного порівняння — достатньо перевірити, чи повернули вони true або false.

ПОГАНО

    if (strstr(args, " -h")!=nullptr)
        PrintHelp();

ДОБРЕ

    if (strstr(args, " -h"))
        PrintHelp();

ПОГАНО

    if (IsUpdateRequired()==true)
        Update();

ДОБРЕ

    if (IsUpdateRequired())
        Update();

ПОГАНО

    cl = GetClient(id);
    if (cl==nullptr)
        return nullptr;

ДОБРЕ

    cl = GetClient(id);
    if (!cl)
        return nullptr;

ПОГАНО

    cl = GetClient(id);
    if (cl!=nullptr)
        Disconnect(cl);

ДОБРЕ

    cl = GetClient(id);
    if (cl)
        Disconnect(cl);

При виділенні структури на стеку використовуйте нульове ініціалізування замість memset:

ПОГАНО

    ShaderParams params;
    memset(&params, sizeof(params), 0);

ДОБРЕ

    ShaderParams params = {};

За можливості, надавайте перевагу inline-функціям замість макросів:

ПОГАНО

    #define RAD2DEG(angle) ((angle)*180/PI)

ДОБРЕ

    template <typename T>
    T RadiansToDegrees(T angle) { return angle*180/PI; }

Назви макросів зазвичай повинні бути у ВЕРХНЬОМУ РЕЄСТРІ, з таким самим форматуванням пробілів, як і в іншому коді:

ПОГАНО

    #define RAD2DEG( angle ) ((angle)*180/PI)
    #define RAD2DEG(angle) \
        ((angle) * 180 / PI)

ДОБРЕ

    #define RAD2DEG(angle) ((angle)*180/PI)

ДОБРЕ

    #define RAD2DEG(angle) \
        ((angle)*180/PI)

Звичайні макроси, які містять більше одного виразу, повинні бути загорнуті в конструкцію do...while(false). Це дозволяє безпечно використовувати їх всередині if...else конструкцій.

Приклад використання:

    if (condition)
        DO_A_AND_B;
    else
        DoSomethingElse();

ПОГАНО

    #define DO_THIS_AND_THAT() \
    { \
        DoThis(); \
        DoThat(); \
    }

ДОБРЕ

    #define DO_THIS_AND_THAT() \
    do { \
        DoThis(); \
        DoThat(); \
    } while (false)

Не використовуйте форвардні оголошення (forward declarations), якщо це не є необхідним. Замість цього змініть порядок оголошень так, щоб викликаюча функція знаходилась нижче за ту, яку вона викликає (callee).

ПОГАНО

    void Callee();
    void Caller()
    {
        Callee();
    }
    void Callee()
    {
    }

ДОБРЕ

    void Callee()
    {
    }
    void Caller()
    {
        Callee();
    }

Віддавайте перевагу переносимим типам даних із Common.hpp замість типів, специфічних для операційної системи.

ПОГАНО

    BYTE b;
    DWORD w;

ДОБРЕ

    byte b;
    uint32 w;

Header guards

На початку файлу додайте захист від повторного включення. Не використовуйте include guards. Натомість використовуйте #pragma once — це менш схильне до помилок і потребує менше коду для написання. Хоча це не частина стандарту, #pragma once добре підтримується більшістю компіляторів.

ПОГАНО

    #ifndef _XRAY_HPP_
    #define _XRAY_HPP_
    
    // The XRay.hpp content comes here
    
    #endif // _XRAY_HPP_

ДОБРЕ

    #pragma once

    // The XRay.hpp content comes here

Кінець файлу

Файли повинні завершуватися порожнім рядком:

ПОГАНО

    int main()
    {
        return 0;
    }<EOF>

ДОБРЕ

    int main()
    {
        return 0;
    }
    <EOF>

Умовна компіляція та препроцесорні директиви

СКЛАДНІ секції з #if повинні відповідати наступним рекомендаціям:

ПОГАНО

    #ifndef NO_SINGLE
        Msg("* Found new patch: %s", versionName);
        #ifdef DOWNLOAD_UPDATES
            #ifdef DOWNLOAD_UPDATES_GATHER_STATS
        DownloadStats stats;
        DownloadUpdate(downloadUrl, stats);
        stats.Dump();
            #else
        DownloadUpdate(downloadUrl);
            #endif
        #endif
    #endif

ПОГАНО

    #ifndef NO_SINGLE
        Msg("* Found new patch: %s", versionName);
    # ifdef DOWNLOAD_UPDATES
    #  ifdef DOWNLOAD_UPDATES_GATHER_STATS
        DownloadStats stats;
        DownloadUpdate(downloadUrl, stats);
        stats.Dump();
    #  else
        DownloadUpdate(downloadUrl);
    #  endif
    # endif
    #endif

ДОБРЕ

    #ifndef NO_SINGLE
        Msg("* Found new patch: %s", versionName);
    #ifdef DOWNLOAD_UPDATES
    #ifdef DOWNLOAD_UPDATES_GATHER_STATS
        DownloadStats stats;
        DownloadUpdate(downloadUrl, stats);
        stats.Dump();
    #else
        DownloadUpdate(downloadUrl);
    #endif
    #endif
    #endif

Прості секції з #if не повинні відступатися:

ДОБРЕ

    #ifndef DEBUG
    #ifdef _DEBUG
    #define DEBUG
    #endif
    #ifdef MIXED
    #define DEBUG
    #endif
    #endif

ПОГАНО

    #ifndef DEBUG
        #ifdef _DEBUG
            #define DEBUG
        #endif
        #ifdef MIXED
            #define DEBUG
        #endif
    #endif

ДОБРЕ

    void Sleep(int milliseconds)
    {
    #ifdef WINDOWS
        ::Sleep(milliseconds);
    #else
        ::sleep(milliseconds);
    #endif
    }

ПОГАНО

    void Sleep(int milliseconds)
    {
        #ifdef WINDOWS
            ::Sleep(milliseconds);
        #else
            ::sleep(milliseconds);
        #endif
    }

Після #endif можна додати коментар, який вказує, до якого #if він належить, якщо між #if та відповідним #endif є великий проміжок.

ПОГАНО

    #ifdef _EDITOR
        VerifyPath(path);
    #endif // _EDITOR

ДОБРЕ

    #ifdef _EDITOR
        VerifyPath(path);
    #endif

ДОБРЕ

    #ifdef _EDITOR
        // Lots of editor related code, that you really have to scroll down
        // to see all of it.
    #endif // _EDITOR

Використовуйте макрос defined() лише тоді, коли у вас складний вираз. Якщо вам потрібно застосовувати defined() для більш ніж одного прапорця, спробуйте об’єднати ці прапорці під одним новим прапорцем.

ПОГАНО

    #ifndef DEDICATED_SERVER
    #ifdef CONFIG_SHOW_LOGO_WINDOW
        DestroyWindow(logoWindow);
    #endif
    #endif

ПОГАНО

    #if defined(CONFIG_SHOW_LOGO_WINDOW)
        DestroyWindow(logoWindow);
    #endif

ДОБРЕ

    #ifdef CONFIG_SHOW_LOGO_WINDOW
        DestroyWindow(logoWindow);
    #endif

ДОБРЕ (since you really need both flags)

    #if !defined(DEDICATED_SERVER) && defined(CONFIG_SHOW_LOGO_WINDOW)
        DestroyWindow(logoWindow);
    #endif

Використовуйте using замість typedef:

ПОГАНО

    typedef void (*FunctionType)(double);
    typedef Vector3<float> vector3f;

ДОБРЕ

    using FunctionType = void (*)(double);
    using vector3f = Vector3<float>;

Використовуйте строго типізовані enum замість звичайних C enum:

ПОГАНО

    enum CmdStatus
    {
        CmdStatusOk,
        CmdStatusInProgress,
        CmdStatusFailed,
    };

ДОБРЕ

    enum class CmdStatus
    {
        Ok,
        InProgress,
        Failed,
    };

Перелічення (enum), які можуть бути серіалізовані, повинні мати призначені значення:

ПОГАНО

    enum class CmdStatus
    {
        Ok = 1,
        InProgress,
        Failed,
    };

ДОБРЕ

    enum class CmdStatus
    {
        Ok = 1,
        InProgress = 2,
        Failed = 3,
    };

Кожен рядок у enum повинен закінчуватися комою — це робить enum розширюваним. Комою слід не відокремлювати тільки останній рядок, якщо передбачається, що він ніколи не буде перевищений.

ПОГАНО

    enum class CmdType
    {
        Reset = 1,
        Load = 2,
        Save = 3
    };

ДОБРЕ

    enum class CmdType
    {
        Reset = 1,
        Load = 2,
        Save = 3,
    };

ДОБРЕ

    enum class CmdType
    {
        Reset = 1,
        Load = 2,
        Save = 3,
        Invalid = 0xFF
    };

Розташовуйте вказівник (*) та посилання (&) поруч з ідентифікатором (іменем змінної):

ПОГАНО

    IClient* GetServerClient();
    void BanAddress(const IPAddress& ip);

ДОБРЕ

    IClient *GetServerClient();
    void BanAddress(const IPAddress &ip);

Розміщуйте static/const/volatile/mutable перед типом:

ПОГАНО

    int const static z;
    bool volatile exit;
    int static Log(char const *s);
    bool mutable lastState;
    char const* const* name;

ДОБРЕ

    static const int z;
    volatile bool exit;
    static int Log(char const *s);
    mutable bool lastState;
    const char *const *name;

Використовуйте виділення в стеку для локальних змінних з константним розміром. Динамічне виділення застосовуйте лише за потреби.

ПОГАНО

    ConnectionParams *cp = new ConnectionParams();
    ...
    delete cp;

ДОБРЕ

    ConnectionParams cp;

Оператор delete також обробляє nullptr, тому немає потреби використовувати if () перед ним:

ПОГАНО (if str can be null):

    if (str)
        delete str;

ДОБРЕ:

    delete str;

Константні рядки, що тривають більше ніж на один рядок, повинні закриватися лапками в кінці кожного рядка і відкриватися знову на наступному рядку. Пробіли між словами повинні залишатися в кінці рядка.

ПОГАНО

    Log("Hello world, this is a long string that we want to print and 
is more than 120 chars long so we need to split it");

ПОГАНО

    Log("Hello world, this is a long string that we want to print"
        " and is more than 120 chars long so we need to split it");

ДОБРЕ

    Log("Hello world, this is a long string that we want to print "
        "and is more than 120 chars long so we need to split it");

Функції, які не приймають жодних параметрів, слід визначати без використання void:

ПОГАНО

    IClient *GetServerClient(void);

ДОБРЕ

    IClient *GetServerClient();

Тривіальні вирази з операторами +, -, *, / не повинні мати пробілів навколо них:

ПОГАНО

    Foo(a + b);

ДОБРЕ

    Foo(a+b);

ПОГАНО

    Foo(2 * (a + b));

ДОБРЕ

    Foo(2*(a+b));

ПОГАНО

    Foo((a + b) / (1000 * 1000));

ДОБРЕ

    Foo((a+b)/(1000*1000));

ПОГАНО

    Foo((Min(aMin, bMin)+Max(aMax, bMax))*(width+Pow(a, p))/(1000*1000));

ДОБРЕ

    Foo((Min(aMin, bMin)+Max(aMax, bMax)) * (width+Pow(a, p)) / (1000*1000));

Не вставляйте порожні рядки між кодом всередині функцій. Якщо хочете розділити фрагменти коду — використовуйте рядок з коментарем.

ПОГАНО

    for (auto &item : items)
        storage.push_back(item);
    
    if (!storage.size())
        Log("! ERROR: No items found");

ДОБРЕ

    for (auto &item : items)
        storage.push_back(item);
    // check if there's no items
    if (!storage.size())
        Log("! ERROR: No items found");

Іменування файлів та обов’язковий вміст

Клас Foo має бути у файлах src/Bar/Foo.cpp і Foo.hpp.

Усі файли повинні строго відповідати цьому прикладу.

Якщо не використовуються препроцесорні заголовки (precompiled headers), то в *.cpp файлі першою має бути директива #include "Config.hpp", щоб конфігурація функцій впливала на всі інші файли. Якщо ж використовуються препроцесорні заголовки, #include "Config.hpp" має йти відразу після них (наприклад, після stdafx.hpp).

Заголовочні файли мають бути самодостатніми, тобто їх можна скомпілювати без залежності від інших включень.

Тому #include "Foo.hpp" має бути одразу після Config.hpp, щоб переконатися, що Foo.hpp компілюється окремо без залежності від інших включень перед ним.

ДОБРЕ

Foo.hpp шаблон

    #pragma once
    #include "Config.hpp"
    // ... Put here minimal includes required by Foo.hpp ...
    
    // ... Put here your declarations ...

Foo.hpp шаблон

    #include "Config.hpp"
    #include "Foo.hpp"
    // ... Put here minimal includes required by Foo.cpp ...
    
    // ... Put here your code ...

ДОБРЕ (використання попередньо скомпільованих заголовків)

stdafx.hpp шаблон

    #pragma once
    #include "Config.hpp"
    // ... Put here additional includes ...

Foo.hpp шаблон

    #pragma once
    #include "Config.hpp"
    // ... Put here minimal includes required by Foo.hpp ...
    
    // ... Put here your declarations ...

Foo.cpp шаблон

    #include "stdafx.hpp"
    #include "Config.hpp"
    #include "Foo.hpp"
    // ... Помістіть тут мінімальні include-файли, необхідні для Foo.cpp ...
    
    // ... Помістіть сюди свій код ...

Використовуйте #include "Foo.hpp" для хедерів у тій же директорії, що й вихідний файл.

Використовуйте #include "PathToFoo/Foo.hpp" для хедерів в інших директоріях, де PathToFoo — шлях відносно кореневої директорії движка.

Використовуйте #include <Foo.hpp> для зовнішніх системних файлів.

ПОГАНО

    #include <Config.hpp>
    #include <xrCore/xrCore.hpp>
    #include "IReader.hpp"
    #include "algorithm"

ДОБРЕ

    #include "Config.hpp"
    #include "xrCore.hpp" // we are in src/xrCore directory
    #include "IO/IReader.hpp" // from src/xrCore/IO/IReader.hpp
    #include <algorithm>

Вільна функція, що використовується в одному CPP-файлі: має бути реалізована як static, а оголошення у CPP-файлі додається лише, якщо функція використовується до реалізації.

Вільна функція, що використовується поза CPP-файлом (глобальна): має бути оголошена у HPP-файлі.

Клас/структура, що використовується в одному CPP-файлі: має бути оголошена всередині CPP-файлу.

Якщо потрібен вказівник поза CPP-файлом: у HPP-файлі має бути форвардне оголошення, а визначення — у C-файлі.

Якщо потрібні члени класу/структури поза CPP-файлом: оголосити їх у HPP-файлі.

Коментарі XXX повинні бути у форматі <keywords>: <коментар>. Якщо декілька назв — розділяти їх символом '/':

ПОГАНО

XXX: dima: optimize this case
XXX alexmx: RENDER check if already loaded
XXX alexmx: oles: move to common

ДОБРЕ

XXX: optimize this case
XXX dima: optimize this case
XXX alexmx RENDER: check if already loaded
XXX alexmx/oles: move to common
Clone this wiki locally