nodakai
nodakai

Reputation: 8021

C preprocessor: How to create a character literal?

Just out of curiosity, I'd like to know if it is possible to define a macro which can turn its argument into a character literal:

 switch(getchar()) {
   case MYMACRO(A): printf("Received A\n"); break;
   case MYMACRO(a): printf("Received a\n"); break;
   case MYMACRO(!): printf("Received an exclamation mark\n"); break;
   default: printf("Neither a nor A nor !\n"); break;
 }

Two possible solutions off the top of my head:

Enumerating all characters

#define LITERAL_a 'a'
#define LITERAL_b 'b'
...
#define MYMACRO(x) LITERAL_ ## x

It doesn't work with MYMACRO(!) because ! is not a valid component of a C identifier.

Convert the parameter into a string literal

#define MYMACRO(x) #x [0]

It involves a pointer dereference and is invalid in places like a case label.

I'm not asking for a way to "improve" the above switch statement itself. It's just a toy example. Repeat. It's just a toy example.

Upvotes: 15

Views: 7624

Answers (3)

T. J. Evers
T. J. Evers

Reputation: 453

Microsoft has the charizing operator #@ but that's compiler specific.

I found that *# works in both VS and gcc (and probably more) but I can't for the life of me remember where I got it from.

#define MAKECHAR(x)  *#x

causes the statement

a = MAKECHAR(b);

to be expanded to

a = 'b';

Upvotes: 1

Eph
Eph

Reputation: 1321

While I couldn't get user4098326's answer to compile, I did get the solution below to compile and work as expected (in Code Composer Studio). The key was to use the symbol concatenation operator. Note however, that according to the standard, this should not work. A single quote (') is not a valid token nor is a single quote followed by a single character ('a). Thus those should not be able to be the input nor output of the concatenation operator. Thus, I would not recommend actually using this solution.

#define CONCAT_H(x,y,z) x##y##z
#define SINGLEQUOTE '
#define CONCAT(x,y,z) CONCAT_H(x,y,z)
#define CHARIFY(x) CONCAT(SINGLEQUOTE , x , SINGLEQUOTE )

#define DO_CASE(...) case CHARIFY(__VA_ARGS__): printf("Got a " #__VA_ARGS__ "\n"); break

Then:

switch(getchar()) {
   DO_CASE(A);
   DO_CASE(a);
   DO_CASE(!);
   default: printf("Neither a nor A nor !\n"); break;
 }

Upvotes: 5

user4098326
user4098326

Reputation: 1742

Here's my possible solution:

#define EVAL(...) __VA_ARGS__
#define Q() '
#define MYMACRO(...) Q()EVAL(__VA_ARGS__)Q()

(Variadic macros are used to to support MYMACRO(,) because it would be parsed as two empty arguments.)

I'm not sure if this code is standard-conformant due to the unmatched '. Still, I think this code works on most C99 compilers. However, this code does not work for the following characters:

  • ( which has to match with )
  • ) used to identify the start and end of the argument list
  • ' and " used for string literals and character constants
  • \, which needs escaping
  • Whitespace characters, because they are not tokens

I'm fairly sure that there's no solution that work for (, ), ' or ", because if this was allowed, the compiler would have to change the way macros arguments are parsed.

Upvotes: 2

Related Questions