Макрос для подсчета (символьных) аргументов - PullRequest
4 голосов
/ 08 августа 2011

У меня есть макрос для преобразования строки в список символов:

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

например. использование: «CHAR_LIST_7 (курица)» дает «c», «h», «i», «c», «k», «e», «n», поэтому его можно использовать в таких вещах, как шаблоны (например: http://hpaste.org/47313/exand)

Однако я хотел бы обобщить это для любого количества символов (и не нужно вручную подсчитывать количество символов)? Так что я мог бы просто пойти: CHAR_LIST (произвольный текст). Есть идеи или решения?

Ответы [ 3 ]

3 голосов
/ 08 августа 2011

Вы не можете разделять токены во время предварительной обработки, вы можете только объединить их (используя ##).

Преобразование идентификатора в строковый литерал не поможет, поскольку вы не можете разбить строковый литерал на части во время предварительной обработки, а также не можете выполнять операции (например, вычислять длину) над строковым литералом.

Во время предварительной обработки компилятор знает, что токен является строковым литералом и каким он является литералом, но он еще не знает своего полного типа и длины, по крайней мере, не таким образом, который доступен для макроса.

1 голос
/ 08 августа 2011

Нет, как уже сказал Джеймс, в препроцессоре нет способа разделить токены или узнать длину строки.

Но я думаю, что для вашего случая использования это вообще не нужно. Строка, которую вы получили бы при строковом аргументе #x, представляет собой строку постоянного размера, например, chicken приводит к "chicken", который просто имеет тип char[8]. Длина такой строки является постоянной времени компиляции, и вы просто можете определить ее с помощью sizeof:

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

Использование такой вещи в C "просто" выглядит

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

Поскольку TOKLEN(TOK) является константой времени компиляции, компилятор может развернуть ее, если необходимо.

Чтобы использовать это в вашем случае для 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)

(не проверено, мой C ++ ржавый)

и теперь с

defConstLenString(chicken, chick);

chick.n - это константа, которая может быть границей цикла for или чего-либо еще, и компилятор должен иметь возможность оптимизировать все идеально.

1 голос
/ 08 августа 2011

Способ, которым вы просите разделить, в общем виде невозможен.Лучший способ - предоставить аргумент как template:

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

Я знаю, что это будет немного больше печатать.Чтобы преодолеть это, вы можете написать простую программу, которая принимает строковый литерал типа chicken в качестве аргумента и выводит его в 'c', 'h', 'i', ... и т. Д.что-то вроде:

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;
}

Тем не менее, нет решения для многословия из-за разделения символов в аргументе template.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...