web-dev-qa-db-ja.com

ヘッダーの「../include/header.h」などの相対パスの利点は何ですか?

私は質問をレビューしました includeディレクティブを正しく使用する方法 および C++ #includeのセマンティクス で、これに対処しません-SO =タイトルを入力したとき...

もしあれば、書くことの利点は何ですか:

#include "../include/someheader.h"
#include "../otherdir/another.h"

単なるファイル名を使用する場合と比較して:

#include "someheader.h"
#include "another.h"

または、おそらく '..'のない相対名:

#include "include/someheader.h"
#include "otherdir/another.h"

私が見る問題は次のとおりです。

  • どのソースファイルにヘッダーが含まれているかを気にせずにヘッダーを移動することはできません。
  • 依存関係とエラーレポートのヘッダーのパスが非常に長くなる可能性があります。今日は「../dir1/include/../../include/../dir2/../include/header.h」で1つありました。

私が見ることができる唯一のメリットは、ファイルを移動する必要はありませんが、ヘッダーを見つけるために常に「-I」ディレクティブを使用せずに逃げることができるかもしれませんが、柔軟性が失われ、サブサブディレクトリなどでコンパイルすることは、利益を上回るようです。

だから、私は利益を見落としていますか?


入力いただきありがとうございます。私が見落としている「..」を使用した表記法に大きな利点はないというのがコンセンサスだと思います。一般的には、「somewhere/header.h」表記が好きです。新しいプロジェクトで使用しています。私が取り組んでいるのは新しいものではありません。

問題の1つは、さまざまなヘッダーセットがあり、多くの場合、rspqr.hrsabc.hrsdef.hrsxyz.hなどのプレフィックスが付いていることです。これらはすべてrsmpディレクトリのコードに関連していますが、ヘッダーの一部はrsmpにあり、その他はrsmp (また、コードの他のさまざまな領域についても繰り返します。複数の場所にヘッダーがあり、他のコードでランダムに必要になります。)コードの長年にわたる複雑化により、移動が大きな問題になっています。また、-Iオプションが提供されているメイクファイルは一貫していません。全体として、それは何十年にもわたるそれほど穏やかな無視の悲しい物語です。何も壊さずにすべてを修正することは、長く退屈な仕事になります。

53

ヘッダーファイルがどの名前空間またはモジュールに属しているかを非常に明確にするため、パス構文が好まれます。

#include "Physics/Solver.h"

すべてのモジュールの名前をヘッダーファイルにプレフィックスする必要なく、非常に自己記述的です。

ただし、「..」構文を使用することはほとんどなく、代わりにプロジェクトに正しいベースロケーションを指定します。

38
Andrew Grant

#include "../include/header.h"の問題は、偶然に動作することが多く、その後、一見無関係な変更によって動作が停止することです。

たとえば、次のソースレイアウトを考えます。

./include/header.h
./lib/library.c
./lib/feature/feature.c

そして、-I. -I./libのインクルードパスでコンパイラを実行しているとしましょう。何が起こるのですか?

  • ./lib/library.c#include "../include/header.h"を実行できますが、これは理にかなっています。
  • ./lib/feature/feature.cは、意味がありませんが、#include "../include/header.h"も実行できます。これは、コンパイラが現在のファイルの場所に関連して#includeディレクティブを試行し、それが失敗した場合、#includeパスの各-Iエントリに関連して#includeディレクティブを試行するためです。

さらに、後で-I./libパスから#includeを削除すると、./lib/feature/feature.cが壊れます。

私は次のようなものが好ましいと思います:

./projectname/include/header.h
./projectname/lib/library.c
./projectname/lib/feature/feature.c

-I.以外のインクルードパスエントリを追加せず、library.cfeature.cの両方が#include "projectname/include/header.h"を使用します。 「プロジェクト名」が一意である可能性が高いと仮定すると、ほとんどの状況で名前の衝突やあいまいさが生じないはずです。また、インクルードパスおよび/またはmakeのVPATH機能を使用して、絶対に必要な場合、プロジェクトの物理レイアウトを複数のディレクトリに分割できます(たとえば、プラットフォーム固有の自動生成コードに対応するため、これは本当に壊れているものです) #include "../../somefile.h"を使用するとダウンします)。

24
bk1e

IANALL。ただし、実際のCまたはC++ソースファイルに..を配置する必要はないと思います。これは移植性がなく、標準ではサポートされていないためです。これは、Windowsで\を使用することに似ています。コンパイラが他の方法で動作できない場合にのみ実行してください。

#include ""ディレクティブのパスは、次の場合に1つ以上の「../」のシーケンスで始まります。

  • インクルードファイルとのコロケーションが固定されているファイルをインクルードします。
  • pOSIXシステム上またはVC++で構築している
  • どのファイルが含まれるかについてあいまいさを避けたい場合。

コードベースにエラーが含まれている場所と、これが原因で診断が困難な障害の原因の例を簡単に提供できます。ただし、プロジェクトに障害がない場合でも、絶対パスを使用して相互に関連するファイルを指定すると、サードパーティによって悪用される可能性があります。

たとえば、次のプロジェクトレイアウトを考えます。

./your_lib/include/foo/header1.h
./your_lib/include/bar/header2.h
./their_lib/include/bar/header2.h

どのようにyour_lib/include/foo/header1.hincludeyour_lib/include/bar/header2.h? 2つのオプションを考えてみましょう。

  1. #include <bar/header2.h>

    your_lib/includetheir_lib/includeの両方がヘッダー検索として引用されていると仮定しますパス(たとえば、GCCの-Iまたは-isystemオプションを使用)、次にheader2.hを選択するかどうかは重要です。これらの2つのパスが検索される順序に。

  2. #include "../bar/header2.h"

    コンパイラが検索する最初の場所は、your_lib/include/foo/header1.hの場所で、your_lib/include/foo /。最初にyour_lib/include/foo /../ bar/header2.hを試しますが、これはyour_lib/include/bar/header2.h正しいファイルを見つけます。ヘッダー検索パスはまったく使用されず、あいまいさの余地はほとんどありません。

この場合、与えられた理由からオプション2)を強くお勧めします。

他の回答のいくつかの議論に応えて:

  • @ andrew-grant says

    ...ヘッダーファイルがどの名前空間またはモジュールに属しているかを非常に明確にします。

    多分。ただし、相対パスは「この同じモジュール内」として解釈できます。異なるモジュールに同じ名前のディレクトリが複数ある場合に明確になります。

  • @ bk1e says

    ...しばしば偶然に動作します...

    相対パスは、プロジェクトが最初から壊れており、簡単に修正できる非常にまれなケースでのみ偶然に機能すると主張します。コンパイラエラーを発生させずにこのような名前の衝突を経験することはありそうにないようです。一般的なシナリオは、依存プロジェクトのファイルにヘッダーの1つが含まれ、ヘッダーに別のヘッダーが含まれる場合です。テストスイートをコンパイルすると、その依存プロジェクトから分離してコンパイルされた場合、「No such file or directory」エラーが発生するはずです。

  • @singlenegationelimination says

    ...これは移植性がなく、標準ではサポートされていません。

    ISO C規格では、プログラムがコンパイルまたは実行されるシステムのすべての詳細が指定されているわけではありません。それは、それらがサポートされていないという意味ではなく、単に標準がCが実行されるプラットフォームを過剰に指定していないということです。 (""<>が現代の一般的なシステムでどのように解釈されるかの区別 可能性が高い POSIX標準で。)

  • @ daniel-paull says

    「..」は相対的な場所を想定しており、壊れやすい

    壊れやすい方法?おそらく、2つのファイルのコロケーションに敏感です。したがって、「..」は、インクルードファイルの作成者が場所を制御する場合にのみ(常に)使用する必要があります。

4
John McFarlane

相対パスを持つウィンドウの別の問題はMAX_PATHです。これにより、コンパイルの問題がトリガーされます。 Androidのクロスコンパイルを実行すると、パスの長さが260を超えます。

2
MadSystem

ソースツリーをネストされたネームスペースと考え、インクルードパスを使用すると、このネームスペースのルートにディレクトリをプルできます。質問は、コードがディスク上でどのように編成されているかに関係なく、コードベースの論理的な名前空間を形成することの1つです。

私は次のようなパスを避けます:

  • "include/foo/bar.h" — "include"は非論理的で冗長なようです
  • "../foo/bar.h" —「..」は相対的な場所を想定しており、脆弱です
  • "bar.h" — bar.hが現在のディレクトリにない限り、これはグローバル名前空間を汚染し、あいまいさを要求しています。

個人的には、次のようなパスをプロジェクトのインクルードパスに追加する傾向があります— "..;../..;../../..;../../../.."

これにより、#includesに一種の非表示ルールを適用でき、他のコードを壊すことなくヘッダーを自由に移動できます。もちろん、完全に修飾されていない名前はあいまいになる可能性がある(または時間が経つにつれて)ので注意しないと、間違ったヘッダーファイルにバインドされるリスクが生じます。

私は、パブリックヘッダーの#includesを完全に修飾する傾向があるため、コードを使用するサードパーティが"..;../..;../../..;../../../.."をプロジェクトに追加する必要はありません。

2
Daniel Paull

プロジェクトのルートを基準にしてファイルを配置し、それをソース管理にチェックインし、別の開発者がローカルシステムの別の場所にチェックアウトすると、動作するためです。

1
Joel Coehoorn