Возможность создавать строки и управлять ими во время компиляции в C ++ имеет несколько полезных приложений. Хотя в C ++ можно создавать строки во время компиляции, этот процесс очень громоздкий, поскольку строку необходимо объявить как последовательность символов с переменным числом аргументов, например
using str = sequence<'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'>;
Такие операции, как конкатенация строк, извлечение подстроки и многие другие, можно легко реализовать как операции над последовательностями символов. Можно ли более удобно объявлять строки времени компиляции? Если нет, то есть ли в разработке предложение, которое позволило бы удобное объявление строк времени компиляции?
Почему существующие подходы терпят неудачу
В идеале мы хотели бы иметь возможность объявлять строки времени компиляции следующим образом:
// Approach 1
using str1 = sequence<"Hello, world!">;
или, используя определенные пользователем литералы,
// Approach 2
constexpr auto str2 = "Hello, world!"_s;
где decltype(str2)
будет constexpr
конструктор. Возможно реализовать более беспорядочную версию подхода 1, воспользовавшись тем, что вы можете сделать следующее:
template <unsigned Size, const char Array[Size]>
struct foo;
Однако у массива должна быть внешняя связь, поэтому, чтобы подход 1 работал, нам нужно было бы написать что-то вроде этого:
/* Implementation of array to sequence goes here. */
constexpr const char str[] = "Hello, world!";
int main()
{
using s = string<13, str>;
return 0;
}
Что и говорить, это очень неудобно. Подход 2 практически невозможно реализовать. Если бы мы объявили constexpr
буквальный оператор ( ), как бы мы указали возвращаемый тип? Поскольку нам нужен оператор для возврата переменной последовательности символов, нам нужно будет использовать const char*
параметр для указания типа возвращаемого значения:
constexpr auto
operator"" _s(const char* s, size_t n) -> /* Some metafunction using `s` */
Это приводит к ошибке компиляции, потому что s
это не файл constexpr
. Попытка обойти это, выполнив следующие действия, мало помогает.
template <char... Ts>
constexpr sequence<Ts...> operator"" _s() { return {}; }
Стандарт требует, чтобы эта конкретная форма буквального оператора зарезервирована для целочисленных типов и типов с плавающей запятой. Пока 123_s
работал abc_s
бы, не стал бы. Что, если мы полностью откажемся от пользовательских литералов и просто будем использовать обычную constexpr
функцию?
template <unsigned Size>
constexpr auto
string(const char (&array)[Size]) -> /* Some metafunction using `array` */
Как и раньше, мы сталкиваемся с проблемой, заключающейся в том, что массив, который теперь является параметром constexpr
функции, сам по себе больше не является constexpr
типом.
Я считаю, что должно быть возможно определить макрос препроцессора C, который принимает строку и размер строки в качестве аргументов и возвращает последовательность, состоящую из символов в строке (с использованием строкового BOOST_PP_FOR
преобразования, индексов массива и т. Д.). Однако у меня нет времени (или достаточно интереса) для реализации такого макроса =)
источник
constexpr
функциях и инициализировать массивы (следовательно, concat, substr и т. Д.).constexpr
строки можно анализировать во время компиляции, так что вы можете использовать разные пути кода в зависимости от результатов. По сути, вы можете создавать EDL в C ++; приложения довольно безграничны.Ответы:
Я не видел ничего, что могло бы сравниться с элегантностью Скотта Шурра,
str_const
представленного на C ++ Now 2012 . Это требуетconstexpr
.Вот как вы можете его использовать и что он может делать:
Это не намного круче, чем проверка диапазона во время компиляции!
И использование, и реализация не содержат макросов. И нет никакого искусственного ограничения на размер строки. Я бы разместил здесь реализацию, но я уважаю неявное авторское право Скотта. Реализация находится на одном слайде его презентации, ссылка на которую приведена выше.
источник
str_const
а другой на основеsequence
), это может быть возможным. Пользователь будет использоватьstr_const
для инициализации строки, но последующие операции, создающие новые строки, будут возвращатьsequence
объекты.template<char... cs>
. Теоретически вы можете создать что-то, что принимает буквальную строку и компилирует ее содержимое в функцию. См. Ответ dyp. Очень полная на вид библиотека метапарчатая . По сути, вы можете определить любое отображение литеральных строк в типы и реализовать его с помощью такой технологии.constexpr operator==
. Сожалею. Презентация Скотта должна помочь вам понять, как это сделать. В C ++ 14 это намного проще, чем в C ++ 11. Я бы даже не стал пытаться использовать C ++ 11. См. Последниеconstexpr
выступления Скотта здесь: youtube.com/user/CppConэто можно реализовать, не полагаясь на ускорение, используя очень простой макрос и некоторые функции C ++ 11:
(последние два здесь не требуются)
нам нужно иметь возможность создать экземпляр вариативного шаблона с указаниями пользователя от 0 до N - инструмент, который также полезен, например, для расширения кортежа в аргумент вариативной функции шаблона (см. вопросы: Как мне расширить кортеж до аргументов вариативной функции шаблона?
" распаковка "кортежа для вызова соответствующего указателя функции )
затем определите вариативный шаблон с именем string с параметром, отличным от типа char:
Теперь самое интересное - передать символьные литералы в строковый шаблон:
простая демонстрация конкатенации показывает использование:
https://ideone.com/8Ft2xu
источник
operator+
вместоoperator*
?(str_hello + str_world)
CSTRING
макрос. В противном случае вы не сможете создатьCSTRING
внутренний вызов[]
оператора, поскольку double[[
зарезервированы для атрибутов.Изменить: как указал Ховард Хиннант (и я несколько в моем комментарии к OP), вам может не понадобиться тип с каждым отдельным символом строки в качестве одного аргумента шаблона. Если вам это действительно нужно, ниже есть решение без макросов.
Есть уловка, которую я обнаружил, пытаясь работать со строками во время компиляции. Требуется ввести другой тип, помимо «строки шаблона», но внутри функций вы можете ограничить область действия этого типа.
Он не использует макросы, а использует некоторые функции C ++ 11.
источник
pair<int,pair<char,double>>
. Я гордился собой, а потом открыл для себя этот ответ и библиотеку метапарков сегодня! Я действительно должен поискать ТАК более тщательно, прежде чем начинать такие глупые проекты :-) Я полагаю, что теоретически можно построить полностью компилятор C ++ на основе такой технологии. Какая самая безумная вещь из всего этого была построена?char[]
.my_str.print();
вместоstr.print();
?Если вы не хотите использовать решение Boost, вы можете создать простые макросы, которые будут делать что-то подобное:
Единственная проблема - это фиксированный размер в 64 символа (плюс дополнительный ноль). Но его можно легко изменить в зависимости от ваших потребностей.
источник
sizeof(str) > i
(вместо добавления дополнительных0,
токенов)? Легко определитьtrim
метафункцию, которая будет делать это после того, как макрос уже был вызван, но было бы неплохо, если бы сам макрос можно было изменить.sizeof(str)
. Можно вручную добавить размер строки, например,MACRO_GET_STR(6, "Hello")
но для этого требуются макросы Boost для работы, потому что для его написания вручную требуется в 100 раз больше кода (вам нужно реализовать такие простые вещи, как1+1
).Есть статья Абеля Синковича и Дэйва Абрахамса : Использование строк в метапрограммах шаблонов C ++ .
В нем есть некоторые улучшения по сравнению с вашей идеей использования макроса + BOOST_PP_REPEAT - он не требует передачи явного размера макросу. Короче говоря, он основан на фиксированном верхнем пределе размера строки и «защите от переполнения строки»:
плюс условное повышение :: mpl :: push_back .
Если вы принимаете завершающие нули, рукописный цикл макроса, двукратное повторение строки в развернутом макросе и не имеете Boost - тогда я согласен - это лучше. Хотя с Boost было бы всего три строчки:
LIVE DEMO
источник
Другой мой ответ, кажется, никому не нравится: - <. Итак, здесь я покажу, как преобразовать str_const в реальный тип:
Компилируется с помощью clang ++ -stdlib = libc ++ -std = c ++ 14 (clang 3.7)
источник
Коллега предложил мне объединить строки в памяти во время компиляции. Он также включает создание экземпляров отдельных строк во время компиляции. Полный листинг кода находится здесь:
источник
objdump -t a.out |grep my
ничего не находит. Когда я начал набирать этот код, я продолжал экспериментировать с удалениемconstexpr
из функций иobjdump
показывал их, когда ониconstexpr
были опущены. Я уверен на 99,9%, что это происходит во время компиляции.-S
), вы заметите, что gcc (4.7.2) действительно разрешаетconstexpr
функции во время компиляции. Тем не менее, строки не собираются во время компиляции. Скорее (если я правильно интерпретирую) для каждого символа этих «собранных» строк существует собственнаяmovb
операция, которая, возможно, является оптимизацией, которую вы искали.Вот краткое решение C ++ 14 для создания std :: tuple <char ...> для каждой переданной строки времени компиляции.
А вот один для создания уникального типа времени компиляции, вырезанный из другого сообщения макроса.
Очень жаль, что пользовательские литералы пока не могут быть использованы для этого.
источник
Основываясь на идее Говарда Хиннанта, вы можете создать буквальный класс, который будет складывать два литерала вместе.
источник
str_at
идет?str_at<int I>(const char* a) { return a[i]; }
Ваш подход №1 правильный.
Нет, не правильно. Это компилируется с помощью clang и gcc. Я надеюсь, что это стандартный С ++ 11, но я не знаток языка.
То, что мне действительно понравилось бы в С ++ 17, было бы следующим, чтобы быть эквивалентным (для завершения подхода №1)
Нечто подобное уже существует в стандарте для шаблонных пользовательских литералов, о чем также упоминается void-pointer, но только для цифр. А пока еще одна небольшая хитрость - использовать режим редактирования переопределения + копирование и вставка
Если вы не возражаете против макроса, это работает (немного изменено из ответа Янкса):
источник
Решение kacey для создания уникального типа времени компиляции с небольшими изменениями может также использоваться с C ++ 11:
Использование:
источник
Играя с картой буст хана наткнулся на эту ветку. Поскольку ни один из ответов не решил мою проблему, я нашел другое решение, которое хочу добавить здесь, поскольку оно может быть потенциально полезно для других.
Моя проблема заключалась в том, что при использовании карты boost hana со строками hana компилятор все еще генерировал некоторый код времени выполнения (см. Ниже). Причина была очевидна в том, что для запроса карты во время компиляции это должно быть
constexpr
. Это невозможно, посколькуBOOST_HANA_STRING
макрос генерирует лямбду, которую нельзя использовать вconstexpr
контексте. С другой стороны, карте нужны строки с разным содержимым, чтобы иметь разные типы.Поскольку решения в этом потоке либо используют лямбда, либо не предоставляют разные типы для разного содержимого, я нашел полезным следующий подход. Также он избегает хакерского
str<'a', 'b', 'c'>
синтаксиса.Основная идея состоит в том, чтобы создать версию
str_const
шаблона Скотта Шурра на основе хэша персонажей. Это возможноc++14
, ноc++11
должно быть возможным при рекурсивной реализацииcrc32
функции (см. Здесь ).Использование:
В результате код ассемблера с
clang-cl
5.0:источник
Я хотел бы добавить два очень маленьких улучшения к ответу @ user1115339. Я упомянул о них в комментариях к ответу, но для удобства помещу здесь решение для копирования и вставки.
Единственное отличие - это
FIXED_CSTRING
макрос, который позволяет использовать строки в шаблонах классов и в качестве аргументов для оператора индекса (полезно, если у вас есть, например, карта времени компиляции).Живой пример .
источник
Моя собственная реализация основана на подходе из
Boost.Hana
строки (класс шаблона с вариативными символами), но использует толькоC++11
стандарт иconstexpr
функции со строгой проверкой времени компиляции (было бы ошибкой времени компиляции, если бы не выражением времени компиляции). Может быть сконструирован из обычной необработанной строки C вместо причудливой{'a', 'b', 'c' }
(с помощью макроса).Реализация: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/include/tacklelib/tackle/tmpl_string.hpp
Тесты: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/src/tests/unit/test_tmpl_string.cpp
Примеры использования:
Подробная информация о
constexpr
границе времени компиляции функции: https://www.boost.org/doc/libs/1_65_0/libs/hana/doc/html/index.html#tutorial-appendix-constexprДля других деталей использования смотрите тесты.
В настоящее время весь проект является экспериментальным.
источник
В C ++ 17 с помощью вспомогательной макрос-функции легко создавать строки времени компиляции:
А это пример использования:
источник