ブログ 約6分

オープンソースプロジェクトから学ぶ基本的なCのコーディングルール

Share this article
オープンソースプロジェクトから学ぶ基本的なCのコーディングルール

どのプロジェクトにも独自のスタイルガイド、つまりそのプロジェクトでコードを書く方法に関する一連の規約があります。基本的なコーディングルールを選ぶ管理者もいれば、非常に高度なルールを好む管理者もいます。多くのプロジェクトではコーディングルールがまったくなく、各開発者が独自のスタイルを使っています。

すべてのソースコードが一貫したスタイルで書かれていると、大規模なコードベースをはるかに理解しやすくなります。

採用すべき最適なコーディングルールを論じた資料はたくさんあります。良いコーディング習慣は、次の方法で学べます。

  • 書籍や雑誌を読む。
  • Webサイト。
  • 同僚から学ぶ。
  • トレーニングコースを受講する。

もう1つの、より興味深い方法は、よく知られた成熟したオープンソースプロジェクトを研究し、その開発者がどうコードを書いているかを見ることです。「C」の世界では、 Linuxカーネル が良い候補になるでしょう。

初心者はもちろん、中級のC開発者にとっても、Linuxカーネルにいきなり飛び込むのは簡単ではないかもしれません。しかし、目的は必ずしもそのソースコードに貢献することではなく、どう実装されているかを探索することです。

例として、Linuxソースコードから関数の実装を1つ見てみましょう。

linux11

コードは非常にクリーンに見えます。実際、この関数は次の特徴を持っています。

  • コード行数が少ない。
  • シグネチャが明確に定義されている。
  • コメントが適切に付いている。
  • 適切にインデントされている。
  • 変数名が非常に分かりやすい。

同じ関数を、別の開発者が次のように実装する可能性もあります。

linux12

コーディングスタイルは、ソースコードの可読性に大きな影響を与えます。開発者のトレーニングに数時間を投資し、定期的なコードレビューを実施することで、コードを保守しやすく進化させやすくできます。

それでは、 CppDepend を使ってLinuxカーネルのソースコード内部を見て、開発者が採用している基本的なコーディングルールをいくつか探ってみましょう。

モジュール性

モジュール性とは、ソフトウェアが独立した部分で構成される度合いを高めるソフトウェア設計手法であり、モジュール化されたコードを管理・保守しやすくします。

Cのような手続き型言語では、名前空間、コンポーネント、クラスなどの論理的な構文が存在しないため、ディレクトリとファイルを使ってモジュール性を実現できます。

考えられるシナリオは次のとおりです。

  • すべてのソースファイルを1つのディレクトリに置く。
  • モジュールまたはサブモジュールに関連するファイルを特定のディレクトリに分離する。

Linuxカーネルの場合、ディレクトリとサブディレクトリを使ってカーネルのソースコードがモジュール化されています。

linux15

カプセル化

カプセル化とは、実装内部の関数とデータを隠蔽することです。Cでは、static キーワードを使ってカプセル化を行います。これらのエンティティは、ファイルスコープの関数と変数と呼ばれます。

次のCQLinqクエリを実行して、すべての static 関数を検索してみましょう。

linux17

Metric View を使うと、対象となる関数の数を適切に把握できます。Metric View では、コードベースはツリーマップとして表現されます。Treemapping は、入れ子になった長方形を使ってツリー構造のデータを表示する手法です。CppDependのツリーマップで使われるツリー構造は、通常のコード階層です。

  • プロジェクトはディレクトリを含む。
  • ディレクトリはファイルを含む。
  • ファイルは構造体、関数、変数を含む。

ツリーマップビューは、CQLinqクエリの結果を表現する便利な方法であり、影響を受けるコード要素を視覚的に確認できます。

linux2

ご覧のとおり、多くの関数が static として宣言されています。

次に、static フィールドを検索してみましょう。

linux3

変数についても同じことが言えます。多くが static として宣言されています。

Linuxカーネルのソースコードでは、関数と変数をファイルスコープに限定する必要がある場合に、常にカプセル化が使われています。

データモデルは構造体に格納する

Cプログラミングでは、関数は変数を使って処理を実行します。これらの変数には次のようなものがあります。

  • 静的変数。
  • グローバル変数。
  • ローカル変数。
  • 構造体の変数。

どのプロジェクトにもデータモデルがあり、それは多くのソースファイルから使用される可能性があります。グローバル変数を使うことも1つの解決策ですが、良い方法ではありません。データを構造体にまとめる方が望ましい方法です。

プリミティブ型のグローバル変数を検索してみましょう。

linux4

該当する変数はごくわずかで、その一部は(elfcorehdr_addr と elfcorehdr_size)や(pm_freezing と pm_nosig_freezing)のように構造体へまとめられるかもしれません。

関数は短く簡潔に保つ

以下は、 LinuxコーディングスタイルのWebページからの、関数の長さに関するアドバイスです。

Functions should be short and sweet, and do just one thing.  They should
fit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24,
as we all know), and do one thing and do that well.

The maximum length of a function is inversely proportional to the
complexity and indentation level of that function.  So, if you have a
conceptually simple function that is just one long (but simple)
case-statement, where you have to do lots of small things for a lot of
different cases, it's OK to have a longer function.

コード行数が30行を超える関数を検索してみましょう。

linux14

30行を超えるコードを持つ関数はごくわずかです。

関数のパラメータ数

NbParameters > 8 の関数は呼び出しにくく、パフォーマンスを低下させる可能性があります。別の方法は、引数渡しを処理する専用の構造体を用意することです。

linux7

8個を超えるパラメータを持つ関数は2個だけです。

ローカル変数の数

NbVariables が8を超える関数は、理解と保守が難しくなります。NbVariables が15を超える関数は非常に複雑であり、より小さな関数に分割すべきです(ツールによって自動生成された場合を除きます)。

linux9

ローカル変数が15個を超える関数は5個だけです。

複雑な関数を定義しない

複雑な関数を検出するメトリクスは多数あります。NBLinesOfCode、パラメータ数、ローカル変数数は基本的なものです。

複雑な関数を検出するための、ほかにも興味深いメトリクスがあります。

  • 循環的複雑度は広く使われている手続き型ソフトウェアメトリクスで、プロシージャ内の判定パス数を測定します。
  • ネスト深度は関数に対して定義されるメトリクスで、関数本体内で入れ子になったスコープの最大深度を表します。
  • 最大ネストループは、関数内のループネストの最大レベルに等しくなります。

これらのメトリクスで許容される最大値はチームの判断に依存し、普遍的な標準はありません。

リファクタリングの候補となる関数を検索してみましょう。

linux8

複雑と見なせる関数はごくわずかです。

命名規則

命名規則に普遍的な標準はなく、各プロジェクトが最適なものを選べます。ただし、選んだ規則に一貫して従うことが非常に重要です。

たとえばLinuxの場合、構造体は小文字で始める必要があります。カーネルソースコード全体でそれが守られているかを確認できます。次のクエリを実行してみましょう。

linux5

小文字ではなく「_」で始まる構造体は4個だけです。

インデント

インデントは、コードを読みやすくするうえで非常に有用です。以下は、 LinuxコーディングスタイルのWebページからの、インデントの背景にある考え方です。

Rationale: The whole idea behind indentation is to clearly define where
a block of control starts and ends.  Especially when you've been looking
at your screen for 20 straight hours, you'll find it a lot easier to see
how the indentation works if you have large indentations.

Now, some people will claim that having 8-character indentations makes
the code move too far to the right, and makes it hard to read on a
80-character terminal screen.  The answer to that is that if you need
more than 3 levels of indentation, you're screwed anyway, and should fix
your program.

まとめ

有名なオープンソースプロジェクトを探索することは、常にプログラミングスキルを高める良い方法です。プロジェクトをダウンロードしてビルドする必要はありません。たとえばGitHub上でコードを探索するだけで十分です。

Share this article