Reputation: 1786
For example
#define BEGIN_RAW R"(
#define END_RAW )"
const char* foo =
BEGIN_RAW
this is a
raw string
END_RAW;
This doesn't work. It looks like the preprocessor itself resolves the raw string into a string token, or at least attempts to and fails because it doesn't find the closing quote sequence.
What I want is the BEGIN_RAW macro to expand to the R"( and the END_RAW to expand to the )" characters at a point where the parser will recognize the expanded result as a raw literal string.
Upvotes: 3
Views: 100
Reputation: 181179
TL;DR: There is no way to use macros to express individual delimiters of any variety of string literal.
This doesn't work.
For multiple reasons.
It looks like the preprocessor itself resolves the raw string into a string token,
A correct compiler recognizes all of
R"(
#define END_RAW )"
as a raw string literal when it tokenizes the source, before it even reaches the preprocessor. This is because
the preprocessor operates on tokens, identified and classified according to C++ rules, not directly on the characters of the source, and
unlike other string literals, raw string literals can contain literal newlines.
or at least attempts to and fails because it doesn't find the closing quote sequence.
Some compilers (mine, for example), are known to erroneously reject newline-containing raw strings. Perhaps that's what you saw that you describe as the compiler not finding the closing quote. Even so, this is again a manifestation of tokenizing before preprocessing.
And we can almost stop right there, because the fact that the source is tokenized before preprocessing rules out the delimiters of any kind of string literal being expressed as separate macros. The replacement list of any macro needs to be a valid token sequence, and a bare, unmatched double quote does not appear in any valid C++ preprocessing token.
Tokenization also largely rules out using macros to construct a raw string literal containing a newline from anything that is not already a raw string literal, because raw string literals are the only kind of tokens that can contain newlines. Although there is a stringification operator with which a function-like macro can can convert bare token sequences into strings, that process replaces any whitespace, including newlines, between tokens with a single space character in the result.
Similarly, tokenization rules out constructing raw string literals from most other character sequences for which raw strings are interesting in the first place, such as those containing character sequences that look like escape sequences, or those containing unpaired single or double quotes.
Neverthless, it is possible to employ macros' stringification and token pasting operators to construct raw strings from bare tokens. I don't know why you would want to do it, but it could look like this:
#include <iostream>
#define STRINGIFY(tokens) # tokens
#define CONCAT(a, b) a ## b
#define MAKE_RAW(s) CONCAT(R, s)
#define RAW_STRING(tokens) MAKE_RAW(STRINGIFY((tokens)))
#define TEST RAW_STRING( \
this is \
a raw string)
int main() {
std::cout << TEST << std::endl;
}
Output:
$ ./example
this is a raw string
Upvotes: 3