4

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]

e.g. usage: "CHAR_LIST_7(chicken)" gives "'c', 'h', 'i', 'c', 'k', 'e', 'n'" so it can be used in things like templates (e.g.: http://hpaste.org/47313/exand )

However, I would like to generalize this for any amount of characters (and not need to manually have to count the amount of characters)? So I could simply go: CHAR_LIST(arbitrary text). Any ideas or solutions ?

Heptic
  • 3,076
  • 4
  • 30
  • 51
  • Out of curiosity: What do you need it for? – Tomek Szpakowicz Aug 08 '11 at 06:29
  • 2
    Apparently you're past the abuse limit of C++ metaprogramming abilities (it doesn't really takes long to get there, btw). Do you realize that the problems you are facing are not real but ghosts created by the deliberate decision of putting your mind in that small cage of C++ metaprogramming limits? If you do this for fun why not going all way down to generating C++ using brainf**ck instead? – 6502 Aug 08 '11 at 06:32
  • @6502, I think this question came from answer (given by me) [elsewhere on SO](http://stackoverflow.com/a/6191216/146041). Love your comment, but I come to SO precisely to battle with ghosts, I like to leave my pragmatism behind when I visit SO :-) – Aaron McDaid Jul 11 '15 at 15:13

3 Answers3

4

You can't split tokens during preprocessing, you can only combine them (using ##).

Converting the identifier to a string literal won't help either as you can't string split a string literal apart during preprocessing, nor can you perform operations (e.g. compute length) on a string literal.

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

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • It's not really a string, though, is it? It's an identifier. (Edit: I was thinking that would help in finding a solution, but I can't come up with anything. Perhaps this _is_ impossible...) – Chris Lutz Aug 08 '11 at 04:54
  • Oh, I got distracted by the `#x` in the first line of the OP's code. It doesn't really matter, though: the answer is the same. – James McNellis Aug 08 '11 at 04:55
  • 1
    I'm not up on my C++0x - can the output of a function declared as `constexpr` be given as an argument to a template? – Chris Lutz Aug 08 '11 at 04:58
  • @Chris Lutz, highly doubt it. I can't even give a string "blah" to the template (i have to give it like 'b', 'l', 'a', 'h') – Heptic Aug 08 '11 at 05:00
  • @Chris: Sure you can (given `template constexpr unsigned static_string_length(const T(&)[N]) { return N - 1; } template struct S { enum { value = N }; };`, the value `S::value` is `5`), but a string literal still cannot be used as a template argument. – James McNellis Aug 08 '11 at 05:07
  • @James, I'm not entirely convinced there's no way to count-letters in a macro, but if no one can answer -- I'll assume that's the case and mark yours the solution – Heptic Aug 08 '11 at 05:23
  • @Heptic - If you can get them split the rest is easy. I'm scouring Jens Gustedt's P99 library (http://p99.gforge.inria.fr/p99-html/index.html) for an idea, but nothing so far. – Chris Lutz Aug 08 '11 at 05:51
  • @Chris, I don't think that there is a way to do this in the preprocessor. But I also really don't see a need for this, looks artificial to me. I'll give an answer, not to concur with James', but to explain a bit more. – Jens Gustedt Aug 08 '11 at 06:23
  • @Jens: Thank you for all of the excellent information and code you've posted on the P99 website (I've seen a lot of your posts here on Stack Overflow, but didn't realize you were the author of P99). – James McNellis Aug 08 '11 at 15:56
1

The way you ask for splitting is not possible in general form. The best way is to provide the argument as it's to the template:

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

I know that it will be a bit more of typing. To overcome that you can write a simple program, which takes a string literal like chicken as argument and outputs it into 'c','h','i',...etc. something like:

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 due to split-ted characters in your template argument.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • I think that you can abuse multi-bytes characters to pass the characters 4 at a time... not so pretty though :/ – Matthieu M. Aug 08 '11 at 10:09
1

No, as James already said, there is no way to split tokens in the preprocessor or to have it to know the length of a string.

But I think that for your use case this is not necessary at all in any case. A string that you would get from stringifying your argument with #x is a constant sized string, e.g chicken leads to "chicken" which simply has type char[8]. The length of such a string is a compile time constant and you simply can detect it with sizeof:

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

Usage of such a thing in C would "simply" look

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

Because TOKLEN(TOK) is a compile time constant the compiler could unroll this if appropriate.

To use that in your 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 bound of a for loop or whatever and the compiler should be able to optimize everything perfectly.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177