54

I faced a problem - I need to use a macro value both as string and as integer.

 #define RECORDS_PER_PAGE 10

 /*... */

 #define REQUEST_RECORDS \
      "SELECT Fields FROM Table WHERE Conditions" \
      " OFFSET %d * " #RECORDS_PER_PAGE \
      " LIMIT " #RECORDS_PER_PAGE ";"

 char result_buffer[RECORDS_PER_PAGE][MAX_RECORD_LEN];

 /* ...and some more uses of RECORDS_PER_PAGE, elsewhere... */

This fails with a message about "stray #", and even if it worked, I guess I'd get the macro names stringified, not the values. Of course I can feed the values to the final method ( "LIMIT %d ", page*RECORDS_PER_PAGE ) but it's neither pretty nor efficient. It's times like this when I wish the preprocessor didn't treat strings in a special way and would process their content just like normal code. For now, I cludged it with #define RECORDS_PER_PAGE_TXT "10" but understandably, I'm not happy about it.

How to get it right?

a3f
  • 8,517
  • 1
  • 41
  • 46
SF.
  • 13,549
  • 14
  • 71
  • 107

3 Answers3

84

The xstr macro defined below will stringify after doing macro-expansion.

#define xstr(a) str(a)
#define str(a) #a

#define RECORDS_PER_PAGE 10

#define REQUEST_RECORDS \
    "SELECT Fields FROM Table WHERE Conditions" \
    " OFFSET %d * " xstr(RECORDS_PER_PAGE) \
    " LIMIT " xstr(RECORDS_PER_PAGE) ";"
Matthew T. Staebler
  • 4,756
  • 19
  • 21
  • 4
    is this a recent requirement ? I don't recall such tricks were required last time I used stringification ... – PypeBros Dec 07 '12 at 10:37
  • For further reference, additional description of the mechanics (and nuance) of stringification (GNU CPP) available at http://gcc.gnu.org/onlinedocs/cpp/Stringification.html . – hoc_age May 27 '14 at 16:12
  • 2
    Updated GCC doc link: https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing – eresonance Feb 27 '17 at 20:17
  • 1
    This doc quote explained it to me: "`There is no way to combine an argument with surrounding text and stringize it all together. Instead, you can write a series of adjacent string constants and stringized arguments.`" – Victor Sergienko Dec 12 '17 at 16:17
  • @VictorSergienko: This is nothing new and nothing harmful - string literal concatenation, `char* abc = "A" "B" "C";` is in all respects equivalent to `char* abc = "ABC";` The trick here is how to get the stringized equivalent, `"B"` instead of `'B'` or `B`. – SF. Apr 09 '19 at 12:51
  • #define PE(BZL,YY) printf("ERROR - " #BZL ": result: 0x%08x!", YY); Doing:int variable1=1; PE(Test1,variable1); --> Results printing: ERROR - Test1: result: 0x1! – Joniale Jun 04 '20 at 09:04
6
#include <stdio.h>

#define RECORDS_PER_PAGE 10

#define TEXTIFY(A) #A

#define _REQUEST_RECORDS(OFFSET, LIMIT)                 \
        "SELECT Fields FROM Table WHERE Conditions"     \
        " OFFSET %d * " TEXTIFY(OFFSET)                 \
        " LIMIT " TEXTIFY(LIMIT) ";"

#define REQUEST_RECORDS _REQUEST_RECORDS(RECORDS_PER_PAGE, RECORDS_PER_PAGE)

int main() {
        printf("%s\n", REQUEST_RECORDS);
        return 0;
}

Outputs:

SELECT Fields FROM Table WHERE Conditions OFFSET %d * 10 LIMIT 10;

Note the indirection to _REQUEST_RECORDS to evaluate the arguments before stringifying them.

Mike Weller
  • 45,401
  • 15
  • 131
  • 151
3

Try double escaping your quotes

#define RECORDS_PER_PAGE 10
#define MAX_RECORD_LEN 10

 /*... */
#define DOUBLEESCAPE(a) #a
#define ESCAPEQUOTE(a) DOUBLEESCAPE(a)
#define REQUEST_RECORDS \
      "SELECT Fields FROM Table WHERE Conditions" \
      " OFFSET %d * " ESCAPEQUOTE(RECORDS_PER_PAGE)       \
      " LIMIT " ESCAPEQUOTE(RECORDS_PER_PAGE) ";"

 char result_buffer[RECORDS_PER_PAGE][MAX_RECORD_LEN];

int main(){
  char * a = REQUEST_RECORDS;
}

compiles for me. The token RECORDS_PER_PAGE will be expanded by the ESCAPEQUOTE macro call, which is then sent into DOUBLEESCAPE to be quoted.

Scott Wales
  • 11,336
  • 5
  • 33
  • 30