##
doesn't concatenate arbitrary things (especially not strings). What it does it is merges symbols together in the parser into a single symbol.
Let's remove one of those *
to see what's going on:
#include <iostream>
#define TO_STRING_HELPER(x) #x
#define TO_STRING(x) TO_STRING_HELPER(x)
#define CONCAT(x, y) *x##y
int main() {
char *pcCommPort = "COM";
char *num = "5";
std::cout << TO_STRING(CONCAT(pcCommPort, num)) << std::endl;
}
Output:
*pcCommPortnum
What CONCAT
does in this code is:
- Expand
x
into pcCommPort
and y
into num
. This gives the expression *pcCommPort##num
.
- Concatenate the two symbols
pcCommPort
and num
into one new symbol: pcCommPortnum
. Now the expression is *pcCommPortnum
(remember, that last part (pcCommPortnum
) is all one symbol).
- Finish evaluating the full macro as a
*
followed by the symbol pcCommPortnum
. This becomes the expression *pcCommPortnum
. Remember, those are two different symbols: *
and pcCommPortnum
. The two symbols just follow one after the other.
If we were to try to use *x##*y
, what the compiler does is this:
- Expand
x
into pcCommPort
and y
into num
. This gives us the expression *pcCommPort##*num
.
- Concatenate the two symbols
pcCommPort
and *
into one new symbol: pcCommPort*
.
- Here, the preprocessor hits an error: the single symbol
pcCommPort*
is not a valid preprocessing token. Remember, it's not two separate symbols at this point (it is not two symbols pcCommPort
followed by *
). It is one single symbol (which we call a token).
If you want to concatenate two strings, you're way better off using std::string
. You can't* do what you're trying to do with the preprocessor.
*Note, though, that consecutive string literals will be merged together by the compiler (i.e. "COM" "5"
will be merged into a single string "COM5"
by the compiler). But this only works with string literals, so you'd have to #define pcCommPort "COM"
and #define num "5"
, at which point you could do pcCommPort num
(without any further macros) and the compiler would evaluate it to the string "COM5"
. But unless you really know what you're doing, you really should just use std::string
.