11. Метапрограммирование с шаблонами и X-macro. Идиома Type Erasure
Описание файла
PDF-файл из архива "11. Метапрограммирование с шаблонами и X-macro. Идиома Type Erasure", который расположен в категории "". Всё это находится в предмете "проектирование больших систем с++" из 11 семестр (3 семестр магистратуры), которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст из PDF
Проектирование больших систем наC++Коноводов В. А.кафедра математической кибернетики ВМКЛекция 1117.11.2017Идиома Type ErasureЕсть класс, сохраняющий объекты произвольного типа:template <typename T>class TValue {T v;public:T Get() const {};template <typename U>void Set(U&& val) {v = std::forward<U>(val);}};Идиома Type ErasureЕсть класс, сохраняющий объекты произвольного типа:template <typename T>class TValue {T v;public:T Get() const {};template <typename U>void Set(U&& val) {v = std::forward<U>(val);}};И мы хотим «подписаться» на изменения Set:TValue<int> v;v.DoOnChange([](int newVal) {std::cout << "set " << newVal << std::endl;});v.Set(1);Идиома Type ErasureАбстрактный интерфейс вызова:template <typename T>class IFunctor {public:virtual ~IFunctor() = default;virtual void Call(const T&t) = 0;};Идиома Type ErasureАбстрактный интерфейс вызова:template <typename T>class IFunctor {public:virtual ~IFunctor() = default;virtual void Call(const T&t) = 0;};Класс для вызова:template <typename T, typename F>class TFunctor: public IFunctor<T> {private:std::decay_t<F> f;public:TFunctor(F f) : f(std::move(f)){}void Call(const T& t) { f(t); }};Идиома TypeErasureСоздание объекта, который будет вызывать нужную функцию:template <typename T, typename F>std::shared_ptr<IFunctor<T>> CreateFunctor(F&& f) {return std::make_shared<TFunctor<T, F>> (std::forward<F>(f));}Идиома TypeErasuretemplate <typename T>class TValue {T v;std::shared_ptr<IFunctor<T>> invPtr;public:T Get() const {};template <typename U>void Set(U&& val) {if (val != v) {v = std::forward<U>(val);invPtr->Call(v);}}template <typename F>void DoOnChange(F&& f) {invPtr = CreateFunctor<T>(std::forward<F>(f));}};Идиома TypeErasure.
Задача.Напишите класс Any, сохраняющий объекты любого типа, иобеспечивающий к ним доступ:Any a(5);a.get<int>(); // 5a.get<std::string>(); // errorПример с закрытым конструкторомclass C {public:C() = delete;int f() { return 5; }};int main() {decltype(C().f()) a = 5; // не компилируется}Пример с закрытым конструкторомclass C {public:C() = delete;int f() { return 5; }};int main() {decltype(std::declval<C>().f()) a = 5;}// ok!Пример с закрытым конструкторомclass C {public:C() = delete;int f() { return 5; }};int main() {decltype(std::declval<C>().f()) a = 5;}// ok!Происходит только вывод типа. Работает во время компиляциии только в таких контекстах.Пример с закрытым конструкторомclass C {public:C() = delete;int f() { return 5; }};int main() {decltype(std::declval<C>().f()) a = 5;}// ok!Происходит только вывод типа. Работает во время компиляциии только в таких контекстах.template<typename T, typename U>using TSum = decltype(std::declval<T>() + std::declval<U>());Есть ли в классе метод?template<class...> using dummy = void;template <class T, typename = void>struct does_have_super_func : public std::false_type {};template <class T>struct does_have_super_func<T,dummy<decltype(std::declval<T>().super_func())>> : public std::true_type {};struct A { int super_func(); };struct B { int func(); };int main() {std::cout << does_have_super_func<A>::value << std::endl;std::cout << does_have_super_func<B>::value << std::endl;std::cout << does_have_super_func<int>::value << std::endl;}ШаблоныПараметр шаблона, в частности, может не быть типом:template <int N>struct A;Если между классами есть зависимости, то компилятор будетпытаться вычислять зависимости на стадии компиляции, вчастности, на стадии компиляции можно заставить компиляторвыполнять нетривиальные задачи.Отсутпление в сторонуВ C++17 можно делать так:template<auto n>void f() {// ...}int main(){f<3>();// intf<'a'>(); // char}А как это сделать в C++11?Шаблонное метапрограммированиеВычисления на этапе компиляции:#include <iostream>template <int Base, int N>struct Power {enum {result = Power<Base, N - 1> ::result * Base };};template <int Base>struct Power <Base, 0> {enum {result = 1 };};int main () {std::cout << Power<2,10> :: result;}Вычисления на шаблонахЧто напечатает программа?#include <iostream>template <typename T>struct A {static const int res = 1;};template <typename T>struct A<T*> {static const int res = 1 + A<T>::res;};int main () {std::cout << A<int***> :: res << std::endl;}ЗадачаНапишите шаблон для вычисления Ckn на этапе компиляции.Шаблонное метапрограммированиеВ C++11 появляется std::integral_constant — обёртка надстатическими константами.template<class T, T v>struct integral_constant {static constexpr T value = v;typedef T value_type;typedef integral_constant type;constexpr operator value_type() const noexcept {return value;}constexpr value_type operator()() const noexcept {return value;}};std::integral_constantИ теперь можно написать так:template <typename X, typename Y>using sum = std::integral_constant<decltype(X::value + Y::value),X::value + Y::value>;using result =sum<std::integral_constant<int, 3>,std::integral_constant<int, 4>>;User-defined literalsВозможно определить пользовательские литералы:Iцелочисленные литералы 1_km,Ifloat-литералы 0.5_rad,Iсимвольные литералы ’a’_XIстроковые литералы "abc"_QВсе суффиксы должны начинаться с подчеркивания, суффиксыстандартной библиотеки не начинаются с подчеркивания.User-defined literalsconstexpr long double operator"" _deg (long double deg){return deg * 3.1415 / 180;}constexpr unsigned long long operator"" _MB(unsigned long long mb) {return static_cast<size_t>(mb) << 20;}static const auto MEMORY_LIMIT = 100_MB;int main() {double alpha = 90.0_deg;}User-defined literalsstruct A {int val;A(int x) : val(x) {}};A operator"" _A (unsigned long long x) { return A(x); }int main() {A a = 1_A;}При этом нельзя написатьA operator"" _A (int x) { return A(x); }Задача: compile-time stringsОпределитеtemplate <char ...c> struct TString { };и необходимые операторы так, чтобы компилировался код:constexpr auto hello = "hello"_s + " world"_s;static_assert(hello == "hello world"_s);Метапрограммирование и макросы#include <iostream>#include <string>enum class TCode {OK = 200,NOT_FOUND = 404};std::string ToString(TCode code) {switch (code) {case TCode::OK: return "OK";case TCode::NOT_FOUND: return "NOT FOUND";default: return "UNK";}}int main() {std::cout << ToString(TCode::OK) << std::endl;}X macroОпределим таблицу нужных нам кодов и будем определять XX:#define STATUS_CODES \XX(200, OK) \XX(404, NOT_FOUND)#define XX(code, text)\text = code,enum class TCode {STATUS_CODES};#undef XXX macro#define XX(code, text) \case TCode::text: return #text;std::string ToString(TCode code) {switch(code) {STATUS_CODESdefault: return "UNKNOWN";}}#undef XX.