Шаблоны Литеральных Операторов Для Строк

Стандарт C++11 ввел в язык такую вещь, как пользовательские литералы.

[1] .

А конкретно десяток вариантов определения оператора "" , добавив немного синтаксического сахара, все за исключением одного — шаблонной версии:

  
  
  
  
  
  
  
  
   

template <char.> type operator "" _op();

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

Например:

auto x = 10001000100011001001001010001000_b;

Однако при разработке стандарта было допущено небольшое упущение — шаблонная версия пользовательского литерала позволяет работать только с числовыми аргументами, несмотря на то, что они анализируются посимвольно.

Такое упущение, конечно, не могло остаться незамеченным, и на этапе согласования стандарта С++14 было предложено решение для строковых аргументов.

[2]

template <typename CharT, CharT .

String> type operator "" _op();

Вскоре реализовано в компиляторах GCC. [3] и clang (расширение GNU).

Однако стандарт C++14 так и не вошёл в окончательную редакцию.

Однако не будем отчаиваться, есть надежда, что C++17 нас порадует. А пока давайте посмотрим, как мы можем использовать новый тип пользовательских литералов.

Давайте определим шаблон метастроки:

template<char .

Chars> struct str { static constexpr const char value[sizeof.(Chars)+1] = {Chars.,'\0'}; static constexpr int size = sizeof.(Chars); }; template<char .

Chars> constexpr const char str<Chars.>::value[sizeof.(Chars)+1];

Давайте определим наш литерал генератора метастроок:

template<typename CharT, CharT .

String> constexpr str<String.> operator"" _s() { return str<String.>(); }

Давайте создадим шаблон структуры данных в виде карты с типами в качестве ключей:

template<class Type, class Key> struct field { using key = Key; using type = Type; type value; }; template<class,class,int N=0> struct field_by_type; template<class Key, class Type, class .

Tail, int N> struct field_by_type<Key, std::tuple<field<Type,Key>,Tail.>, N> { static constexpr int value = N; }; template<class Key, class Head, class .

Tail, int N> struct field_by_type<Key, std::tuple<Head,Tail.>, N> : field_by_type<Key,std::tuple<Tail.>,N+1> {}; template<class .

Fields> struct record { using tuple_type = std::tuple<Fields.>; template<class Key> typename std::tuple_element<field_by_type<Key,tuple_type>::value,tuple_type>::type::type& operator[](Key) { return std::get<field_by_type<Key,tuple_type>::value>(data).

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

value; } tuple_type data; };

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

template<class Type, class Key> std::ostream& operator<< (std::ostream& os, const field<Type,Key> f){ os << Key::value << " = " << f.value << "\n"; return os; } template<int I, typename. Ts> struct print_tuple { std::ostream& operator() (std::ostream& os, const std::tuple<Ts.>& t) { os << std::get<sizeof.(Ts)-I>(t); return print_tuple<I - 1, Ts.>{}(os,t); } }; template<typename. Ts> struct print_tuple<0, Ts.> { std::ostream& operator() (std::ostream& os, const std::tuple<Ts.>& t) { return os; } }; template<class .

Fields> std::ostream& operator<< (std::ostream& os, const record<Fields.>& r) { os << "{\n"; print_tuple<sizeof.(Fields),Fields.>{}(os,r.data); os << "}"; return os; }

Ну а теперь сам пример:

using Person = record< field<int, decltype("id"_s)>, field<std::string, decltype("first_name"_s)>, field<std::string, decltype("last_name"_s)> >; 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<int, decltype("id"_s)>, field<std::string, decltype("first_name"_s)>, field<std::string, decltype("last_name"_s)> > { 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:

Шаблоны литеральных операторов для строк



Ссылки

  1. Пользовательские литералы (cpпредпочтение)
  2. N3599 Шаблоны литеральных операторов для строк (Ричард Смит)
  3. [C++1y] Поддержка n3599 — шаблоны литеральных операторов для строк для C++1y. (Проект GCC)
Теги: #C++ #C++1z #метапрограммирование шаблонов #C++
Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.