8

Here's a program that illustrates my problem:

#include <stdio.h>

#define NUMERATOR 8
#define DENOMINATOR 2
#define QUOTIENT (NUMERATOR / DENOMINATOR)

#define ZSTR(x) XSTR(#x)
#define YSTR(x) XSTR(x)
#define XSTR(x) STR(x)
#define STR(x) #x

int main()
{
    printf("QUOTIENT:       %d\n", QUOTIENT);
    printf("STR(QUOTIENT):  %s\n", STR(QUOTIENT));
    printf("XSTR(QUOTIENT): %s\n", XSTR(QUOTIENT));
    printf("YSTR(QUOTIENT): %s\n", YSTR(QUOTIENT));
    printf("ZSTR(QUOTIENT): %s\n", ZSTR(QUOTIENT));
    return 0;
}

And here's its output:

$ gcc -g -Wall -o stringify stringify.c && ./stringify 
QUOTIENT:       4
STR(QUOTIENT):  QUOTIENT
XSTR(QUOTIENT): (8 / 2)
YSTR(QUOTIENT): (8 / 2)
ZSTR(QUOTIENT): "QUOTIENT"

I would like to have a the string literal "4" passed to the compiler, but I'm losing hope. This is related to this question, but adds a level.

Community
  • 1
  • 1
nmichaels
  • 49,466
  • 12
  • 107
  • 135
  • 2
    I don't think you can do that... the `#` operator works on a preprocessor token not an expression so I'd not be at all surprised if there is no way to do this whatsoever. – Spudd86 Aug 19 '11 at 19:22
  • @Spudd86: Yeah, I was just hoping there was a way to do math in the preprocessor and that someone here would know about it. – nmichaels Aug 19 '11 at 19:28
  • you CAN do math in the preprocessor, you just can't stringize the result `#if 0 == 1 - 1` will result in true – Spudd86 Aug 19 '11 at 19:34
  • @Spudd86: Okay, so I was hoping there was a way for me to use the result of math done in the preprocessor. Maybe I should `#if QUOTIENT != 4 #error`. – nmichaels Aug 19 '11 at 19:48
  • Related question http://stackoverflow.com/questions/1275004/how-to-stringify-an-expression-in-c – Joe Aug 19 '11 at 20:16

4 Answers4

4

You can define macros that paste together their arguments and then define a (large) number of other macros that do the evaluation as kind of a table lookup:

#define DIV(X, Y)  DIV_(X, Y)
#define DIV_(X, Y) DIV_##X##_##Y
#define DIV_0_1  0
#define DIV_1_1  1
#define DIV_2_1  2
    :
#define DIV_8_2  4
    :

This is kind of tedious, but you can easily write a little program to generate a header file with the above stuff in it and run that as part of your build process. Then you just need

#define QUOTIENT  DIV(NUMERATOR, DENOMINATOR)

Note that his kind of thing only works for unsigned integers -- if you need negative numbers or floating point, it won't work

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
3

With some tricks you can implement basic arithmetic in a C99 conforming preprocessor. P99 implements arithmetic and logic for small decimal numbers. E.g

P99_IF_GT(1,0)(true)(false)
P99_ADD(3, 7)
P99_MUL(7, 2)
P99_DIV(7, 2)

would be preprocessed to something like

1
10
14
3

These macros can be processed further, stringified and everthing you like.

P99_STRINGIFY(P99_PASTE2(XXX_, P99_ADD(3, 7)))

leads to "XXX_10" as the result of preprocessing.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • 1
    You're making me feel bad about wanting the preprocessor to do this stuff for me. (Un?)fortunately, I can't use a C99 conforming preprocessor for this without rewriting all my build scripts. – nmichaels Aug 19 '11 at 20:38
  • Actually, you only need a C89 conformant preprocessor to do this kind of op-by-macro-paste stuff from P99. Other P99 stuff requires C99 – Chris Dodd Aug 19 '11 at 20:59
  • @Chris, this heavily uses `__VA_ARGS__`, no? This is a C99 feature. Many compilers implement this as an extension to C89, the P99 implementation uses all tricks of the prescribed evaluation order of parameters a such things, so they'd better implement that part of C99 completely. – Jens Gustedt Aug 19 '11 at 21:16
  • Forgive my ignorance... Where is the body for `P99_ADD` and friends? – jww Feb 27 '18 at 20:18
  • @jww, what exactly do you want to know? Where to find the source of P99? – Jens Gustedt Feb 27 '18 at 22:39
  • What is the body. How do we make it happen? How do we add the numbers? – jww Feb 27 '18 at 22:49
  • By body, you mean the definition? Did you see the link to the source? – Jens Gustedt Feb 28 '18 at 07:08
2

The best you can do is stringify the expansion of the macro which is done with your XSTR and YSTR examples. Although it may compile to 4 with optimizations all the preprocessor will be able see is (8 / 2)

Joe
  • 56,979
  • 9
  • 128
  • 135
2

Well, I'm a little reluctant to admit I know one way to make this work.

Attack it from the other direction. You want your compiler to see something like this.

#define QUOTIENT 4
#include <stdio.h>

int main(void)
{
  printf("QUOTIENT:       %d\n", QUOTIENT);
  return 0;
}

How do we do that without using a literal "4" in the definition of QUOTIENT, since the macro processor won't help us? By using an additional preprocessor. Write a source file, stringify.c.awk, like this.

/* stringify.c.awk --  Source file for stringify.c  
   (Put build instructions here.)  
 */
#define QUOTIENT NUMERATOR/DENOMINATOR
#include <stdio.h>

int main(void)
{
  printf("QUOTIENT:       %d\n", QUOTIENT);
  return 0;
}

Write the secondary preprocessor in awk. I deliberately used a really tight regular expression. I think it's the most likely regex to fail if there are changes to the source file, and I think that's usually what you want. (I usually want to discourage cosmetic changes to the #define.)

# stringify.awk -- calculate and substitute the value for #define QUOTIENT.
BEGIN {
  NUMERATOR = 8;
  DENOMINATOR = 2;
}
{
  if ($0~/^#define QUOTIENT NUMERATOR\/DENOMINATOR$/) {
    sub(/NUMERATOR\/DENOMINATOR/, NUMERATOR/DENOMINATOR);
  } 
  print $0;
}

Now you can build stringify.c from the stringify.c.awk file.

$ awk -f stringify.awk stringify.c.awk > stringify.c
$ gcc -Wall -o stringify stringify.c
$ ./stringify
QUOTIENT:       4

A makefile and generous comments takes a lot of the pain away.

(m4 won't help for more or less the same reasons the C preprocessor won't help.)

Mike Sherrill 'Cat Recall'
  • 91,602
  • 17
  • 122
  • 185
  • 1
    Wow, this is like a contest for things we shouldn't do while writing software. I'll fix it in documentation. +1 for reluctance. – nmichaels Aug 22 '11 at 12:25