14

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.

nodakai
  • 7,773
  • 3
  • 30
  • 60
  • I think your question is not self consistent. First you ask for a way to "create a character literal" using a macro. Then you present a possible solution, but which doesn't at all create a character literal, but would just get you a char value. You should adjust the title to actually express what you are after. "Convert x to compile time value equal to 'x'"? Alternatively you can call your "possible solutions" "attempted workarounds" but that depends on what you are after. – Johannes Schaub - litb Feb 20 '16 at 16:08
  • @JohannesSchaub-litb Sorry if my wording was confusing. Well, the first one really produces a character literal, though it's just a [partial function](https://en.wikipedia.org/wiki/Partial_function). As you pointed out, the second one merely produces an integral value and that's not really a "solution". I just presented it so that others don't spend their time on alikes. – nodakai Feb 20 '16 at 16:19
  • 6
    There is a Microsoft-specific solution called _charizing_: https://msdn.microsoft.com/library/91tt6dfs%28v=vs.110%29.aspx . But if you need standard conformity, you will be lost, I fear. – Ctx Feb 20 '16 at 16:25
  • @Ctx Interesting, that suggests there's no standard-conformant ways for it. But I'd like to keep searching regardless platform-specific or not – nodakai Feb 20 '16 at 16:31
  • C does not define character literals. Perhaps you want a character constant. C11 6.4.4.4 – chux - Reinstate Monica Feb 20 '16 at 17:15
  • 3
    A non-partial function is clearly impossible even with a non-standard extension like the one @ctx points at, because whitespace is not a token. Any way, standard C does not provide a solution. – rici Feb 20 '16 at 17:25
  • @rici Good point, that's probably why VC `#@` hasn't been considered for standardization – nodakai Feb 20 '16 at 17:35
  • Certainly code could use `case 'A': ... case '!' ...` instead of `case MYMACRO(A): ...MYMACRO(!): ...`. Is this _the_ application of such a macro or is there another situation you are considering. – chux - Reinstate Monica Feb 20 '16 at 17:44
  • After `case`, code needs a constant and literals in C are not constants. – chux - Reinstate Monica Feb 20 '16 at 17:46
  • 5
    For what it's worth, `#define MYMACRO(x) 'x'` works in GCC when the `-traditional` flag is specified. That will preclude all modern macro definitions, for example variadic macros. (I'm in the camp of those who find `'x'` for character literals sufficiently expressive.) – M Oehm Feb 20 '16 at 18:32
  • @MOehm No it doesn't. Try `MYMACRO(()`. – n. m. could be an AI Feb 21 '16 at 14:44
  • Sure, you just need to surround the character with single quotes like this: `MYMACRO('!')`. Easy! The definition of MYMACRO is left as an exercise for the reader. – n. m. could be an AI Feb 21 '16 at 14:46
  • @n.m.: Touché. I tested it with the given exclam. The `#@` thing doesn't work with a single quote, either. Still, no need to be snide about it. I think I made it clear that I don't see the practical use of such a macro. The "charization" operator has provided us with a good term to feed a search engibe with. I've yet to find a page that shows an actual use of the operator beyond providing an alternative to the single-quoted literal. – M Oehm Feb 21 '16 at 15:13
  • @MOehm But MYMACRO(y) will be 'x' with this definition, too, and not 'y' – Ctx Feb 22 '16 at 22:34
  • 1
    @Ctx: Not when I specify `--traditional`, I've just tried it out again to make sure. (This [answer on the C FAQ](http://c-faq.com/cpp/charize.html) made me try it.) – M Oehm Feb 23 '16 at 06:06
  • @MOehm Ah, ok, I missed that. Interesting, indeed. – Ctx Feb 23 '16 at 10:42
  • I wonder what the use of such a macro is. because `MYMACRO(!)` looks alot more convoluted than let's say `'!'` .. – Alexander Oh Feb 27 '16 at 12:20
  • @Alex It could be combined with other macro operations, _eg._ `#define FOO(x) foo_ ## x (#x, MYMACRO(x))` – nodakai Feb 27 '16 at 16:30

2 Answers2

5

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;
 }
Rick
  • 1,240
  • 14
  • 21
  • ICC doesn't complain about this, but gcc and clang do, any idea which specific `-Wno-*` options are needed to turn off the "missing terminating '" ... already added `-Wno-multichar` which got rid of the multichar constant warning but unfortunately `-Wno-cpp` only eliminates `#warning` directives ... so currently using `-Wp,-w` ... assuming anything that would also give a warning in the preprocessor will give a warning in the compiler (except `#warning` of course, but `-Wp,-w,-Wcpp` doesn't work as you would expect) – technosaurus May 20 '17 at 22:24
  • @technosaurus unfortunately, your guess is better than mine. I only have CCS on my work system. Given that this solution goes against the standards, it's certainly possible that no flags will make it happy. – Rick May 22 '17 at 11:49
2

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.

user4098326
  • 1,712
  • 4
  • 16
  • 20
  • 1
    `gcc -std=c99 -pedantic-errors` verifies that this isn't standard-compliant, and [section 6.4p2](http://www.iso-9899.info/n1256.html#6.4p2) explains why it's not possible to come up with a standard-compliant solution. – autistic Feb 21 '16 at 14:57
  • 1
    I just tested this code today and found that although the expansion result resembles a character constant, it is not recognised by the compiler as such, so a full program like `char a=MYMACRO(a);` does not compile. – user4098326 Feb 22 '16 at 03:00
  • The lexical analysis phase of compilation occurs prior to the macro substitution phase of compilation. – autistic Feb 22 '16 at 13:39
  • 1
    "[...] so a full program like `char a=MYMACRO(a);` [...]" lol. – cadaniluk Feb 23 '16 at 07:27