cpp-common-code/readme/develop.md

7.6 KiB
Raw Blame History

C/C++の開発環境

Linter

ライブラリの候補

  • clang-tidy
    • 精度はかなり高い
    • C++向け(Cはカバー範囲が狭い)
  • cppcheck
    • C/C++ 両方対応・軽量
    • 導入が簡単にできる

導入方法

Macの場合

brew install cppcheck llvm
# パスの追加が必要
export PATH="/opt/homebrew/opt/llvm/bin:$PATH"
clang-tidy --version
cppcheck --version
  • llvm: clang-tidy は llvm に含まれています

Linux(Ubuntu)の場合

sudo apt install cppcheck clang-tidy
clang-tidy --version
cppcheck --version

Dockerを活用する

Dockerfileはこちら

Dockerイメージをビルドする

docker build -f Dockerfile.devtools -t c-dev-tools .
# docker run --rm  -v "$(pwd)":/work -w /work  c-dev-tools <コマンド>

Linterの使い方

clang-tidy の使い方(C++向け)

単一ファイルをチェックする例

clang-tidy src/cpp/hello.cpp -- -Iinclude -std=c++17
# Docker
docker run --rm  -v "$(pwd)":/work -w /work clang-tidy src/cpp/hello.cpp -- -Iinclude -std=c++17
  • --以降がコンパイラフラグ(g++に渡すものと同じ)
  • -Iinclude : ヘッダファイルのパス
  • -std=c++17 : 使用している C++ のバージョン

チェック内容を絞る使い方

clang-tidy src/cpp/hello.cpp \
  -checks='modernize-*,bugprone-*,readability-*' \
  -- -Iinclude -std=c++17

よく使うチェックカテゴリ:

  • bugprone-* : バグにつながるコード
  • modernize-* : モダンC++スタイルへの改善提案
  • readability-* : 可読性の改善

プロジェクト全体

CMake + compile_commands.jsonがある場合に以下の内容でできます (clang-tidy は「コンパイル情報」がないと正しく解析できない) compile_commands.jsonについては別途解説します。

mkdir -p build
cd build
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..

# プロジェクト全体に対して実行
run-clang-tidy -p build

ccppcheck の使い方C / C++ 両対応)

cppcheckはコンパイル不要で実行できる静的解析ツールです。 CでもC++でも同じコマンドで使えます。

基本的な使い方

cppcheck src/
# Docker
docker run --rm  -v "$(pwd)":/work -w /work  c-dev-tools cppcheck src/

チェックレベルを広げる(おすすめ)

cppcheck --enable=warning,style,performance,portability src/
# Docker
docker run --rm  -v "$(pwd)":/work -w /work c-dev-tools \
cppcheck --enable=warning,style,performance,portability src/
  • warning : 危険なコードやバグの可能性
  • style : コーディングスタイル上の問題
  • performance : 非効率なコード
  • portability : 環境依存のコード

テスト

テストフレームワークの選定

  • C
    • Unity:
      • 最軽量・C専用
      • C言語の組込み開発でもよく使われる
      • シングルヘッダに近い軽量テストツール
      • doctest / googletest の C版のような位置付け
    • CMocka: Unity より“しっかりしたテスト”をしたいときに最適
    • Criterion: C言語でも“近代的なテスト開発”をしたい場合に良い
  • C++
    • GoogleTest:
      • C++テストで最も使われる
      • 機能が豊富モック、パラメトリック、DeathTestなど
    • Catch2: シンプルで読みやすいテストを作りたい人向け
    • doctest: 個人開発や高速実行が必要なプロジェクトに最適

テスト環境を構築する

# unity.c,unity.h,unity_internals.h
git clone https://github.com/ThrowTheSwitch/Unity /opt/unity
  • Alpine(musl libc)ではGTestのABIが合わず、gtest が正常にビルドできないケースが多い
  • doctestとCatch2は普通にビルドで使えます

Dockerfileはこちら

テスト用のDockerイメージをビルドする

docker build -f Dockerfile.testtools -t c-test-tools .

Doc(ドキュメンテーション)

C / C++ の自動ドキュメンテーションをしたい場合、 Doxygen(Graphviz)が圧倒的スタンダードで、最もおすすめです。

  • C / C++ / Java / Python などメジャー言語に対応
  • コメントを解析して HTML / PDF / Markdown などへ出力
  • 関数・構造体・クラスの関係図UML・依存グラフを自動生成
  • C++ テンプレートや名前空間にも対応
  • Graphvizを入れれば図が自動で描かれる

環境構築

Macの場合

brew install doxygen graphviz

Linux(Ubuntu)の場合

sudo apt install doxygen graphviz

Dockerを活用する

Dockerfileはこちら

Dockerイメージをビルドする

docker build -f Dockerfile.devtools -t c-dev-tools .

Linuxでコマンドを実行する

mkdir -p docs
# docker run --rm  -v "$(pwd)":/work -w /work/docs  c-dev-tools <コマンド>
# 初期化コマンド
docker run --rm  -v "$(pwd)":/work -w /work/docs  c-dev-tools doxygen -g
# ドキュメントを生成する
docker run --rm  -v "$(pwd)":/work -w /work/docs  c-dev-tools doxygen Doxyfile

doxygen 使い方

設定ファイルを生成

doxygen -g

Doxyfileが作成される

ドキュメント生成

doxygen Doxyfile
  • html/ に HTML ドキュメント
  • latex/ に PDF 用のファイルが出力される(必要なら make)

CI / CDによる実行

Git Actionsで生成の場合

- name: Generate Doxygen docs
  run: doxygen docs/Doxyfile

- name: Deploy to GitHub Pages
  uses: peaceiris/actions-gh-pages@v3
  with:
    github_token: ${{ secrets.GITHUB_TOKEN }}
    publish_dir: ./docs/html

Doxyfileの設定

docs/Doxyfileを設定する

  • PROJECT_NAMEを実際のプロジェクト名に変更する
# ドキュメント化したいディレクトリ
INPUT = ../src ../include

# INPUT 内を再帰的に探索
RECURSIVE = YES

# C向け出力最適化
OPTIMIZE_OUTPUT_FOR_C = YES
# 全シンボル抽出
EXTRACT_ALL = YES

Docコメントの書き方

C言語の場合

/**
 * @brief  二数の最大値を返す関数
 * @param  a  整数
 * @param  b  整数
 * @return 最大値
 */
int max(int a, int b);

C++言語の場合

/**
 * @class User
 * @brief ユーザー情報を扱うクラス
 */
class User {
public:
    /**
     * @brief コンストラクタ
     */
    User(std::string name, int age);

    /**
     * @brief 名前を取得する
     */
    std::string getName() const;