Macros for counting (character) arguments

I have a macro to convert a string to a list of characters:

#define TO_STRING(x) #x #define CHAR_LIST_7(x) TO_STRING(x)[0] \ , TO_STRING(x)[1] \ , TO_STRING(x)[2] \ , TO_STRING(x)[3] \ , TO_STRING(x)[4] \ , TO_STRING(x)[5] \ , TO_STRING(x)[6] 

eg. Usage: "CHAR_LIST_7 (chicken)" gives "c", "h", "i", "c", "k", "e", "n", so it can be used in things like patterns (for example: http://hpaste.org/47313/exand )

However, I would like to generalize this to any number of characters (and don't need to manually count the number of characters)? So I could just go: CHAR_LIST (free text). Any ideas or solutions?

+4
source share
3 answers

You cannot split tokens during pre-processing, you can combine them (using ## ).

Converting an identifier to a string literal will not help either, since you cannot separate the string literal separately during preprocessing, and you cannot perform operations (for example, the length of the calculations) in the string literal.

During preprocessing, the compiler knows that the token is a string literal and what it is, but it does not yet know its full type and length, at least not in a way that is accessible to the macro.

+3
source

The way you ask for separation is not possible in a general way. The best way is to provide an argument as template :

 Literal<'c','h','i','c','k','e','n'>::print(); 

I know this will print a little more. To overcome this, you can write a simple program that takes a string literal as chicken as an argument and prints it to 'c' , 'h' , 'i' , ... etc. sort of:

 int main(int argc, char **argv) { if(argc != 2) return 0; const char *s = argv[1]; int length = strlen(s)-1; for(int i = 0; i < length; i++) cout<<s[i]<<","; cout<<s[length]<<endl; } 

However, there is no solution for verbosity because of the separated characters in your template argument.

+1
source

No, as James said, there is no way to split tokens in the preprocessor or find out the length of a string.

But I think that for your use case this is not necessary at all. The line that you would fail with your argument with #x is a constant-size string, for example, chicken leads to "chicken" , which simply has type char[8] . The length of such a line is a compile-time constant, and you can simply detect it with sizeof :

 #define TOKLEN(TOK) (sizeof(#TOK)-1) 

Using this kind of thing in C would just look

 #define SCARY(TOK) for (size_t i = 0; i < TOKLEN(TOK); ++i) printf("%c:", #TOK[i]) 

Since TOKLEN(TOK) is a compile-time constant, the compiler can expand it if necessary.

To use this in my use case for C ++

 template < size_t n > class constLenString { size_t const len = n; char const* str; constLenString(char* s) : str(s) { } }; #define defConstLenString(TOK, NAME) constLenString< TOKLEN(TOK) > NAME(#TOK) 

(untested, my C ++ is rusty)

and now with

 defConstLenString(chicken, chick); 

chick.n is a constant that can be the boundary of a for loop or any other, and the compiler should be able to fully optimize everything.

+1
source

All Articles