- C/C++ のソース内に、GLSLやHLSL等のシェーダのソースを文字列リテラルとして埋め込んでしまいたい。
- 一行ごとクオーテーションとか改行コードとか書きたくないので、別ファイルに書いたシェーダソースを include したい。
長年できないと思い込んでたけど実は簡単にできた
OpenGL や DirectX のアプリを組む際、シェーダのソースを起動後にファイルで読み込むのは面倒なのでソース内に文字列リテラルとして埋め込んでしまいたいんだけど、C/C++ はソース内に複数行の文字列は書けないので一行ごとクオーテーションでくくるしかなくて、読みにくいしメンテ性も最悪。
不便だなと思いつつもできないと思い込んでたのだけど、久々に調べてみたら実は思いのほか簡単にできたっぽい。
// プレーンテキストを文字列化するマクロ。マクロ名は好きなように。
// __VA_ARGS__ が使えるコンパイラならこの方が使い勝手がいい
#define INLINE_TEXT(...) #__VA_ARGS__
// __VA_ARGS__ が使えないならこれでも概ねOK。ただしマクロ内にカンマが書けない(後述)
#define INLINE_TEXT(str) #str
INLINE_TEXT(
// このマクロ内にシェーダソースを好きに書くがいい!
// ただし制約がいくつか(後述)
); // <-セミコロンをここに書くか、C/C++ソースに書くかはお好みで
// セミコロンをテキスト内に書くならこんな感じ
// セミコロンをこちらに書く場合は、#include の行には書けないので次の行に書く
const char* sShaderSource =
#include "shader_source.hlsl"
この方法を使う場合の制約
マクロ内の改行やタブはスペースに置換される。必要なら \n
や \t
を書けばOK
さらに連続するスペース、改行、タブ等の空白文字はスペース1つにまとめられてしまう。Visual Studio / Android Studio / Xcode とも同挙動。
GLSL や HLSL なら改行等が省かれてもほぼ問題ないけど、ソース内に #define
や #if
などを書く場合には前後に \n
が必要 なのがちょっと残念。あと文章などを改行そのままに埋め込みたいといった用途には使えない。
マクロ内の C/C++ スタイルのコメントは排除される
マクロ内に C/C++ スタイルのコメントがある場合は、それを省いたうえで文字列化 される。プリプロセッサよりも前の段階でまずコメントを排除してるんだろうね。
この仕様はシェーダソースを埋め込む上では好都合で、シェーダソースがコメントごとアプリに含まれてしまうのを回避できるし、シェーダコンパイルもちょっとは速くなる。
マクロ内に # は書けない。\x23
でエスケープすればOK
正確には、# を書くとエラーになるコンパイラが多い。Visual Studio では問題ない。Android Studio はコンパイルエラー。Xcode はエラーになったりならなかったり一部の # だけ読み飛ばされたり死ねばいいのに。結論としてエスケープするのが吉。
マクロ内の左括弧と右括弧は同数でないといけない
マクロ関数である以上、右括弧が現れた時点でマクロ終了なんだけど、マクロ内に左括弧がある場合は、その括弧を閉じるまではマクロの終了判定はされない。コンパイラ依存かもしれんけど、少なくとも Visual Studio, Android Studio, Xcode は同挙動。
// 左括弧と右括弧が帳尻が合っていれば意図通りに文字列化される(赤字が文字列)
INLINE_TEXT( function( (int)a ); )
// 左括弧の方が多い場合、マクロが意図した場所で終わらない
INLINE_TEXT( 例えばこんな :( 顔文字とか )
// 右括弧の方が多い場合、そこでマクロが終わってしまう
INLINE_TEXT( 例えばこんな :) 顔文字とか )
__VA_ARGS__ を使わない場合、マクロ内にカンマは書けない
これまたマクロ関数である以上、カンマが現れたらそこから次の引数なので、__VA_ARGS__
が使えない環境ではエラーになる。__VA_ARGS__
を使った場合はエラーにはならないけど、あまりにカンマ(=引数の数)が多いとエラーになりそうな気はする。
ちなみに、__VA_ARGS__
を使わない場合でも、マクロ内に左括弧があるならその括弧を閉じるまでの間はカンマは解釈されない ので、括弧内のカンマはいくら書いても問題ない。それとカンマを \x2c
でエスケープした場合も当然問題ない。
てことで、括弧やエスケープなしでカンマを多用する文字列だと注意が必要かも。