web-dev-qa-db-ja.com

#pragmaがかつて自動的に仮定されないのはなぜですか?

ファイルを1回だけインクルードするようにコンパイラーに指示することの意味は何ですか?デフォルトでは意味がありませんか。 1つのファイルを複数回含める必要がある理由すらありませんか?なぜ仮定しないのですか?それは特定のハードウェアと関係があるか?

74
Johnny Cache

ここに複数の関連質問があります。

  • #pragma onceが自動的に強制されないのはなぜですか?
    ファイルを複数回インクルードしたい場合があるためです。

  • ファイルを複数回インクルードしたいのはなぜですか?
    他の答えではいくつかの理由が挙げられています(Boost.Preprocessor、X-Macros、データファイルを含む)。 「コードの重複を避ける」という特定の例を追加したいと思います。OpenFOAMは、関数内のビットや部分を#includeするのが一般的な概念であるというスタイルを推奨します。例えば ​​ これ 議論を見てください。

  • わかりましたが、オプトアウトのデフォルトではないのはなぜですか?
    規格では実際には指定されていないためです。 #pragmaは、定義により実装固有の拡張です。

  • #pragma onceがまだ標準化された機能になっていないのはなぜですか(広くサポートされているため)。
    プラットフォームに依存しない方法で「同じファイル」とは何かを突き止めるのは、実際には驚くほど難しいからです。 より詳しい情報はこの答えを見てください

80
Max Langhof

#includeanywhereは、グローバルスコープだけでなく、関数内で(および必要に応じて複数回)ファイル内で使用できます。確かに、見苦しくてスタイルが良くありませんが、可能性があり、時には賢明です(インクルードするファイルによって異なります)。 #includeが一度だけだった場合、それは機能しません。 #includeは、結局のところ、単なるテキストの置換(切り取りと貼り付け)を行うだけであり、含めるものすべてがヘッダーファイルである必要はありません。たとえば、#includeは、std::vectorを初期化するための生データを含む自動生成データを含むファイルです。いいね

std::vector<int> data = {
#include "my_generated_data.txt"
}

そして、「my_generated_data.txt」がコンパイル中にビルドシステムによって生成されたものになるようにします。

または多分私は怠け者/愚かな/愚かで、これをファイルに入れます(very不自然な例):

const noexcept;

そして、私はやる

class foo {
    void f1()
    #include "stupid.file"
    int f2(int)
    #include "stupid.file"
};

別の、少し不自然な例は、多くの関数が名前空間で大量の型を使用する必要があるソースファイルですが、グローバル名前空間を汚染するため、グローバルにusing namespace foo;と言いたくないだけです。あなたが望んでいない他の多くのもの。したがって、次を含むファイル「foo」を作成します

using std::vector;
using std::array;
using std::rotate;
... You get the idea ...

そして、あなたはあなたのソースファイルでこれを行います

void f1() {
#include "foo" // needs "stuff"
}

void f2() {
    // Doesn't need "stuff"
}

void f3() {
#include "foo" // also needs "stuff"
}

注:このようなことをすることは推奨していません。しかし、それは可能であり、いくつかのコードベースで行われ、なぜ許可されないのかわかりません。 does用途があります。

特定のマクロ(#defines)の値によって、インクルードするファイルの動作が異なることもあります。そのため、最初に値を変更した後、複数の場所にファイルを含めると、ソースファイルのさまざまな部分で異なる動作が得られる場合があります。

36
Jesper Juhl

複数回含めることは、例えば X-macro の手法で使えます。

data.inc:

X(ONE)
X(TWO)
X(THREE)

use_data_inc_twice.c

enum data_e { 
#define X(V) V,
   #include "data.inc"
#undef X
};
char const* data_e__strings[]={
#define X(V) [V]=#V,
   #include "data.inc"
#undef X
};

私は他の用途については知りません。

26
PSkocik

言語に存在する「#include」機能の目的は、プログラムを複数のコンパイル単位に分解するためのサポートを提供することであるという仮定の下で動作しているようです。 それは間違っています。

それはその役割を果たしますが、それは意図された目的ではありませんでした。 Cは 最初に開発された PDP-11よりもわずかに高いレベルの言語として Macro-11 Unixを再実装するためのアセンブリです。 Macro-11の機能であるため、マクロプリプロセッサがありました。別のファイルのマクロをテキストでインクルードする機能がありました。これは、新しいCコンパイラに移植していた既存のUnixが利用していたMacro-11の機能であったためです。

これで、「#include」は、(おそらく)ちょっとしたハックとして、コードをコンパイル単位に分離するのに便利であることがわかりました。ただし、このハックが存在するという事実は、Cで行われる方法になったことを意味します。方法が存在するという事実は、この機能を提供するために作成される新しいメソッドneededを意味しません。そのため、これまでより安全なもの(たとえば、複数の包含に対して脆弱ではない)は作成されませんでした。既にCにあったので、Cの残りの構文とイディオムのほとんどと共にC++にコピーされました。

C++ 適切なモジュールシステム を提供する提案があります。そのため、この45年前のプリプロセッサハックは最終的に不要になります。しかし、これがどれほど差し迫っているのかわかりません。私はそれが10年以上も作業中であると聞いてきました。

19
T.E.D.

いいえ、これは例えば図書館の作家に利用可能なオプションを著しく妨げるでしょう。たとえば、Boost.Preprocessorを使用すると、プリプロセッサループを使用できます。それらを実現する唯一の方法は、同じファイルを複数含めることです。

そしてBoost.Preprocessorは多くの非常に便利なライブラリのためのビルディングブロックです。

10
SergeyA

私が主に作業している製品のファームウェアでは、関数とグローバル/スタティック変数をメモリのどこに割り当てるべきかを指定できる必要があります。プロセッサが直接高速にアクセスできるように、リアルタイム処理はチップ上のL1メモリに存在する必要があります。それほど重要ではない処理は、チップ上のL2メモリに入り得る。また、特に早く処理する必要がないものは、外部DDRに格納してキャッシュ処理を行うことができます。これは少し遅くても問題ありません。

物事がどこに行くかを割り当てるための#pragmaは、長くて些細ではない行です。誤解するのは簡単でしょう。誤解すると、コード/データがデフォルト(DDR)メモリに黙って置かれることになり、 that の効果は、閉ループ制御が機能しなくなり、簡単な理由で機能しなくなる可能性があります。

だから私はそのプラグマだけを含むインクルードファイルを使います。私のコードは今このようになっています。

ヘッダファイル...

#ifndef HEADERFILE_H
#define HEADERFILE_H

#include "set_fast_storage.h"

/* Declare variables */

#include "set_slow_storage.h"

/* Declare functions for initialisation on startup */

#include "set_fast_storage.h"

/* Declare functions for real-time processing */

#include "set_storage_default.h"

#endif

そして情報源...

#include "headerfile.h"

#include "set_fast_storage.h"

/* Define variables */

#include "set_slow_storage.h"

/* Define functions for initialisation on startup */

#include "set_fast_storage.h"

/* Define functions for real-time processing */

同じファイルがヘッダーにあっても、そこに複数のインクルードがあることに気付くでしょう。今何か間違えて入力した場合、コンパイラはインクルードファイル "set_fat_storage.h"が見つからないと言ってくれるので、簡単に修正できます。

それであなたの質問に答えて、これは多重包含が要求されるところの本当の、実際的な例です。

8
Graham