Уміння створювати та маніпулювати рядками під час компіляції в 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 ++; програми досить безмежні.