[Из песочницы] Literal operator templates for strings

Стандарт C++11 привнес в язык такую вещь, как пользовательские литералы[1]. Конкретно — дюжину вариантов для определения оператора », добавляющих небольшой синтаксический сахар, всех, за исключением одного — шаблонного варианта: template type operator » _op (); Отличается этот вариант тем, что он не просто дает альтернативный вариант вызова функции с помощью суффикса, а позволяет определить свои правила парсинга передаваемого аргумента на этапе компиляции, расширяя тем самым возможности компилятора.Например:

auto x = 10001000100011001001001010001000_b; Однако при разработке стандарта было допущено небольшое упущение — шаблонный вариант пользовательского литерала позволяет работать только с числовыми аргументами, несмотря на то, что парсинг их осуществляется посимвольно.Такое упущение, конечно же, не могло остаться не замеченным, и на этапе согласования стандарта C++14 было предложено решение для строковых аргументов[2] template type operator » _op (); В скором времени было реализовано в компиляторах GCC[3] и clang (GNU extension). Однако в финальную редакцию стандарта C++14 так и не попало. Впрочем, не будем отчаиваться, есть надежда, что нас обрадует C++17. А пока посмотрим, как можно будет применять новый тип пользовательских литералов.Определим шаблон мета-строки: template struct str { static constexpr const char value[sizeof…(Chars)+1] = {Chars…,'\0'}; static constexpr int size = sizeof…(Chars); }; Определим наш литерал-генератор мета-строки: template constexpr str operator» _s () { return str(); } Создадим шаблон map-like структуры данных, с типами в качестве ключей: template struct field { using key = Key; using type = Type; type value; };

template struct field_by_type;

template struct field_by_type, Tail…>, N> { static constexpr int value = N; };

template struct field_by_type, N>: field_by_type, N+1> {};

template struct record { using tuple_type = std: tuple;

template typename std: tuple_element:: value, tuple_type>:: type: type& operator[](Key) { return std: get:: value>(data).value; }

template const typename std: tuple_element:: value, tuple_type>:: type: type& operator[](Key) const { return std: get:: value>(data).value; } tuple_type data; }; Так как в качестве типов-ключей мы собираемся использовать мета-строки, добавим немного ввода-вывода: template std: ostream& operator<< (std::ostream& os, const field f){ os << Key::value << " = " << f.value << "\n"; return os; }

template struct print_tuple { std: ostream& operator () (std: ostream& os, const std: tuple& t) { os << std::get(t); return print_tuple{}(os, t); } };

template struct print_tuple<0, Ts...> { std: ostream& operator () (std: ostream& os, const std: tuple& t) { return os; } };

template std: ostream& operator<< (std::ostream& os, const record& r) { os << "{\n"; print_tuple{}(os, r.data); os << "}"; return os; } Ну а теперь и сам пример: using Person = record< field, field, field >;

int main (){ Person p; p[«id»_s] = 10; p[«first_name»_s] = «John»; p[«last_name»_s] = «Smith»; std: cout << p << "\n"; } Также мы можем наследовать, добавляя новые функции: class Person : public record< field, field, field, > { public: void set_name (const std: string& f, const std: string& l) { (*this)[«first_name»_s] = f; (*this)[«last_name»_s] = l; }; }; int main (){ Person p; p[«id»_s] = 10; p.set_name («John», «Smith»); std: cout << p << "\n"; } Итоговые объекты статически выводят тип поля по заданному ключу. А при использовании невалидного ключа не только генерируют ошибку компиляции, но и могут закрашать компилятор clang:d1af72dfc745414da12aa834e9c25faf.png

Ссылки User-defined literals (cppreference) N3599 Literal operator templates for strings (Richard Smith) [C++1y] Support n3599 — Literal operator templates for strings for C++1y (GCC Project)

© Habrahabr.ru