Why string concat macro doesn't work for this

2019-05-07 04:46发布

问题:

Short question:

Is it permitted to concat special signs such as +, - for the string concatenation macro ##? For example,

#define OP(var) operator##var

will OP(+) be expanded to operator+?

Exact problem:

#include "z3++.h"
#include <unordered_map>

namespace z3 {
z3::expr operator+(z3::expr const &, z3::expr const &);
}

typedef z3::expr (*MyOperatorTy)(z3::expr const &, z3::expr const &);

#define STR(var) #var
#define z3Op(var) static_cast<MyOperatorTy>(&z3::operator##var)
#define StrOpPair(var) \
  { STR(var), z3Op(var) }

void test() {
  std::unordered_map<std::string, MyOperatorTy> strOpMap1{
      {"+", static_cast<MyOperatorTy>(&z3::operator+)}};  // fine
  std::unordered_map<std::string, MyOperatorTy> strOpMap2{StrOpPair(+)}; // error
}

For strOpMap2, using clang++ -c -std=c++11, it reports:

error: pasting formed 'operator+', an invalid preprocessing token

while using g++ -c -std=c++11, it gives:

error: pasting "operator" and "+" does not give a valid preprocessing token

By reading the manual by gcc I find it should be possible to concat, but why both compilers emit errors?

回答1:

You can paste punctuation to form other punctuation, e.g.

#define PASTE(a,b) a##b

int main()
{
     int i = 0;
     i PASTE(+,+);
     // i == 1 now
}

The ## operator is for producing a valid preprocessing token from other preprocessing tokens. The result of pasting must be a valid preprocessing token. So this is not valid:

PASTE(i,++)

because i++ is not a preprocessing token; it's two adjacent tokens i and ++.

The list of possible tokens is (N3797):

  • header-name
  • identifier
  • pp-number
  • character-literal
  • user-defined-character-literal
  • string-literal
  • user-defined-string-literal
  • preprocessing-op-or-punc
  • each non-white-space character that cannot be one of the above

Note: at the preprocessing stage, keyword does not exist; but after preprocessing, any identifiers which should be keyword are converted (semantically) into keywords. So you can build keywords by pasting shorter words.

In your code, operator+ is two tokens: operator and +. So you do not build it with ##; you just do one then the other.

#define OP(punc) operator punc