17

注: この質問は、OpenCL 自体とは何の関係もありません。私の質問の簡潔な説明については、最後の段落を確認してください。しかし、いくつかの背景を提供するために:

OpenCL を利用する C++ コードを書いています。私は自分の OpenCL カーネルのソースを独自のファイルに保持して、コーディングとメンテナンスを容易にするのが好きです (関連する C++ コードにソースを文字列定数として直接埋め込むのではなく)。これは必然的に、バイナリを配布するときに OpenCL ランタイムにそれらをロードする方法の問題につながります。理想的には、OpenCL ソースがバイナリに含まれているため、バイナリが特定の場所にある必要はありません。 OpenCLソースコードがどこにあるかを知るために、いくつかのディレクトリ構造内で。

OpenCL ファイルを文字列定数としてどこかに含めたいと思います。できれば、追加のビルド手順や外部ツールを使用せずに (クロスコンパイラ/クロスプラットフォームの使いやすさのために...つまり、toxxdなどは使用しません)。このスレッドの 2 番目の回答に基づいて、次のようなテクニックを見つけたと思いました。

#define STRINGIFY(src) #src

inline const char* Kernels() {
  static const char* kernels = STRINGIFY(
    #include "kernels/util.cl"
    #include "kernels/basic.cl"
  );
  return kernels;
}

STRINGIFY可能であれば、OpenCLコードにマクロを埋め込まないことをお勧めします(上記のSOの質問で行われたように)。現在、これは Clang/LLVM コンパイラで素晴らしく機能しますが、GCC は恐ろしい死に方をします (「マクロ STRINGIFY を呼び出す未終了の引数リスト」と、.cl ファイルの内容に関連するさまざまな構文「エラー」が表示されます)。したがって、明らかに、この正確な手法はコンパイラ間で使用できません (MSVC は試していませんが、そこでも動作するようにしたいと思います)...コンパイラ間で動作するように最小限にマッサージするにはどうすればよいですか?

要約すると、外部ツールを呼び出したり、余分なコードでファイルを汚染したりせずに、ファイルの内容を C/C++ 文字列定数として含めるための標準準拠の手法が必要です。アイデア?

EDIT : Potatoswatter が指摘したように、上記の動作は定義されていないため、文字列化されるファイルに触れることを含まない真のクロスコンパイラ プリプロセッサ手法はおそらく不可能です (凶悪なほとんど/すべてのコンパイラで機能するハック回答ポイントを取得します)。興味深いことに、ここでの 2 番目の応答で提案されていることを実行することになりました...つまり、STRINGIFY含めていた OpenCL ファイルにマクロを直接追加しました。

somefile.cl

STRINGIFY(
  ... // Lots of OpenCL code
)

somefile.cpp

#define STRINGIFY(src) #src

inline const char* Kernels() {
  static const char* kernels =
    #include "somefile.cl"
    ;
  return kernels;
}

これは、私が試したコンパイラ (マクロ内にプリプロセッサ ディレクティブがないため、Clang と GCC も同様) で機能し、少なくとも私のコンテキストでは負担が大きすぎません (つまり、そうではありません)。 t は、OpenCL ファイルの構文の強調表示/編集を妨げません)。このようなプリプロセッサ アプローチの特徴の 1 つは、隣接する文字列が連結されるため、次のように記述できることです。

inline const char* Kernels() {
  static const char* kernels =
    #include "utility_functions.cl"
    #include "somefile.cl"
    ;
  return kernels;
}

STRINGIFY マクロが両方のファイルにある限り.cl、文字列が連結され、OpenCL コードをモジュール化できます。

4

4 に答える 4

4

標準の最も関連性の高い部分は、§16.3/10 です。

最も外側の対応する括弧で区切られた一連の前処理トークンは、関数のようなマクロの引数のリストを形成します。リスト内の個々の引数はコンマ前処理トークンで区切られていますが、一致する内側の括弧の間のコンマ前処理トークンは引数を分離しません。(引数置換の前に) いずれかの引数が前処理トークンで構成されていない場合、動作は未定義です。引数のリスト内に、前処理ディレクティブとして機能する一連の前処理トークンがある場合、動作は未定義です。

キーポイントの抽出:

  • ファイル内のすべてのコンマ文字が別の引数を導入しているとマクロが認識しないように、ヘッダー ファイルを一対の括弧で囲む必要があります。これらの括弧も文字列化されますが、回避するのは難しくありません。
  • 引数#includeリストを入れることは公式には未定義の動作であるため、これは移植できません。コンパイラは、結果の文字列を にするかどうかを公式には知りません"#include \"kernels/util.cl\""
于 2011-06-28T06:35:14.057 に答える
1

従来の手法は、bin2c のようなプログラムを使用しており、通常は急いで作成されます。別の方法は、GNU binutils から objcopy を使用することです。

$ objcopy -I binary extensions.cfg -O elf32-little -B i386 --rename-section .data=.rodata extensions.o
$ objdump -x extensions.o

extensions.o:     file format elf32-i386
extensions.o
architecture: i386, flags 0x00000010:
HAS_SYMS
start address 0x00000000

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .rodata       00000447  00000000  00000000  00000034  2**0
                  CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
00000000 l    d  .rodata        00000000 .rodata
00000000 g       .rodata        00000000 _binary_extensions_cfg_start
00000447 g       .rodata        00000000 _binary_extensions_cfg_end
00000447 g       *ABS*  00000000 _binary_extensions_cfg_size

-O フラグと -B フラグは、コンパイルされたオブジェクト ファイルの 1 つに対する objdump 出力と一致してリンカーを満足させる必要がありますが、セクションの名前変更は、このデータが読み取り専用であることを実行時リンカーに通知するためのものです。シンボル、開始アドレス、終了アドレス、およびデータ サイズへのマッピングに注意してください。それらはそれぞれアドレスとしてカウントされるため、C では次のように使用します。

extern const char _binary_extensions_cfg_start, _binary_extensions_cfg_end;
extern const char _binary_extensions_cfg_size;
for (const char *p=&_binary_extensions_cfg_start; p<&_binary_extensions_cfg_end; p++)
    do_something(p);
memcpy(somewhere, &_binary_extensions_cfg_start, (intptr_t)&_binary_extensions_cfg_size);

どちらもあなたが求めているプリプロセッサではないことはわかっていますが、それを行うように設計されていません。それでも、それが可能かどうか知りたいです。

于 2011-06-28T06:23:11.150 に答える
0

そのようにはできません。それがclangで機能したことに驚いています。バイナリに直接テキスト ファイルを含めたい場合は、いくつかのオプションがあります。

1: .cl ファイルを文字列を定義する .cpp ファイルに変換するコンパイルの前に実行される前処理。

2: コンパイル済みの実行可能ファイルにコンパイラ固有のストレージを介して文字列データを格納します。これは、Visual Studio のようなツールに、Windows アプリケーションのビルドに使用される .rc、.ico、およびその他のファイルを含める方法です。もちろん、前述のとおり、これらはコンパイラ固有のものです。

最も安全な方法はオプション 1 です。

于 2011-06-28T06:03:18.983 に答える
-2

xcode Cで私がしていることは次のとおりです。

const char oursource[60000];
const char * oursourceptr = oursource;
const char * * oursourceptrptr = & oursourceptr;

// in function "readfileintostring":

char *fylnm = "/Developer/projs/myproj/mykernel.cl";

long enby; short pathref;

FSRef dink; FSPathMakeRef( (const UInt8 *) &fylnm, &dink, NULL );
SInt16 forkRefNum;  HFSUniStr255 dataForkName;  FSGetDataForkName(&dataForkName);
FSOpenFork( &dink, dataForkName.length, dataForkName.unicode, fsRdPerm, (FSIORefNum *) &pathref );

enby = 100000;  FSRead( pathref, &enby, (void *) oursourceptr );

// .. then later ..

program = clCreateProgramWithSource(context, 1, (const char **) oursourceptrptr, NULL, &err);

...プリプロセッサブードゥーではありませんが、私にとってはうまくいきます.clファイルで強調表示された構文を見ることができ、.clを.cにコピーし、1つの#defineを変更して、xcode Cとして実行することもできます. ....

于 2011-07-22T16:37:24.477 に答える