web-dev-qa-db-ja.com

コンパイル時にファイルを文字列に読み込みます

ファイルに何かを書き込みたい(それをfoo.cppと呼び、それを文字列としてプログラムに含めますコンパイル時#includeと同様です。

現在、私はこのCプリプロセッサを使用しています#define

#define toString(src) #src

この例のように使用される一連のコードを文字列に変換するには:

const char* str = toString(
  int x;
  void main(){}
);

必要に応じて、マクロの文字列化について読むことができます そこに

そのコードを外部ファイルに移動したいと思います。外部ファイルはコンパイル時に「リンク」されます。 ファイルをプログラムと一緒に配布する必要はありません。これは、実行時にファイルを読み取る場合に当てはまります。

以下に示すように#includeディレクティブを使用しようとしましたが、コンパイラーはそれを拒否しました。

const char* str = toString(
#include "foo.cpp"
);

g++は完全に混乱しているようですが、clang++は私にこのエラーを与えました:

error: embedding a #include directive within macro arguments is not supported

これができるかどうか/どのようにできるか誰かが知っていますか?

注:これを使用してGLSLシェーダーを作成していますが、この情報が役に立ったとは思えません。

PS:これは この質問 の複製であると言う前に、コードを独自のファイルの巨大な文字列に入れるか、外部ツール(例:xxd)16進表現をダンプすることは、私の現在の方法よりも優れていない(つまり、より簡単/クリーンである)ため、私にとって「解決策」ではありません。


数年後の更新:
この質問は重複して閉じられていたため、答えることができなかったことに気づきました。 このコミット 、それ自体が この記事へのコメント に基づいているのを見たときに探していた答えを見つけ、それ以来それを使用しています。

簡単に言うと、小さなアセンブリファイルには、必要なファイルが含まれ、指定されたNAMEの下に公開され、3つの変数NAME_beginNAME_end、およびNAME_lenで次のことができます。 Cコードからコンテンツにアクセスします。

このようにして、必要なコードだけを含む通常のファイルが作成され、実行時に読み取ったり、xxdフープをジャンプしたりする代わりに、コンパイル時に自動的に読み取られます。

20
1ace

何を達成しようとしているのかよくわかりませんが、Linuxコマンドラインユーティリティxxdがあなたが探しているものかもしれません。

xxd -i [filename]

フルバイナリエンコーディングのファイル内容とその長さの変数を含む配列を含むCスタイルのヘッダーファイルを生成します。

例:

xxd -i /proc/cpuinfo

でファイルを作成します

unsigned char _proc_cpuinfo[] = {
  0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x09, 0x3a, 0x20,
  0x30, 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x09,
...
};
unsigned int _proc_cpuinfo_len = 654390;

結果のヘッダーをコードに含め、それらの変数を介して配列とファイルの長さにアクセスできます。

12
Sergey L.

xxdはファイルが読み取れなくなるため、気に入らないと言います。けっこうだ。入力と出力として文字列が特に必要なため、データを別の形式でエンコードする独自のユーティリティを作成するのは簡単です。文字列リテラルの連結を利用して、これを読みやすくすることができます。

const char* str =
#include "foo.h"
    ;

foo.h:

"  int x;" "\n"
"  void main(){}" "\n"

C++ 11の 生の文字列リテラル を簡単に使用してみました。これにより、再フォーマットせずにファイルを使用できるようになりましたが、機能しませんでした。 #includeは、必要に応じてファイルを含めるのではなく、文字列の一部と見なされます。

8
Mark Ransom

この種の場合の最も簡単なことは、ファイルを読み取り、各行を引用符で囲んで出力する小さなプリプロセッサを作成することです。私はおそらくPythonでこれを行うでしょうが、C++でも非常に簡単です。どこかからinputFileoutputFilevariableNameを取得していると仮定します(おそらくargvですが、後者の2つは入力ファイル名から取得することをお勧めします。 :

_void
wrapFile( std::istream& inputFile,
          std::ostream& outputFile,
          std::string const& variableName )
{
    outputFile << "extern char const " << variableName << "[] =\n";
    std::string line;
    while ( std::getline( inputFile, line ) ) {
        outputFile << "    \"" << line << "\\n\"\n";
    }
    std::outputFile << ";" << std::endl;
}
_

含めるファイルの内容によっては、_"_や_\_などをエスケープするために、出力する前にlineをマングルする必要がある場合があります。

凝ったものにしたい場合は、独自の行ではなく、最後にラップされた行にセミコロンを挿入するテストを追加できますが、それは実際には必要ありません。

これにより、_'\0'_で終了する文字列が生成されます。文字列の推定長さを考えると、次の長さの2番目の変数を追加することが望ましい場合があります。

_std::outputFile << "extern int const "
                << variableName
                << "_len = sizeof(" << variableName << ") - 1;\n";
_

(コンパイラが文字列リテラルに追加する終了_'\0'_をカウントしたくないので、-1を忘れないでください。)生成されたファイルを使用する場所に含める場合は、 _std::begin_と_std::end_が必要な情報を提供するため、必ずしもこれは必要ありません(ただし、_'\n'_を無視するには、std::end( variableName ) - 1を使用することを忘れないでください。 )。

makeを使用している場合、生成されたファイルをラップされるファイルに依存させるのはかなり簡単です。およびラップを実行する実行可能ファイル(これは上記のソースに依存します)など)。 Visual Studioでは、C++で記述した場合(これにPython)を使用する理由の1つ)、ラッピングコード用に別のプロジェクトを作成する必要があります。依存関係の管理に問題がある可能性があります。VisualStudioは実際には専門的な作業用に設計されていません(このような手法を使用してコードの大きなブロックが定期的に生成されます)。

1
James Kanze