58

私は現在大きなプロジェクトに取り組んでおり、警備員を含むすべてを維持することは私を夢中にさせます!手で書くのは時間の無駄です。多くの編集者はインクルードガードを生成できますが、これはあまり役に立ちません。

  1. エディタは、ファイル名に基づいてガードシンボルを生成します。この問題は、異なるディレクトリに同じファイル名のヘッダーがある場合に発生します。それらの両方が同じインクルードガードを取得します。ガードシンボルにディレクトリ構造を含めるには、マクロのスラッシュとバックスラッシュが最適ではないため、エディタからの凝ったアプローチが必要になります。

  2. ファイルの名前を変更する必要がある場合は、すべてのインクルードガードの名前も変更する必要があります(ifndefで、define、理想的にはendifのコメント)。迷惑。

  3. プリプロセッサは、それらが何を意味するのか見当がつかないまま、大量のシンボルで溢れています。

  4. それにもかかわらず、定義は一度含まれますが、コンパイラーはヘッダーが含まれるたびにヘッダーを開きます。

  5. インクルードガードは名前空間にもテンプレートにも適合しません。実際、それらは名前空間を破壊しています!

  6. ガードシンボルが一意にならない可能性があります。

たぶん、プログラムが単一のディレクトリに1000未満のヘッダーを含んでいたとき、それらは許容できる解決策でした。しかし、最近は?それは古く、現代のコーディング習慣とは何の関係もありません。私が最も気になるのは、この問題は#pragmaonceディレクティブによってほぼ完全に解決できるということです。なぜそれは標準ではないのですか?

4

9 に答える 9

54

のようなディレクティブ#pragma onceは、明確な利点がある完全に移植可能な方法で定義するのは簡単ではありません。質問を提起する概念のいくつかは、をサポートするすべてのシステムで明確に定義されているわけではなくC、単純な方法で定義しても、従来のインクルードガードに勝る利点はない可能性があります。

コンパイルが検出された場合#pragma once、その内容が再び含まれないように、このファイルをどのように識別する必要がありますか?

明白な答えは、システム上のファイルの一意の場所です。システムにすべてのファイルに対して一意の場所があるが、多くのシステムがリンク(シンボリックリンクとハードリンク)を提供している場合、これは問題ありません。つまり、「ファイル」には一意の場所がありません。別の名前で見つかったという理由だけでファイルを再含める必要がありますか?おそらくそうではありません。

しかし、今問題があります#pragma once。すべてのプラットフォームで正確な意味を持つ方法での動作をどのように定義することができますか-シンボリックリンクはもちろん、ディレクトリさえも持っていないものでも-システムで望ましい動作を得るにはどうすればよいですか?それはそれらを持っていますか?

ファイルIDはその内容によって決定されると言えます。したがって、含まれているファイルにがあり、まったく#pragma once同じ内容のファイルが含まれている場合、2番目以降のsは効果がありません。#include

これは定義が簡単で、セマンティクスが明確に定義されています。また、プロジェクトがファイルシステムリンクをサポートおよび使用するシステムからサポートしないシステムに移動された場合でも、同じように動作するなどの優れたプロパティがあります。

欠点として、その内容を含むインクルードファイルが検出されるたびに、これまでにすでにインクルードされている#pragma onceファイルを使用して、他のすべてのファイルと照合する必要があります。#pragma onceこれ#includeは、どのような場合でもガードを使用するのと同様のパフォーマンスヒットを意味し、コンパイラの作成者に取るに足らない負担を追加します。明らかに、この結果はキャッシュできますが、従来のインクルードガードについても同じことが言えます。

従来のインクルードガードは、プログラマーにインクルードファイルの一意の識別子であるマクロを選択するように強制しますが、少なくとも動作は明確に定義されており、実装が簡単です。

潜在的な落とし穴とコスト、および従来のインクルードガードが機能するという事実を考えると、標準化委員会が標準化の必要性を感じなかったことは私にとって驚くべきことではありません#pragma once

于 2009-11-08T12:07:47.930 に答える
19

インクルードガードは間違いなく煩わしいものであり、Cは元々、ヘッダーがデフォルトで1回インクルードされるように設計されている必要があります。ヘッダーを複数回インクルードするには、特別なオプションが必要です。

しかし、そうではありませんでした。ほとんどの場合、インクルードガードを使用する必要があります。とは言う#pragma onceものの、かなり広くサポートされているので、それを使用することで逃げることができるかもしれません。

個人的には、インクルードガードにGUIDを追加することで、最初の問題(同様の名前のインクルードファイル)を解決します。それは醜いです、そしてほとんどの人はそれを嫌います(それで私はしばしばそれを仕事で使わないことを余儀なくされます)、しかしあなたの経験はそれが恐ろしく醜いとしても-アイデアがいくらかの価値を持っていることを示しています一種のハック-どうして完全に独り占めしませんか?):

#ifndef C_ASSERT_H_3803b949_b422_4377_8713_ce606f29d546
#define C_ASSERT_H_3803b949_b422_4377_8713_ce606f29d546

// blah blah blah...

#endif

コンパイラは、インクルードガードを含むヘッダーファイルを実際に再度開くことはないと聞いています(イディオムを認識することを学びました)。それが本当かどうか(またはどの程度本当か)はわかりません。私はそれを測定したことがありません。私も心配していませんが、私のプロジェクトはそれほど大きくないので問題になります。

私のGUIDハックは、アイテム1、5、および6をほぼ解決します。私はアイテム2、3、および4を使用しています。実際、アイテム2の場合、GUIDによってファイルの名前を変更するときに、インクルードガードマクロの名前を変更せずに使用できます。それはユニークなままです。実際、ファイル名をGUIDに組み込む理由はまったくありません。しかし、私はそうします-伝統だと思います。

于 2009-11-08T09:05:01.840 に答える
14
  1. このプロジェクトにインクルードファイルを追加する必要がある頻度はどれくらいですか。DIRNAME_FILENAMEをガードに追加するのは本当に難しいですか?そして、常にGUIDがあります。
  2. 本当に頻繁にファイルの名前を変更しますか?これまで?また、GUARDを#endifに入れることは、他の役に立たないコメントと同じように迷惑です。
  3. ガードが定義する1000個のヘッダーファイルが、システムライブラリ(特にWindows)によって生成される定義の数のほんのわずかな割合でさえあるとは思えません。
  4. MSC 10 for DOS(20年以上前)は、どのヘッダーが含まれているかを追跡しており、それらに含まれている場合、ガードが再び含まれている場合はそれらをスキップすると思います。これは古い技術です。
  5. 名前空間とテンプレートは、ヘッダーにまたがってはなりません。親愛なる、あなたがこれをすることを私に言わないでください:

    template <typename foo>
    class bar {
    #include "bar_impl.h"
    };
    
  6. あなたはすでにそれを言いました。

于 2009-11-08T09:22:41.197 に答える
9

すでに述べたように、C ++標準はさまざまな開発プラットフォームを考慮に入れる必要があり、その一部には制限があり、#pragmaを一度サポートすると実装できなくなる可能性があります。

一方、スレッドのサポートは以前と同様の理由で追加されませんでしたが、新しいC++標準にはそれでもスレッドが含まれています。後者の場合、非常に限られたプラットフォーム用にクロスコンパイルすることができますが、開発は本格的なプラットフォームで行われます。GCCはこの拡張機能をサポートしているので、あなたの質問に対する本当の答えは、この機能をC++標準にプッシュすることに関係者がいないということだと思います。

実用的な観点から、警備員を含めると、#pragmaonceディレクティブの違反よりもチームに多くの問題が発生しました。たとえば、ファイルが複製され、後で両方のコピーが含まれる場合、インクルードガードのGUIDは役に立ちません。#pragmaのみを使用すると、重複した定義エラーが発生し、ソースコードの統合に時間を費やす可能性があります。ただし、インクルードガードの場合、問題をキャッチするために実行時テストが必要になる場合があります。たとえば、関数パラメーターのデフォルト引数のコピーが異なる場合に発生します。

インクルードガードの使用は避けます。#pragmaをサポートせずにコードをコンパイラに移植する必要がある場合は、すべてのヘッダーファイルにインクルードガードを追加するスクリプトを作成します。

于 2011-08-08T07:52:53.003 に答える
4

この問題は、異なるディレクトリに同じファイル名のヘッダーがある場合に発生します。

ice_cream_maker.hつまり、プロジェクトで両方とも呼び出される2つのヘッダーがあり、どちらにice_cream_makerも同じ機能を実行する定義済みのクラスがありますか?または、システム内のすべてのクラスを呼び出していますfooか?

それにもかかわらず、定義は一度含まれますが、コンパイラーはヘッダーが含まれるたびにヘッダーを開きます。

ヘッダーを複数回含めないようにコードを編集します。

(ライブラリのメインヘッダーではなく)依存ヘッダーの場合、私はよく次のようなヘッダーガードを使用します。

#ifdef FOO_BAR_BAZ_H
#error foo_bar_baz.h multiply included
#else
#define FOO_BAR_BAZ_H

// header body

#endif
于 2009-11-08T09:55:56.080 に答える
3

IIRC、#pragma何でも言語の一部ではありません。そして、それは実際にたくさん現れます。

(編集:「この時代」の最も明白な弱点の1つであるため、包含およびリンクシステムが新しい標準の焦点となるはずだったことに完全に同意します)

于 2009-11-08T10:23:14.177 に答える
2

ファイルに含まれるクラスと名前空間の名前を含むようにインクルードガードを設定することで、ランダムな文字列を使用せずに名前の衝突を回避できる可能性があります。

その上、#pragmaはかつてMSコンパイラとGCCの両方でかなり長い間サポートされていましたが、なぜISO標準に準拠していないのが気になるのでしょうか。

于 2009-11-08T09:10:37.010 に答える
2

実用的な解決策:

1)一貫したガード命名ポリシーを選択します(たとえば、プロジェクトルートからの相対パス+ファイル名、またはその他の選択したもの)。サードパーティのコードで発生する可能性のある例外を含めます。

2)ソースツリーを再帰的にウォークし、ガードがすべてポリシーに準拠していることを確認するプログラムを作成します(単純なPythonスクリプトで実行できます)。また、ガードが間違っている場合は、ユーザーが簡単に修正に適用できるdiff(またはsedスクリプトなど)を出力します。または、確認を求めて、同じプログラムから変更を加えます。

3)プロジェクトの全員にそれを使用させます(たとえば、ソース管理に提出する前に)。

于 2009-11-08T10:44:11.207 に答える
1

特別なプラグマのみで複数のインクルードを許可し、デフォルトで複数のインクルードを禁止する正しい方法は、次のように思います。

#pragma allow_multiple_include_this_file

だからあなたが理由を尋ねたので。標準の開発者に提案を送信しましたか?:)私も送りません。理由はありますか?

于 2012-01-13T04:28:46.793 に答える