2010-03-31

Raw String Literalとプリプロセッサ

行連結は Raw string literal を解釈するフェーズより前のはずなんだが、
どうなってるんだろう?

いい質問だ。日本もまだまだ捨てたものではない。惜しむらくは、そこまでわかっていながら、なぜドラフトやペーパーを読まないのか。

そこは、なかなか面白い。簡単に言えば、Phase 3で、Raw String Literalに限っては、Phase1, 2で加えられたプリプロセッサによる変換の、「逆変換」を行うのだ。

2.2 Phases of translation [lex.phases] p3

Within the r-char-sequence of a raw string literal, any transformations performed in phases 1 and 2 (trigraphs, universal-character-names, and line splicing) are reverted.

もちろん、ユーザーは、このような規格上の定義は、気にする必要はない。ただ、Raw String Literal内では、プリプロセッサによる変換が行われないと考えておけば、それで問題はない。詳細は、Language lawerの仕事だ。

N3077: Alternative approach to Raw String issues

追記:C++の規格というのは、規格の定義する意味と同じであれば、どのような実装をしても構わないものである。だから、Raw String Literalであることを認識する、賢いプリプロセッサがあってもいいし、実際、ペーパーによれば、多くのコンパイラの実装では、この変換を、覚えているらしい。

Apparently many compilers already keep track of those transformations internally.

6 comments:

Takuya said...

char const *str = R"XY(test)X\
Y";//)X\
Y";

が str = "test"; になるのか str = "test)X\\\nY\";//"; になるのかちょっと判断つかないのですがどうなんでしょう

江添亮 said...

phase 3で、逆変換が起こるのは、r-char-sequenceの中だけです。

raw-string:
" d-char-sequence opt ( r-char-sequence opt ) d-char-sequence opt "

つまり、d-char-sequenceには、相変わらず、phase 1, 2は適用されます。

また、一行コメントの文末にバックスラッシュは、もちろん、改行の除去の対象です。

つまり、

char const *str = R"XY(test)X\
Y";//)X\
Y";

は、phase 3を終えた後には、

char const *str = R"XY(test)XY";//)XY";

という形に変換されているので、strは、"test"になります。

Takuya said...

ありがとうございます。すると、

・ r-char-sequence の中に delimiter として使われている「) d-char-sequence_opt "」が出現してはならない

という規則は正確には

・phase 1, 2 の後、 phase 3 の逆変換が行われる前の状態 (r-char-sequense の中も「バックスラッシュ改行」が除去されている状態) で上の規則が成立する

つまり、生のソースコードで言うと、出現してはならないのは「) d-char-sequence_opt "」だけに限らず、
「) d-char-sequence_opt "」に「バックスラッシュ改行」を有限個挿入したものも出現してはならないということになるわけですね。

江添亮 said...

ん? こういうことですか?

R"delimiter( )deli\
miter )delimiter" ;

これは、")delimiter"として、認識されると思いますが。
あ、でもどうでしょう。ちょっとこれは、調べます。

江添亮 said...

ああ、思い出した。この問題は、今まさに、議論されていたのでした。

江添亮 said...

うへ、これ、ill-formedです。
つまり、
「) d-char-sequence_opt "」に「バックスラッシュ改行」を有限個挿入したものも出現してはならない
というのは、正しいようです。
気がつかなかった。