ブログ 約5分

Herb Sutter のC++安全性への行動喚起は、もうすぐ実現するのか?

Share this article
Herb Sutter のC++安全性への行動喚起は、もうすぐ実現するのか?

最近、Herb SutterがC++の安全性に関する優れた 記事 を書きました。彼は多数のアイデアを議論しましたが、中期的にC++の安全性を強化するために何ができるかについての彼の視点の要約をお伝えします。

C++では、デフォルトで以下を強制…(A) 新規/更新コード向けのソリューション(コード変更が必要な場合あり — リンク/バイナリ変更なし)(B) 既存コード向けのソリューション(再コンパイルのみ必要 — 手動コード変更なし、リンク/バイナリ変更なし)
型安全性本質的に安全でないすべてのキャストと変換を禁止安全な代替がある安全でないキャストと変換を安全な動作にする
境界安全性ポインタ演算を禁止 チェックされないイテレータ演算を禁止許可されたすべてのイテレータ演算で境界内をチェック すべての添字操作で境界内をチェック
初期化安全性すべての変数の初期化を必須にする(宣言時、または初回使用前)
ライフタイム安全性多くの一般的なポインタ/イテレータのライフタイムエラーを静的に診断すべてのポインタ参照でnot-nullをチェック
未定義動作の削減既知のUB/バグケースを静的に診断し、再コンパイルだけで既存コードの実際のバグをエラーにし、誤検出ゼロ:
数学的に無効な比較チェーンを禁止
(UB Annexレビューからの追加ケース)
既知のUB/バグケースを自動的に修正し、再コンパイルだけで既存コードの現在のバグを実際に正しくし、誤検出ゼロ:
数学的に有効な比較チェーンを定義
C&を返すCの代入演算子にデフォルトでreturn *this;
(UB Annexレビューからの追加ケース)

しかし、この目標を達成するために現在利用可能なオプションは何でしょうか?

型安全性

C++で型安全性を強制するには、厳密な型チェックやその他の安全機能を有効にするさまざまなフラグを使った以下のコンパイルコマンドを使用できます:

g++ -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wshadow -Werror  -o output_file source_file.cpp

これらのフラグの機能の内訳は次のとおりです:

  • -Wall:一般的な警告をすべて有効にします。
  • -Wextra:追加の警告を有効にします。
  • -Wpedantic:厳密なISO C++準拠を強制します。
  • -Wconversion:値を変更する可能性のある暗黙の変換について警告します。
  • -Wsign-conversion:符号付きと符号なしの型間の暗黙の変換について警告します。
  • -Wshadow:変数宣言が外側のスコープの宣言を隠す場合に警告します。
  • -Werror:すべての警告をエラーとして扱い、警告がある場合はコンパイルを停止します。

境界安全性

C++で境界安全性を強制するには、 -fsanitize=bounds オプションを gcc または clangで使用できます。このフラグは、範囲外の配列アクセスの実行時チェックを有効にします。サンプルのコンパイルコマンドは次のとおりです:

g++ -fsanitize=bounds -o my_program my_program.cpp

このコマンドは my_program.cpp を境界安全性チェックを有効にしてコンパイルし、 my_programという名前の実行ファイルを生成します。実行中に範囲外アクセスが発生した場合、サニタイザーがそれを報告します。

初期化安全性

C++で初期化安全性を強制するには、未初期化変数やその他の関連問題を検出するのに役立ついくつかのコンパイラフラグを使用できます。GCCとClangのコマンドは次のとおりです:

GCC
g++ -Wall -Wextra -Wuninitialized -Wmaybe-uninitialized -o your_program your_program.cpp
Clang
clang++ -Wall -Wextra -Wuninitialized -o your_program your_program.cpp
説明
  • -Wall:一般的に使用される警告メッセージをすべて有効にします。
  • -Wextra-Wall
  • -Wuninitialized:未初期化変数について警告します。
  • -Wmaybe-uninitialized:未初期化の可能性のある変数について警告します。

ライフタイム安全性

C++でライフタイム安全性を強制するには、特定のコンパイラフラグを使用し、ライフタイム解析と安全性チェックを支援するように設計された特定の機能やツールを有効にする必要があります。最近の開発では、ClangまたはGCCコンパイラの -fsanitize=address フラグがライフタイムの問題の検出に役立ちます。さらに、より包括的なチェックには、Clangの静的アナライザーやVisual StudioのMicrosoftツールを使いたいかもしれません。

GCCとClangで使用できるコマンドは次のとおりです:

GCC
g++ -fsanitize=address -fno-omit-frame-pointer -g -o your_program your_program.cpp
Clang
clang++ -fsanitize=address -fno-omit-frame-pointer -g -o your_program your_program.cpp

これらのコマンドはAddressSanitizerを有効にし、use-after-free、use-after-return、use-after-scopeの問題を含むさまざまなメモリエラーの検出を支援します。これらはライフタイム安全性の強制に不可欠です。

未定義動作

C++の未定義動作の安全性を強制するには、さまざまなコンパイラフラグやツールを使用できます。未定義動作の検出と防止に役立つ一般的なフラグを含む g++ (GNU Compiler Collectionの一部)を使ったコンパイルコマンドは次のとおりです:

g++ -Wall -Wextra -Werror -pedantic -fsanitize=address,undefined -fstack-protector-all -O2 -g your_file.cpp -o your_program

使用されるフラグの内訳は次のとおりです:

  • -Wall:一般的に使用される警告メッセージをすべて有効にします。
  • -Wextra-Wall
  • -Werror:すべての警告をエラーとして扱い、修正を強制します。
  • -pedantic:厳密なISO C++準拠を強制します。
  • -fsanitize=address,undefined:AddressSanitizerとUndefinedBehaviorSanitizerを有効にし、実行時にメモリエラーと未定義動作を検出します。
  • -fstack-protector-all:スタックバッファオーバーフローを検出するスタック保護を追加します。
  • -O2:最適化(レベル2)を有効にします。パフォーマンスとデバッグの良いバランスです。
  • -g:デバッガ(例: gdb)で使用するためのデバッグ情報をバイナリに含めます。

C++サニタイザーの問題

ご覧のとおり、サニタイザーはHerb Sutterが報告した問題の一部に対処できます。しかし、いくつかの欠点もあります:

1- パフォーマンスオーバーヘッド

  • 実行時パフォーマンス:サニタイザーは大きな実行時オーバーヘッドをもたらします。例えばAddressSanitizerは、プログラムの実行を2〜3倍遅くする可能性があります。
  • メモリ使用量:サニタイザー、特にAddressSanitizerは、メモリ使用量を大幅に増加させます。これはメモリ制約のある環境で問題になる可能性があります。

2- 互換性の問題

  • プラットフォームサポート:すべてのサニタイザーがすべてのプラットフォームやコンパイラで利用できるわけではありません。これはクロスプラットフォームプロジェクトでの使用を制限する可能性があります。
  • サードパーティライブラリ:サニタイゼーションを考慮せずにコンパイルされたサードパーティライブラリでサニタイザーを使用すると、互換性の問題や誤ったエラーが発生する可能性があります。

3- ビルドと実行の複雑さ

  • ビルドプロセスの複雑さ:サニタイザーをビルドプロセスに統合すると、特に複数のビルドタイプ(リリースとデバッグなど)を扱う場合、ビルド設定がより複雑になる可能性があります。
  • 特別な実行環境:サニタイザー下でのテスト実行には、多くの場合、特別な実行環境と追加のセットアップが必要で、プロセスがより煩雑になります。

4- 限られたスコープ

  • 特定の種類のバグ:サニタイザーは特定の種類のバグ(メモリエラーや未定義動作など)を検出するように設計されており、ロジックエラー、アルゴリズムの非効率性、その他の種類のバグは検出できない可能性があります。

5- デバッグの複雑さ

  • 詳細なレポート:サニタイザーからの詳細なレポートは役立ちますが、特に大規模で複雑なコードベースでは、圧倒されたり解釈が難しかったりすることがあります。
  • 再現性:サニタイザーが報告する一部の問題は非決定的で再現が難しい場合があり、デバッグプロセスを複雑にします。

C++のパフォーマンスを損なうことなく安全性を向上させる追加のメカニズムが必要なのは明らかです。この目標はC++委員会にとって優先度の高い関心事となっており、この緊急の問題に対する決定的な解決策が近々得られることを願っています。

Share this article