GCCの-I
スイッチの細かい印刷を読んで、コマンドラインでそれを使用するとシステムインクルードが上書きされることにかなりショックを受けました。 プリプロセッサドキュメント から
"これらのディレクトリは標準システムヘッダーファイルディレクトリの前に検索されるため、
-I
を使用してシステムヘッダーファイルを上書きし、独自のバージョンに置き換えることができます。"
彼らは嘘をついていないようです。 GCC 7を使用する2つの異なるUbuntuシステムで、ファイルendian.h
を作成すると:
#error "This endian.h shouldn't be included"
...そして同じディレクトリにmain.cpp
(またはmain.c、同じ違い)を作成します。
#include <stdlib.h>
int main() {}
次に、g++ main.cpp -I. -o main
(またはclang、同じ違い)でコンパイルすると、次のようになります。
In file included from /usr/include/x86_64-linux-gnu/sys/types.h:194:0,
from /usr/include/stdlib.h:394,
from /usr/include/c++/7/cstdlib:75,
from /usr/include/c++/7/stdlib.h:36,
from main.cpp:1:
./endian.h:1:2: error: #error "This endian.h shouldn't be included"
したがって、stdlib.hには、このtypes.hファイルが含まれています。194行目では、#include <endian.h>
とだけ記述されています。私の明らかな誤解(そしておそらく他の誤解)は、山括弧がこれを防いでいたということでしたが、-私は思っていたよりも強いです。
強力ではありませんが、十分ではありません。これは、最初にコマンドラインに/ usr/includeを貼り付けても修正できないためです。
」標準システムのインクルードディレクトリ、または
-isystem
で指定されたディレクトリが-I
でも指定されている場合、-I
オプションは無視されます。ディレクトリはまだです。検索されますが、システムの通常の位置にあるシステムディレクトリとしてチェーンをインクルードします。」
実際、g++ -v main.cpp -I/usr/include -I. -o main
の詳細出力では、リストの下部に/ usr/includeが残ります。
#include "..." search starts here:
#include <...> search starts here:
.
/usr/include/c++/7
/usr/include/x86_64-linux-gnu/c++/7
/usr/include/c++/7/backward
/usr/lib/gcc/x86_64-linux-gnu/7/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/7/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
びっくりさせて。私はこれを質問にすると思います:
この非常に深刻な問題を考慮して、ほとんどのプロジェクトが-I
を使用する正当な理由は何ですか?付随的な名前に基づいてシステム上の任意のヘッダーを上書きできます衝突。ほぼ全員が-iquote
を代わりに使用するべきではありませんか?
-I
が-iquote
よりも正当な理由は何ですか? -I
は標準化されています(少なくとも POSIX で)が、-iquote
は標準化されていません。 (実際、-I
を使用しているのは、tinycc(プロジェクトのコンパイルに使用するコンパイラの1つ)が-iquote
をサポートしていないためです。)
危険を考えると、プロジェクトはどのように-I
で管理しますか?インクルードをディレクトリにラップし、-Iを使用してそのディレクトリを含むディレクトリを追加します。
includes/mylib/endian.h
-Iincludes
#include "mylib/endian.h" //or <mylib/endian.h>
これにより、mylib
の名前で衝突しない限り、衝突しません(少なくともヘッダー名に関しては)。
GCCのマニュアルを振り返ると、-iquote
およびその他のオプションがGCC 4にのみ追加されたように見えます https://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Directory- Options.html#Directory%20Options
したがって、"-I"
の使用は、おそらく、習慣、怠zy、後方互換性、新しいオプションの無知、他のコンパイラとの互換性のいくつかの組み合わせです。
解決策は、ヘッダーファイルをサブディレクトリに配置することで、ヘッダーファイルを「名前空間」にすることです。たとえば、エンディアンヘッダーを"include/mylib/endian.h"
に配置し、"-Iinclude"
をコマンドラインに追加すると、他のライブラリまたはシステムライブラリと競合しない#include "mylib/endian.h"
を実行できます。
これは、-I
が危険だという前提は誤りです。この言語は、#include
のいずれかの形式で実装が定義されたヘッダーファイルの検索を残しているため、標準ヘッダーファイルの名前とまったく競合するヘッダーファイルを使用することは安全ではありません。単にこれを行うことを控えてください。
明らかなケースは、クロスコンパイルです。 GCCは、常にローカルシステム用にコンパイルしているという歴史的なUNIXの前提、または少なくとも非常に近いものに苦しんでいます。そのため、コンパイラのヘッダーファイルはシステムルートにあります。クリーンなインターフェイスがありません。
これに対して、Windowsはコンパイラを想定しておらず、Windowsコンパイラは、ローカルシステムをターゲットにしていると想定していません。そのため、コンパイラのセットとSDKのセットをインストールできます。
現在、クロスコンパイルでは、GCCはWindows用のコンパイラのように動作します。ローカルシステムヘッダーを使用することを前提とはしていませんが、必要なヘッダーを正確に指定できます。そして明らかに、リンクしたライブラリにも同じことが言えます。
これを行うと、置換ヘッダーのセットは、ベースシステムのtopに進むように設計されていることに注意してください。実装が同一である場合、置換セットのヘッダーは省略できます。例えば。 <complex.h>
が同じである可能性があります。複素数の実装にはそれほど大きな違いはありません。ただし、<endian.h>
などの内部実装ビットをランダムに置き換えることはできません。
TL、DR:このオプションは、自分が何をしているかを知っている人向けです。 「安全でない」ということは、対象読者にとって議論ではありません。