Stringification of a macro value

2019-01-01 16:34发布

问题:

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?

回答1:

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) \";\"


回答2:

#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.



回答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.