C ++: Может ли макрос расширять «abc» на «a», «b», «c»?
Я написал вариационный шаблон, который принимает переменное число параметров char
, т. Е.
template struct Foo;
Мне просто интересно, были ли какие-либо макро-трюки, которые позволили бы мне создать экземпляр этого синтаксиса, подобный следующему:
Foo
или
- byte в hex string
- Почему длина этой строки больше, чем количество символов в ней?
- Правильный способ использования StringBuilder в SQL
- Самый быстрый способ перебрать все символы в строке
- Хороший пример для boost :: algorithm :: join
Foo
или
Foo
и т.п.
В принципе, все, что мешает вам писать персонажи индивидуально, например
Foo
Для меня это не большая проблема, так как это только для игрушечной программы, но я думал, что все равно спрошу.
- Как прочитать весь файл в строке с помощью C #?
- Конкатенация символов для формирования строки дает разные результаты
- Как разбить String на несколько значений?
- Преобразование целого в String с запятой для тысяч
- От String до NSDate в Swift
- Как вернуть строковое значение из функции Bash
- Многострочная строка Java
- Исключить строку для шаблона замены sed
Я создал его сегодня и тестировал на 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)