web-dev-qa-db-ja.com

C ++#includeセマンティクス

これは、同じ前処理命令に対する複数の質問です。

1-<>または ""?

MSDNにある情報とは別に:

#includeディレクティブ(C-C++)

1.a:2つの表記の違いは何ですか?
1.b:すべてのコンパイラーはそれらを同じ方法で実装しますか?
1.c:いつ<>を使用し、いつ「」を使用しますか(つまり、ヘッダーインクルードにどちらか一方を使用するために使用する基準は何ですか)。

2-#include {TheProject/TheHeader.hpp}または{TheHeader.hpp}?

私は自分のプロジェクトヘッダーを含む少なくとも2つの書き方を見てきました。少なくとも4種類のヘッダーがあることを考えると、次のようになります。

  • プロジェクトのプライベートヘッダー?
  • プロジェクトのヘッダーですが、シンボルをエクスポートしています(したがって「パブリック」)
  • モジュールがリンクしている別のプロジェクトのヘッダー
  • コンパイラまたは標準ライブラリのヘッダー

ヘッダーの種類ごとに:

2.a:<>または ""のどちらを使用しますか?
2.b:{TheProject/TheHeader.hpp}に含めるか、{TheHeader.hpp}のみに含めるか。

3-ボーナス

3.a:ツリーのような組織内のソースやヘッダー(つまり、「1つのディレクトリ内のすべてのファイル」ではなくディレクトリ内のディレクトリ)を使用してプロジェクトに取り組んでいますか?長所/短所は何ですか?

38
paercebal

すべての回答とコンパイラのドキュメントを読んだ後、私は次の標準に従うことにしました。

プロジェクトヘッダーであれ外部ヘッダーであれ、すべてのファイルに対して、常に次のパターンを使用します。

#include <namespace/header.hpp>

衝突を避けるために、名前空間は少なくとも1つのディレクトリの深さです。

もちろん、これは、プロジェクトヘッダーが配置されているプロジェクトディレクトリも「デフォルトのインクルードヘッダー」としてメイクファイルに追加する必要があることを意味します。

この選択の理由は、私が次の情報を見つけたからです。

1.インクルード ""パターンはコンパイラに依存します

以下に答えます

1.a標準

ソース:

  • C++ 14ワーキングドラフトn3797: https://isocpp.org/files/papers/N3797.pdf
  • C++ 11、C++ 98、C99、C89(引用されているセクションはこれらすべての規格で変更されていません)

セクション16.2ソースファイルインクルードでは、次のように読むことができます。

フォームの前処理ディレクティブ

  #include <h-char-sequence> new-line

実装で定義された場所のシーケンスで、<と>区切り文字の間の指定されたシーケンスによって一意に識別されるヘッダーを検索し、そのディレクティブをヘッダーのコンテンツ全体に置き換えます。場所の指定方法または識別されるヘッダーの方法は、実装によって定義されます。

これは、#include <...>が実装で定義された方法でファイルを検索することを意味します。

次に、次の段落:

フォームの前処理ディレクティブ

  #include "q-char-sequence" new-line

そのディレクティブを、「区切り文字」の間の指定されたシーケンスで識別されるソースファイルのコンテンツ全体に置き換えます。指定されたソースファイルは、実装定義の方法で検索されます。この検索がサポートされていない場合、または検索が失敗した場合、ディレクティブは読み取りのように再処理されます

  #include <h-char-sequence> new-line

元のディレクティブからの同一の含まれるシーケンス(存在する場合は>文字を含む)。

つまり、#include "..."は実装で定義された方法でファイルを検索し、ファイルが見つからない場合は、#include <...>であるかのように別の検索を実行します。

結論として、コンパイラのドキュメントを読む必要があります。

何らかの理由で、標準のどこにも「システム」または「ライブラリ」ヘッダーまたは他のヘッダーの違いがないことに注意してください。唯一の違いは、#include <...>がヘッダーをターゲットにしているように見えるのに対し、#include "..."はソースをターゲットにしているようです(少なくとも英語の表現では)。

1.b Visual C++:

ソース:

#include "MyFile.hpp"

プリプロセッサは、次の順序でインクルードファイルを検索します。

  1. #includeステートメントを含むファイルと同じディレクトリ内。
  2. 以前に開いたディレクトリには、開いたときとは逆の順序でファイルが含まれます。検索は、最後に開かれたインクルードファイルのディレクトリから始まり、最初に開かれたインクルードファイルのディレクトリまで続きます。
  3. 各/ Iコンパイラオプションで指定されたパスに沿って。
  4. (*)INCLUDE環境変数または開発環境のデフォルトのインクルードで指定されたパスに沿って。

#include <MyFile.hpp>

プリプロセッサは、次の順序でインクルードファイルを検索します。

  1. 各/ Iコンパイラオプションで指定されたパスに沿って。
  2. (*)INCLUDE環境変数または開発環境のデフォルトのインクルードで指定されたパスに沿って。

最後のステップについての注意

このドキュメントでは、<...>"..."の両方に含まれる「INCLUDE環境変数で指定されたパスに沿って」の部分について明確ではありません。次の引用はそれを標準に固執させます:

#include "path-spec"として指定されているインクルードファイルの場合、ディレクトリ検索は親ファイルのディレクトリから始まり、祖父母ファイルのディレクトリをたどります。つまり、検索は、処理中の#includeディレクティブを含むソースファイルを含むディレクトリを基準にして開始されます。祖父母ファイルがなく、ファイルが見つからない場合は、ファイル名が山かっこで囲まれているかのように検索が続行されます。

したがって、最後のステップ(アスタリスクでマークされている)は、ドキュメント全体を読んだことによる解釈です。

1.c g ++

ソース:

次の引用は、プロセスを要約したものです。

GCC [...]は、[システムディレクトリ]で#include <file>で要求されたヘッダーを検索します[...]-Iで指定されたすべてのディレクトリは、デフォルトの前に左から右の順序で検索されますディレクトリ

GCCは、最初に現在のファイルを含むディレクトリで#include "file"で要求されたヘッダーを検索し、次に-iquoteオプションで指定されたディレクトリで検索し、次に同じ場所で山括弧で要求されたヘッダーを検索します。

#include "MyFile.hpp"

このバリアントは、独自のプログラムのヘッダーファイルに使用されます。プリプロセッサは、次の順序でインクルードファイルを検索します。

  1. #includeステートメントを含むファイルと同じディレクトリ内。
  2. 各-iquoteコンパイラオプションで指定されたパスに沿って。
  3. #include <MyFile.hpp>について

#include <MyFile.hpp>

このバリアントは、システムヘッダーファイルに使用されます。プリプロセッサは、次の順序でインクルードファイルを検索します。

  1. 各-Iコンパイラオプションで指定されたパスに沿って。
  2. システムディレクトリ内。

1.d Oracle/Sun Studio CC

ソース:

テキストはそれ自体と多少矛盾していることに注意してください(理解するには例を参照してください)。キーフレーズは「違いは、現在のディレクトリでは、名前を引用符で囲んだヘッダーファイルのみが検索されることです。「」

#include "MyFile.hpp"

このバリアントは、独自のプログラムのヘッダーファイルに使用されます。プリプロセッサは、次の順序でインクルードファイルを検索します。

  1. 現在のディレクトリ(つまり、「含む」ファイルを含むディレクトリ)
  2. -Iオプションで名前が付けられたディレクトリ(存在する場合)
  3. システムディレクトリ(例:/ usr/includeディレクトリ)

#include <MyFile.hpp>

このバリアントは、システムヘッダーファイルに使用されます。プリプロセッサは、次の順序でインクルードファイルを検索します。

  1. -Iオプションで名前が付けられたディレクトリ(存在する場合)
  2. システムディレクトリ(例:/ usr/includeディレクトリ)

1.e XL C/C++コンパイラーリファレンス-IBM/AIX

ソース:

どちらのドキュメントにも「XLC/C++コンパイラリファレンス」というタイトルが付いています。最初のドキュメントは古い(8.0)ですが、理解しやすいです。 2つ目は新しい(12.1)ですが、復号化するのが少し難しいです。

#include "MyFile.hpp"

このバリアントは、独自のプログラムのヘッダーファイルに使用されます。プリプロセッサは、次の順序でインクルードファイルを検索します。

  1. 現在のディレクトリ(つまり、「含む」ファイルを含むディレクトリ)
  2. -Iオプションで名前が付けられたディレクトリ(存在する場合)
  3. システムディレクトリ(例:/ usr/vac [cpp]/includeまたは/ usr/includeディレクトリ)

#include <MyFile.hpp>

このバリアントは、システムヘッダーファイルに使用されます。プリプロセッサは、次の順序でインクルードファイルを検索します。

  1. -Iオプションで名前が付けられたディレクトリ(存在する場合)
  2. システムディレクトリ(例:/ usr/vac [cpp]/includeまたは/ usr/includeディレクトリ)

1.e結論

パターン ""は、コンパイラ間で微妙なコンパイルエラーを引き起こす可能性があります。現在、Windows Visual C++、Linux g ++、Oracle/Solaris CC、AIX XLの両方で作業しているため、これは受け入れられません。

とにかく、「」で説明されている機能の利点は、とにかく興味深いものではありません。

2. {namespace} /header.hppパターンを使用します

私は仕事で見ました(つまり、これは理論ではなく、現実の、痛みを伴う専門的な経験です)同じ名前の2つのヘッダー。1つはローカルプロジェクトディレクトリにあり、もう1つはグローバルインクルードにあります。

「」パターンを使用していて、そのファイルがローカルヘッダーとグローバルヘッダーの両方に含まれていたため、奇妙なエラーが発生したときに実際に何が起こっているのかを理解する方法がありませんでした。

インクルードでディレクトリを使用すると、ユーザーは次のいずれかを記述する必要があるため、時間を節約できます。

#include <MyLocalProject/Header.hpp>

または

#include <GlobalInclude/Header.hpp>

あなたはその間注意するでしょう

#include "Header.hpp"

正常にコンパイルされたはずなので、問題は隠されていますが、

#include <Header.hpp>

通常の状況ではコンパイルされなかったでしょう。

したがって、<>表記に固執すると、開発者はインクルードの前に正しいディレクトリを付ける必要があります。これは、「」よりも<>を好むもう1つの理由です。

3.結論

<>表記と名前空間表記の両方を一緒に使用すると、プリコンパイラからファイルを推測する可能性がなくなり、代わりにデフォルトのインクルードディレクトリのみが検索されます。

もちろん、標準ライブラリは通常どおり含まれています。つまり、次のとおりです。

#include <cstdlib>
#include <vector>
31
paercebal

私は通常、システムヘッダーに<>を使用し、プロジェクトヘッダーに「」を使用します。パスに関しては、必要なファイルがインクルードパスのサブディレクトリにある場合にのみ必要です。

たとえば、/ usr/include/SDL /にファイルが必要であるが、インクルードパスに/ usr/include /のみが含まれている場合は、次を使用できます。

#include <SDL/whatever.h>

また、入力するパスが/で始まらない限り、現在の作業ディレクトリからの相対パスであることに注意してください。

コメントに答えるために編集:ライブラリのインクルードが少ない場合は、そのサブディレクトリをインクルードパスに含めるだけですが、ライブラリに多数のヘッダー(数十など)がある場合は、次のようにします。私が指定したサブディレクトリにあります。この良い例は、Linuxのシステムヘッダーです。あなたはそれらを次のように使用します:

#include <sys/io.h>
#include <linux/limits.h>

等.

別の良い答えを含めるように編集する:また、2つ以上のライブラリが同じ名前のヘッダーを提供することが考えられる場合、サブディレクトリソリューションは基本的に各ヘッダーに名前空間を与えます。

7
Evan Teran

C99標準から引用すると(一見すると、C90標準では表現が同じように見えますが、そこからカットアンドペーストすることはできません):

フォームの前処理ディレクティブ

# include "q-char-sequence" new-line

そのディレクティブを、「区切り文字」の間の指定されたシーケンスで識別されるソースファイルのコンテンツ全体に置き換えます。指定されたソースファイルは、実装定義の方法で検索されます。この検索がサポートされていない場合、または検索が失敗した場合、ディレクティブは読み取りのように再処理されます

# include <h-char-sequence> new-line

元のディレクティブからの同一の含まれるシーケンス(存在する場合は>文字を含む)。

したがって、#include "whatever"によって検索された場所は、#include <whatever>によって検索された場所のスーパーセットです。その意図は、最初のスタイルが一般にユーザーに「属する」ヘッダーに使用され、2番目の方法がコンパイラー/環境に「属する」ヘッダーに使用されることです。もちろん、灰色の領域がいくつかあることがよくあります。たとえば、Boostヘッダーにはどちらを使用する必要がありますか?私は#include <>を使用しますが、私のチームの他の誰かが#include ""を望んでいたとしても、あまり議論はしません。

実際には、ビルドが壊れない限り、どのフォームが使用されているかについては誰もあまり注意を払っていないと思います。私は確かにそれがコードレビューで言及されたことを覚えていません(あるいはそうでなければ)。

5
Michael Burr

私はあなたの質問の2番目の部分に取り組みます:

サードパーティからのヘッダーを含める場合、通常は<project/libHeader.h>を使用します。プロジェクト内からヘッダーを含める場合は"myHeader.h"

<project/libHeader.h>の代わりに<libHeader.h>を使用する理由は、複数のライブラリに「libHeader.h」ファイルがある可能性があるためです。それらを両方含めるには、含まれるファイル名の一部としてライブラリ名が必要です。

3
Trent

1.a:2つの表記の違いは何ですか?

""は、C/C++ファイルが配置されているディレクトリで検索を開始します。 <>は、-Iディレクトリおよびデフォルトの場所(/ usr/includeなど)で検索を開始します。どちらも最終的に同じ場所のセットを検索しますが、順序が異なるだけです。

1.b:すべてのコンパイラはそれらを同じ方法で実装しますか?

私はそう願っていますが、よくわかりません。

1.c:いつ<>を使用し、いつ「」を使用しますか(つまり、ヘッダーインクルードにどちらか一方を使用するために使用する基準は何ですか)?

インクルードファイルがCファイルの隣にあるはずの場合は「」を使用し、それ以外の場合は<>を使用します。特に、私たちのプロジェクトでは、すべての「パブリック」インクルードファイルがproject/includeディレクトリにあるため、<>を使用します。

2-#include {TheProject/TheHeader.hpp}または{TheHeader.hpp}?

すでに指摘したように、xxx/filename.hを使用すると、diskio /ErrorCodes.hやnetio/ErrorCodes.hなどを実行できます。

*プロジェクトのプライベートヘッダー?

プロジェクト内のサブシステムのプライベートヘッダー。プロジェクト内のサブシステムの「filename.h」パブリックヘッダーを使用します(プロジェクトの外部には表示されませんが、他のサブシステムからはアクセスできます)。プロジェクトに適合した規則に応じて、またはを使用します。使いたい

*プロジェクトのヘッダーですが、シンボルをエクスポートしています(したがって、「パブリック」)

ライブラリのユーザーが含めるのとまったく同じように含めます。恐らく

*モジュールがリンクしている別のプロジェクトのヘッダー

プロジェクトによって決定されますが、コンパイラまたは標準ライブラリの<> *ヘッダーを確実に使用します。標準に従って間違いなく<>。

3.a:ツリーのような組織内のソースやヘッダー(つまり、「1つのディレクトリ内のすべてのファイル」ではなくディレクトリ内のディレクトリ)を使用してプロジェクトに取り組んでいますか?長所/短所は何ですか?

私は構造化プロジェクトに取り組んでいます。スコアを超えるファイルがあるとすぐに、いくつかの分割が明らかになります。あなたはコードがあなたを引っ張っている方法に行くべきです。

2
user3458

私が正しく覚えていれば。

「パス」にあるすべてのライブラリにひし形を使用します。つまり、STLにあるライブラリ、またはインストールしたライブラリです。 Linuxでは、パスは通常「/ usr/include」です。Windowsではわかりませんが、「C:\ windows」の下にあると思います。

次に「」を使用して、その他すべてを指定します。開始ディレクトリ情報のない「my_bla.cpp」は、コードが存在/コンパイルされているディレクトリに解決されます。または、インクルードの正確な場所を指定することもできます。このように「c:\ myproj\some_code.cpp」

ヘッダーのタイプは重要ではなく、場所だけが重要です。

1
J.J.

_<>_と_""_の間には2つの主な違いがあります。 1つ目は、どの文字が名前を終了するかです。ヘッダー名にはエスケープシーケンスがないため、_#include <bla"file.cpp>_または_"bla>file.cpp"_を実行する必要がある場合があります。しかし、それはおそらく頻繁には起こりません。他の違いは、システムインクルードは_""_で発生することは想定されておらず、_<>_だけで発生することです。したがって、_#include "iostream"_が機能することは保証されていません。 _#include <iostream>_はです。私の個人的な好みは、プロジェクトの一部であるファイルには_""_を使用し、そうでないファイルには_<>_を使用することです。一部の人々は、標準ライブラリヘッダーに_<>_のみを使用し、その他すべてに_""_を使用します。 _<>_をBoostとstdにのみ使用する人もいます。プロジェクトによって異なります。すべてのスタイルの側面と同様に、最も重要なことは一貫性を保つことです。

パスに関しては、外部ライブラリがヘッダーの規則を指定します。例えば_<boost/preprocessor.hpp>_ _<wx/window.h>_ _<Magic++.h>_。ローカルプロジェクトでは、トップレベルのsrcdir(またはそれらが異なるライブラリプロジェクトではincludeディレクトリ)に関連するすべてのパスを記述します。

ライブラリを作成するときは、<>を使用してプライベートヘッダーとパブリックヘッダーを区別するか、ソースディレクトリではなく上記のディレクトリを_-I_するので、_#include "public_header.hpp"_と_"src/private_header.hpp"_。それは本当にあなた次第です。

編集:ディレクトリ構造を持つプロジェクトに関しては、私はそれらを強くお勧めします。すべてのブーストが1つのディレクトリにある(サブネームスペースがない)場合を想像してみてください!ディレクトリ構造は、ファイルを簡単に見つけることができ、命名の柔軟性が高くなるため、優れています(_"module\_text\_processor.hpp"_ではなく_"module/text\_processor.hpp"_)。後者はより自然で使いやすいです。

1
coppro

再<>対 ""。私の店では、「スタイル」に関しては非常に手間がかかります。私が要件を持っている数少ない領域の1つは、#includeステートメントで山かっこを使用することです。ルールは次のとおりです。オペレーティングシステムまたはコンパイラファイルを#includeしている場合は、必要に応じて山かっこを使用できます。他のすべての場合、それらは禁止されています。ここに誰かが書いたファイルまたはサードパーティのライブラリを#includeしている場合、<>は禁止されています。

理由は次のとおりです。#include "x.h"と#includeは同じパスを検索しません。 #includeは、システムパスと、入力したものだけを検索します。重要なことに、ファイルx.hが他の方法で検索パスに含まれていない場合、そのディレクトリは検索されません。

たとえば、次のファイルがあるとします。

c:\ dev\angles\main.cpp

#include "c:\utils\mylib\mylibrary.h"

int main()
{
    return 0;
}

c:\ utils\mylib\mylibrary.h

#ifndef MYLIB_H
#define MYLIB_H

#include <speech.h>

namespace mylib
{
    void Speak(SpeechType speechType);  
};

#endif

c:\ utils\mhlib\spirit.h

#ifndef SPEECH_H
#define SPEECH_H

namespace mylib
{
    enum SpeechType {Bark, Growl};
};

#endif

これは、PATH環境変数を設定するか、c:\ utils\mhlib \ディレクトリで-iを実行することにより、パスを変更せずにコンパイルされません。そのファイルが#include <speech.h>と同じディレクトリにある場合でも、コンパイラはmylibrary.hを削除できません。

2つの理由から、コードの#includeステートメントで相対パス名と絶対パス名を多用しています。

1)ライブラリとコンポーネントをメインのソースツリーから外しておく(つまり、ユーティリティライブラリを特別なディレクトリに置く)ことで、ライブラリのライフサイクルをアプリケーションのライフサイクルに結び付けません。これは、共通のライブラリを使用するいくつかの異なる製品がある場合に特に重要です。

2) Junctions を使用して、ハードドライブ上の物理的な場所を論理ドライブ上のディレクトリにマップしてから、すべての#includeで論理ドライブ上の完全修飾パスを使用します。例えば:

#include "x:\utils\mylib.h" --good、x:はsubstされたドライブであり、x:\ utilsはハードドライブ上のc:\ code\utils_1.0を指します

#include "c:\utils_1.0\mylib.h"-悪い! mylib.hを含むアプリケーションがMYLIBライブラリの特定のバージョンに結合され、すべての開発者はハードドライブの同じディレクトリc:\ utils_1.0にそれを持っている必要があります。

最後に、私のチームの目標を達成するのは広いですが難しいのは、ワンクリックコンパイルをサポートできるようにすることです。これには、ソース管理からコードを取得してから「コンパイル」を押すだけで、メインのソースツリーをコンパイルできることが含まれます。特に、コンパイルできるようにするためにパスとマシン全体の#includeディレクトリを設定する必要があるのは嫌です。なぜなら、開発マシンのビルドでセットアップフェーズに追加する小さなステップごとに、混乱が難しくなり、混乱しやすくなるからです。新しいマシンを高速化してコードを生成するのに時間がかかります。

1
John Dibling

システムヘッダーファイル(stdio、iostreams、stringなど)の<...>を使用し、そのプロジェクトに固有のヘッダーには「...」を使用します。

0
James Curran

プロジェクトのローカルヘッダーには#include "header.h"を使用し、システムインクルード、サードパーティインクルード、およびソリューション内の他のプロジェクトには#includeを使用します。 Visual Studioを使用しており、ヘッダーインクルードでプロジェクトディレクトリを使用する方がはるかに簡単です。このように、新しいプロジェクトを作成するときは常に、すべてのプロジェクトディレクトリを含むディレクトリのインクルードパスを指定するだけで済みます。各プロジェクト。

0
Brian Stewart