cpp-common-code/readme/cpp-coding-conv.md

6.3 KiB
Raw Permalink Blame History

C++ コーディング規約

1. 一般的なガイドライン

  1. モダン C++C++17 以上) を前提とする。
  2. 生ポインタより、まず std::unique_ptr / std::shared_ptr / std::vector など RAII を優先する。
  3. 例外を使う場合は、例外 or エラーコードのどちらかに統一する。
  4. new / delete は基本的に直接使わず、スマートポインタやコンテナを使う。
  5. ヘッダ/ソース分割は *.h / *.cpp(または *.hpp / *.cpp)で行う。
  6. 自動整形には clang-format を利用する。

2. コメント/ドキュメント

2.1 ファイルコメント

// user_service.h - ユーザー関連のユースケースを提供するクラス群
#pragma once

// ...

2.2 クラスコメントDoxygen 推奨)

/**
 * @brief ユーザーを管理するサービスクラス。
 *
 * ユーザーの作成、取得、削除などの機能を提供する。
 */
class UserService {
public:
    /// コンストラクタ
    explicit UserService(UserRepository& repo);

    /// ユーザーを作成する
    User createUser(const std::string& name, int age);

private:
    UserRepository& repo_;
};

2.3 メソッドコメント

/**
 * @brief ユーザーをIDで取得する。
 *
 * @throws std::runtime_error ユーザーが存在しない場合。
 */
User UserService::getUserById(int id);

3. 命名規則

  1. クラス名/構造体名: CamelCase

    class UserService;
    struct HttpRequest;
    
  2. メソッド・関数名: camelCase または snake_case(プロジェクト内で統一)

    void addUser(const User& user);
    int  find_user(const std::string& name);  // プロジェクト方針次第
    
  3. メンバ変数:

    • プレフィックスやサフィックスを付けるスタイルが多い 例: snake_case_ / m_name / _name など
    class User {
    public:
        explicit User(std::string name, int age)
            : name_(std::move(name)), age_(age) {}
    
    private:
        std::string name_;
        int         age_;
    };
    
  4. 定数:

    • グローバル定数には k プレフィックスを使う場合も多い。
    constexpr int kMaxUserCount = 1000;
    
  5. 名前空間: すべて小文字でプロジェクト名などを使う。

    namespace myapp::user {
    // ...
    }
    

4. インクルードガイドライン

  1. ヘッダでのインクルードは 最小限 にする。前方宣言で済むなら前方宣言を使う。

    // user_service.h
    #pragma once
    
    #include <string>
    
    namespace myapp {
    
    class UserRepository;  // 前方宣言
    
    class UserService {
    public:
        explicit UserService(UserRepository& repo);
        // ...
    private:
        UserRepository& repo_;
    };
    
    } // namespace myapp
    
  2. ソースファイルでは対応するヘッダをまずインクルードし、その後に標準/外部ライブラリ。

    // user_service.cpp
    #include "user_service.h"
    
    #include "user_repository.h"
    
    #include <stdexcept>
    
  3. インクルード順は以下を推奨:

    1. 自分のヘッダ
    2. 同じモジュールのヘッダ
    3. 標準ライブラリ
    4. サードパーティヘッダ

5. エラーハンドリング

5.1 例外を使う場合

  1. ロジック上の失敗には throw で例外を投げ、呼び出し側で try/catch

    User UserService::getUserById(int id)
    {
        auto user = repo_.findById(id);
        if (!user) {
            throw std::runtime_error("User not found");
        }
        return *user;
    }
    
  2. 例外の種類は、std::runtime_error / std::invalid_argument などを使い分ける。

  3. ライブラリ側では「例外を投げる API」と「エラーコードを返す API」を明確に分ける。

5.2 例外を使わない場合

  1. 戻り値に std::optional<T>expected パターン(独自実装)を使う。

    std::optional<User> UserService::tryGetUserById(int id)
    {
        return repo_.findById(id);
    }
    

6. 複数ファイル間の分割(ヘッダ / ソース)

6.1 例: User と UserService

include/
  user.h
  user_service.h
src/
  user.cpp
  user_service.cpp
  main.cpp

user.h

#pragma once

#include <string>

namespace myapp {

class User {
public:
    User(int id, std::string name);

    int              id() const;
    const std::string& name() const;

private:
    int         id_;
    std::string name_;
};

} // namespace myapp

user.cpp

#include "user.h"

namespace myapp {

User::User(int id, std::string name)
    : id_(id), name_(std::move(name))
{
}

int User::id() const
{
    return id_;
}

const std::string& User::name() const
{
    return name_;
}

} // namespace myapp

user_service.h

#pragma once

#include <memory>

#include "user.h"

namespace myapp {

class UserRepository;

class UserService {
public:
    explicit UserService(UserRepository& repo);

    User createUser(const std::string& name, int age);

private:
    UserRepository& repo_;
};

} // namespace myapp

user_service.cpp

#include "user_service.h"

#include "user_repository.h"

namespace myapp {

UserService::UserService(UserRepository& repo)
    : repo_(repo)
{
}

User UserService::createUser(const std::string& name, int age)
{
    // 仮実装: ID採番して保存するなど
    int newId = repo_.nextId();
    User user(newId, name);
    repo_.save(user);
    return user;
}

} // namespace myapp

7. clang-format / clang-tidy / VSCode 設定

7.1 .clang-formatC と共有してもOK

BasedOnStyle: LLVM
IndentWidth: 4
UseTab: Never
ColumnLimit: 100
AllowShortIfStatementsOnASingleLine: false
BreakBeforeBraces: Attach
NamespaceIndentation: All

7.2 VSCode 設定例

{
  "[cpp]": {
    "editor.defaultFormatter": "ms-vscode.cpptools",
    "editor.formatOnSave": true
  },
  "C_Cpp.clang_format_style": "file",

  // clang-tidy を使う場合
  "C_Cpp.codeAnalysis.clangTidy.enabled": true,

  "files.trimTrailingWhitespace": true,
  "files.insertFinalNewline": true
}