プログラミング、リファクタリング、そしてすべてにおける究極の疑問: No. 35

その他インテル® DPC++/C++ コンパイラー特集

この記事は、インテル® デベロッパー・ゾーンに公開されている「The Ultimate Question of Programming, Refactoring, and Everything」の日本語参考訳です。


35. enum へ新しい定数を追加する際は switch 演算子も修正する

Appleseed プロジェクトから抜粋した以下のコードについて考えてみます。このコードにはエラーが含まれています。PVS-Studio アナライザーは、次の診断を出力します。

V719 The switch statement does not cover all values of the ‘InputFormat’ enum: InputFormatEntity. (V719 switch 文は、’InputFormat’ 列挙型のすべての値をカバーしていません: InputFormatEntity。)

enum InputFormat
{
    InputFormatScalar,
    InputFormatSpectralReflectance,
    InputFormatSpectralIlluminance,
    InputFormatSpectralReflectanceWithAlpha,
    InputFormatSpectralIlluminanceWithAlpha,
    InputFormatEntity
};

switch (m_format)
{
  case InputFormatScalar:
    ....
  case InputFormatSpectralReflectance:
  case InputFormatSpectralIlluminance:
    ....
  case InputFormatSpectralReflectanceWithAlpha:
  case InputFormatSpectralIlluminanceWithAlpha:
    ....
}

説明

既存の列挙型 (enum) に新しい項目を追加しなければならない場合、注意が必要です。その場合、コード全体にわたって、各 switch 文や if チェーンなどで enum の参照 (例えば、上記のコード例のような状況) を確認する必要があります。

上記のコード例では、InputFormatEntity が最後に追加されていることから、この定数が InputFormat に追加されたと仮定します。通常、プログラマーは新しい定数を列挙型の最後に追加しますが、新しい定数がコード全体にわたって適切に使用されているか確認し、switch 演算子を修正することを忘れがちです。

それが、上記のコード例です。”m_format==InputFormatEntity” は全く処理されていません。

正しいコード

switch (m_format)
{
  case InputFormatScalar:
  ....
  case InputFormatSpectralReflectance:
  case InputFormatSpectralIlluminance:
  ....
  case InputFormatSpectralReflectanceWithAlpha:
  case InputFormatSpectralIlluminanceWithAlpha:
  ....
  case InputFormatEntity:
  ....
}

推奨事項

コード・リファクタリングの際に、このようなエラーを軽減する方法について考えてみます。最も簡単な方法は、次のようにメッセージを表示する “default:” を追加することです。しかし、これはあまり効果的な解決策ではありません。

switch (m_format)
{
  case InputFormatScalar:
  ....
  ....
  default:
    assert(false);
    throw "Not all variants are considered"
}

これで、m_format 変数が InputFormatEntity の場合、例外が発生します。このアプローチには、2 つの重大な欠点があります。

1. このエラーはテスト時に発生しない可能性があるため (テスト実行時に m_formatInputFormatEntity にならない場合)、エラーがリリースビルドに入ってしまい、後に顧客サイトで実行中に発生する場合があります。顧客からそのような問題の報告を受け取るのは望ましいことではありません。

2. default をエラーとする場合、列挙型のすべての可能な値に対する case を記述する必要があります。これは不便です。特に、列挙型に多くの定数が含まれる場合には特に不便です。場合によっては、異なるケースを default セクションで処理すると便利です。

この問題は、次の方法で解決することを推奨します。完璧ではありませんが、少なくとも問題を解決することができます。

enum を定義する際に、コメントを追加するようにします。キーワードと列挙名も使用できます。

例:

enum InputFormat
{
  InputFormatScalar,
  ....
  InputFormatEntity
  //If you want to add a new constant, find all ENUM:InputFormat.
};

switch (m_format) //ENUM:InputFormat
{
  ....
}

上記のコードでは、InputFormat 列挙型を変更する場合、プロジェクトのソースコードで “ENUM:InputFormat” を確認するようにコメントで指示しています。

開発チームで作業する場合は、このことをチーム全体に知らせるとともに、コーディング基準とスタイルガイドに追加します。誰かがこの規則に従わない場合、それは非常に残念なことです。

コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

タイトルとURLをコピーしました