組込み C / C++ プロジェクトの初期の全体感を何とかする

この記事は Akerun Advent Calendar 2022 - Qiita の 17 日目の記事です.

ファームウェアグループの @tarotene です.「組」と「込」の間の「み」はつけない派です 1.昨年も 2 本ほど記事を書いておりました:

akerun.hateblo.jp

akerun.hateblo.jp

今年は技術的な学びも沢山あり,その中からピックアップしてちょっとお硬いテーマにしてみました.せっかくなのでお付き合いください.

TL; DR

一般に Github などのホスティングサービスで新しいコードベース(プロジェクト)を立ち上げる際に検討しておくと良さそうに関して,組込み C / C++ 特有の知見がこの 1 年で溜まったので年末の大掃除がてら共有します.基本的にはプロジェクトレイアウトを大きなスケールでは他人任せにして,一段浅いところではちゃんと考えようねというお話と,ビルドシステムは積極的に使おうという話の 2 階建てで,既存の知見を自分なりの観点で整理してみました.パッケージマネージャの話もちょっと補足してあります.

「C / C++」という括り方に関して

自分で書いていて雑だと思ったので予防線としての断り書きです.ここでいう C++ は C 由来のオブジェクトと相互にリンク可能なオブジェクトを自分自身も生成できる言語,という意味合いです.本来の C++ の機能的サブセットとして捉えて下さい.

「組込み」の指すスコープ

組込みという言葉は相変わらずだいぶフワッとしていると思います.

本記事では「(典型的に)サーバ PC 上で動くような Web アプリケーションとは性質を異にするソフトウェアならびにプラットフォーム全般」としてざっくりと定義してしまいます 2

本題: 自由なプロジェクトレイアウトと柔軟なビルドシステム

そもそも,C / C++ がどこで必要になるかを考えると,筆者の思いつく範囲だと概ね

  • 組込み向けアプリケーション開発(RTOS 含む)
  • PC 向け OS の開発
  • 軽量言語のランタイムの開発

とその周辺に絞られるように見えます(学習・教育・研究目的を除く).その中でも(市場の大きさから)組込み向けアプリケーション開発がボリュームゾーンになるという認識を持っています.

こうした領域ではリアルタイム要求の帰結として CPU やメモリを(仮想化を一切介さずに)直接利用できることが前提のことも多く,近年では Rust による置き換えの実施例も増えてきていますが,C / C++ を完全に置き換えにかかるのはまだまだ難しそうな印象があります 3

しかし,C / C++ はそれなりに長い歴史を持つ言語なので,モダンな開発言語が必ず備えているであろうエコシステムの一部―フォーマッタ,テストフレームワーク,プロジェクトテンプレート―をサードパーティに委ねざるを得ません.特に実務において問題になりがちで,ともすると開発において不便さを認識しづらいのがプロジェクトテンプレートの機能であると筆者は考えます 4

例えば,フォーマッタや静的解析の機能は近年だと LLVM ベースの clang-format や clang-tidy がデファクトスタンダードとして受け入れられつつあり,テストフレームワークに関しては GoogleTest がそれに相当します.一方で,プロジェクトテンプレートに関しては様々な提案がなされてきたものの未決着であるという印象です.

こうした状況に一石を投じたのが Pitch-fork layout です:

api.csswg.org

Pitch-fork layout は,既存の C / C++ プロジェクトを網羅的に調査する中で得られた気付きの蒸留であるという点でも興味深いものです 5

詳細は類書に譲りますが,C / C++ では(インクルードやマクロなど # から始まるプリプロセッサ命令を全て消化した後の)翻訳単位と呼ばれるソースファイルを個別にオブジェクトファイルに変換(コンパイル)し,それらをリンクすることでより大きなオブジェクトファイルや実行形式を作るのが通例です.したがって,任意の(ビルド可能な)C / C++ のプロジェクトにおいて,下記の 2 つが機械的に定義されます:

  • (プロジェクトの)ディレクトリ構造 =: プロジェクトレイアウト
  • (プロジェクトの)翻訳単位の集合

一方,ソフトウェア設計においてはこうした機械的な内部境界とは別に(抽象度が高めな)モジュール分割というものをよく考えると思います.ここで言うモジュール分割というのは,要するにヒトへの認知負荷の軽減されたドキュメントレベルのアーキテクチャのことです.

ここで,「ディレクトリ構造もしくは翻訳単位の集合は常にモジュール分割の良い表現たり得るか」という問題が自然と提起されます.なぜなら,異なる複数の設計情報を脳内に留めながらソフトウェア開発を行うのはそれ自体が大きな認知負荷であり,万難を排して避けたいことだからです.

Pitch-fork layout は,あるべきプロジェクトテンプレートの形を提示することで,こうした問題を部分的に解決してくれます.詳細は本家を眺めてみて下さい.

一方で,プロジェクトテンプレートの形を決めるのが開発の最初期の話であるとすると,開発の中期くらいまでは大規模なリファクタリングの一環でディレクトリ構造を転換させたくなる場面が多く発生します.つまり,プロジェクトとしては動的ディレクトリ構造を受け入れざるを得ません.しかし,こうした動的なディレクトリ構造を受け入れる仕事は,基本的に CMake のようなビルドシステムに任せてしまえば良いと考えます:

cmake.org

つまり,結論としてはざっくりと

  • プロジェクトレイアウトに関しては大規模なところと細かい命名規則は一旦 Pitch-fork layout で形だけ整えてしまおう
  • Pitch-fork layout で吸収しきれない動的なプロジェクトの構造変化は CMake で吸収してあげよう

ということになります.

こうした(プロダクトの価値とは本来独立な)技術的意思決定を一部肩代わりしてくれる言語経験者で C / C++ の大規模なプロジェクト(e.g., 組込み開発)に改めて参入予定の方の参考になれば幸いです.

閑話休題: C / C++ 用パッケージマネージャ conan の話

CMake には,開発環境の OS や CPU アーキテクチャを自動的に判別して起動させる C / C++ コンパイラを明示的に切り替えたり,組込み開発で必須とされるクロスコンパイラを指定する機能が搭載されています:

こうした機能は使いこなせば使いこなすほど利便性が身に沁みて分かるものですが,とはいえ複雑は複雑です.

また,CMake はそれ自体がプロダクションコードと同等の品質で書かれるべきという話 6 もあり,やたら滅多に CMakeLists.txt の改訂を行うのも避けたいです:

更に,例えば組込み Linux の開発では開発環境の PC 向けに動作させたいテストビルド(開発用 PC が x86_64 なら x86_64)とターゲットデバイス向けビルド(ターゲットが armhf なら armhf)を分けたいといったニーズがしばしば発生します.しかも上手くやれるなら,アプリケーション層は一切取り替えずに,低レイヤのドライバだけ(互換性がないので)ごっそり取り替えたい.そうしたニーズを世の中のソフトウェアエンジニアはどうやって解決しているのでしょうか.要するにパッケージマネージャはないんでしょうか.

残念ながら,プロジェクトテンプレートのケースと同様,C / C++ 向けのパッケージマネージャに純正品は存在しません.しかし,ある程度広く受け入れられた便利なサードパーティは存在します.それが conan です:

conan.io

C / C++ 向けのパッケージマネージャには,他にも vcpkg といったモノもあり,conan がデファクトスタンダードかどうかは難しい問題ですが,conan を初手で検討するのは悪くない判断だと筆者は考えています 7

考察〜結論

CMake や conan のような「動的な何かを吸収するエコシステム」が存在しない世界線を考えてみましょう.

仮に CMake がなければ,Makefile 直書きプロジェクトで Makefile を頻繁に変更することを強いられるでしょう.こうしたことは Makefile をバージョン管理せず独自に持つという危険なインセンティブに繋がります.

また,例えば conan のようなパッケージマネージャがなければ,開発者はおそらく git submodule を積極的に利用して外部ライブラリを導入するでしょう:

git-scm.com

しかし,git submodule もまた頻繁な変更を躊躇ってしまう対象です 8

要するにこれらはプロジェクト全体の変更コストを安くするための仕組みと言えます.しかし,導入することでプロジェクトのメンテナンス対象となるレイヤーが 1 つ多くなることにも気をつける必要があります.こうした事例は何でもトレードオフで,開発の初期に決めるべきことでもあるので,より多くの事例を知った上で普段からよく悩んでおくことがいざという時に最適な意思決定に導いてくれると筆者は信じています.


株式会社フォトシンスでは、一緒にプロダクトを成長させる様々なレイヤのエンジニアを募集しています。 hrmos.co

Akerun Proにご興味のある方はこちらから akerun.com


  1. 付ける派の人の記事はこちらです.
  2. 少なくとも記事の趣旨としてカバーしたいのが組込み Linux 環境と(RTOS 含む)ベアメタル環境の 2 つであるという意図です.
  3. 長年に渡って蓄積されてきた開発資産の利用や既存のエコシステムとの共存を考えると
  4. プロジェクトテンプレートというより,プロジェクトレイアウトの方が用語としては適切かも知れない.テンプレートは,何らかのレイアウトの鋳型というニュアンスである.
  5. 調査の経緯は提案者のブログ記事 vector-of-bool.github.io に詳しく書かれています:
  6. https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1 を参照.
  7. このあたりは usagi.hatenablog.jp に詳しくまとまっています.
  8. submodule は git 本来のよく用いられる機能に比して表立って取り扱われないことが多く,ある操作(例えば submodule add)に対してその逆操作がアトミックなワンライナーで行えなかったりする点が不便である.