C ++: Может ли макрос расширять «abc» на «a», «b», «c»?

Я написал вариационный шаблон, который принимает переменное число параметров char , т. Е.

 template  struct Foo; 

Мне просто интересно, были ли какие-либо макро-трюки, которые позволили бы мне создать экземпляр этого синтаксиса, подобный следующему:

 Foo 

или

 Foo 

или

 Foo 

и т.п.

В принципе, все, что мешает вам писать персонажи индивидуально, например

 Foo 

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

Я создал его сегодня и тестировал на GCC4.6.0.

 #include  #define E(L,I) \ (I < sizeof(L)) ? L[I] : 0 #define STR(X, L) \ typename Expand \ cstring<>, sizeof L-1>::type #define CSTR(L) STR(cstring, L) template struct cstring { }; template class P, typename S, typename R, int N> struct Expand; template class P, char S1, char ...S, char ...R, int N> struct Expand, cstring, N> : Expand, cstring, N-1>{ }; template class P, char S1, char ...S, char ...R> struct Expand, cstring, 0> { typedef P type; }; 

Некоторое испытание

 template struct Test { static void print() { char x[] = { S... }; std::cout << sizeof...(S) << std::endl; std::cout << x << std::endl; } }; template void process(cstring) { /* process C, possibly at compile time */ } int main() { typedef STR(Test, "Hello folks") type; type::print(); process(CSTR("Hi guys")()); } 

Поэтому, пока вы не получаете 'a', 'b', 'c' , вы все равно получаете строки времени компиляции.

Прошло много испытаний, но в конечном итоге это обречено на провал, я думаю.

Чтобы понять, почему, нужно понять, как работает препроцессор. Вход препроцессора можно рассматривать как stream. Этот stream сначала преобразовывается в токеты предварительной обработки (список доступен на языке программирования C ++, 3-е издание, грамматика Приложения А, стр. 795)

На этих жетонах препроцессор может применять только очень ограниченное количество операций, кроме данных о диграммах / триграммах, эту сумму:

  • (для заголовков), это может не отображаться в макросе, насколько я знаю
  • макрозамена (что чрезвычайно сложно: p)
  • # : преобразует токен в токен -литеральный токен (окружая его кавычками)
  • ## : объединяет два токена

Вот и все.

  • Нет инструкции препроцессора, которая может разделить токен на несколько токенов: это макроподстановка, что означает, что на самом деле есть макрос, определенный в первую очередь
  • Нет инструкции препроцессора для преобразования строкового литерала в обычный токен (удаление котировок), который затем может быть заменен макрозаменой.

Поэтому я утверждаю, что это невозможно (либо в C ++ 03, либо в C ++ 0x), хотя для этого могут быть (возможно) специальные расширения для компилятора.

Решение, основанное на ответе Сильвана Defresne выше, возможно в C ++ 11:

 #include  #include  template  constexpr char get_ch (char const (&s) [N], unsigned int i) { return i >= N ? '\0' : s[i]; } #define STRING_TO_CHARS_EXTRACT(z, n, data) \ BOOST_PP_COMMA_IF(n) get_ch(data, n) #define STRING_TO_CHARS(STRLEN, STR) \ BOOST_PP_REPEAT(STRLEN, STRING_TO_CHARS_EXTRACT, STR) // Foo  // expands to // Foo <'a', 'b', 'c'> 

Кроме того, если данный шаблон способен обрабатывать несколько завершающих символов «\ 0», мы можем облегчить требование длины в пользу максимальной длины:

 #define STRING_TO_CHARS_ANY(STR) \ STRING_TO_CHARS(100, STR) // Foo  // expands to // Foo <'a', 'b', 'c', '\0', '\0', ...> 

Вышеприведенные примеры правильно компилируются на clang ++ (3.2) и g ++ (4.8.0).

это использовалось для работы в ранней версии msvc, я не знаю, все ли это делает:

 #define CHAR_SPLIT(...) #@__VA_ARGS__ 

К сожалению, я считаю, что это невозможно. Лучшее, что вы можете получить от препроцессора, обеспечивается Boost.Preprocessor , в первую очередь через его типы данных:

  • array : синтаксис будет (3, (a, b, c))
  • list : синтаксис будет (a, (b, (c, BOOST_PP_NIL)))
  • sequence : синтаксис будет (a)(b)(c)
  • tuple : синтаксис будет (a, b, c)

Из любого из этих типов вы можете легко создать макрос, который будет создавать список разделенных запятыми элементов с одной BOOST_PP_SEQ_ENUM (см., Например, BOOST_PP_SEQ_ENUM ), но я считаю, что ввод этого макроса должен быть одним из этих типов, и все требуют, чтобы символы вводились индивидуально.

Основываясь на том, что я обсуждал выше, может потребоваться следующий ужасный шаблонный хакер, чтобы снять это. Я не тестировал это (извините!), Но я уверен, что это или что-то близкое к нему может сработать.

Первым шагом является создание classа шаблонов, который просто содержит кортеж символов:

 template  class CharTuple {}; 

Теперь давайте построим адаптер, который может преобразовать строку стиля C в CharTuple. Для этого нам понадобится следующий вспомогательный class, который по существу представляет собой недостатки типа LISP для кортежей:

 template  class Cons; template  class Cons> { typedef CharTuple type; } 

Предположим также, что мы имеем оператор meta-if:

 template  class If { typedef typename TrueType::type type; }; template  class If { typedef typename FalseType::type type; }; 

Тогда следующее должно позволить вам преобразовать строку стиля C в кортеж:

 template  class Identity { typedef T type; }; template  class StringToChars { typedef typename If<*str == '\0', Identity>, Cons<*str, typename StringToChars::type>>::type type; }; 

Теперь, когда вы можете преобразовать строку C-стиля в кортеж символов, вы можете упорядочить строку ввода через этот тип, чтобы восстановить кортеж. Тем не менее, нам нужно сделать немного больше механизмов для этого. Разве это не TMP весело? 🙂

Первый шаг – взять исходный код:

 template  class Foo { /* ... */ }; 

и используйте некоторую специализированную специализацию, чтобы преобразовать ее в

 template  class FooImpl; tempalte  class FooImpl> { /* ... */ }; 

Это просто еще один слой косвенности; больше ничего.

Наконец, вы должны это сделать:

 template  class Foo { typedef typename FooImpl::type>::type type; }; 

Я действительно надеюсь, что это сработает. Если это не так, я все же думаю, что это стоит публикации, потому что это, вероятно, ε-близко к действительному ответу. 🙂

Основано на решении user1653543 выше.

Некоторая магия шаблона:

 template  constexpr char getch (char const (&s) [N], unsigned int i) { return i >= N ? '\0' : s[i]; } template struct split_helper; template struct split_helper { typedef push_front_t::type, char_> type; }; template struct split_helper<'\0', Cs...> { typedef std::integer_sequence type; }; template using split_helper_t = typename split_helper::type; 

Некоторое волшебство PP:

 #define SPLIT_CHARS_EXTRACT(z, n, data) \ BOOST_PP_COMMA_IF(n) getch(data, n) #define STRING_N(n, str) \ split_helper_t #define STRING(str) STRING_N(BOOST_PP_LIMIT_REPEAT, str) 

split_helper просто вспомогательный, чтобы обрезать конечные нули. Теперь STRING("Hello") представляет собой типизированную последовательность символов времени компиляции ( std::integer_sequence ). Длина строковых констант составляет до BOOST_PP_LIMIT_REPEAT символов.

Домашнее задание : реализовать push_front_t и c_str для получения c_str с нулевым завершением std::integer_sequence . (Хотя, вы можете попробовать использовать Boost.MPL)

  • Нечувствительная к регистру строка в C
  • Заменить часть строки другой строкой
  • Java String не работает
  • C ++ преобразует строку в шестнадцатеричную и наоборот
  • Реверсирование строки с рекурсией в Java
  • C оптимизация строковых литералов
  • Перенаправить вывод консоли в строку в java
  • Как получить количество символов в std :: string?
  • Начальная емкость ArrayList и IndexOutOfBoundsException
  • Загрузите модуль node.js из строки в памяти
  • Как реализована функция GetHashCode () из строки C #?
  • Давайте будем гением компьютера.