├── .clang-format ├── .devcontainer └── devcontainer.json ├── .gitignore ├── .vscode └── settings.json ├── Dockerfile ├── README.md ├── book ├── .gitignore ├── bibliography.bib ├── chapter1.tex ├── chapter2.tex ├── chapter3.tex ├── chapter4.tex ├── hyphenation.tex ├── latexmkrc ├── main.tex ├── metaprogramming-book.pdf └── preamble.tex ├── codestyle.md ├── course ├── lib │ └── assert.hpp └── testing │ ├── RegularityWitness.hpp │ ├── assert.hpp │ ├── concepts.hpp │ ├── debug_trap.hpp │ └── metafunctions.hpp ├── run_tests.sh ├── samples ├── sem1 │ ├── .gitignore │ ├── CMakeLists.txt │ └── main.cpp ├── sem2 │ ├── .gitignore │ ├── CMakeLists.txt │ └── main.cpp ├── sem3 │ ├── .gitignore │ ├── CMakeLists.txt │ ├── main.cpp │ ├── operations.hpp │ └── typelist.hpp ├── sem4 │ ├── .gitignore │ ├── CMakeLists.txt │ ├── function.hpp │ └── main.cpp ├── sem5 │ ├── .gitignore │ ├── CMakeLists.txt │ ├── any.hpp │ └── main.cpp ├── sem6 │ ├── .gitignore │ ├── CMakeLists.txt │ └── main.cpp ├── sem7 │ ├── .gitignore │ ├── CMakeLists.txt │ └── main.cpp └── sem8 │ ├── .vscode │ └── settings.json │ ├── CMakeLists.txt │ ├── any_object.hpp │ ├── main.cpp │ ├── shapes.hpp │ └── tag_invoke.hpp ├── solutions.code-workspace ├── tasks ├── .gitignore ├── CMakeLists.txt ├── annotations │ ├── README.md │ └── tests │ │ ├── CMakeLists.txt │ │ ├── annotations.hpp │ │ ├── static.cpp │ │ ├── stress.cpp │ │ └── tests ├── enumerators │ ├── README.md │ └── tests │ │ ├── CMakeLists.txt │ │ ├── main.cpp │ │ ├── stress.cpp │ │ └── tests ├── mapper │ ├── README.md │ └── tests │ │ ├── CMakeLists.txt │ │ ├── main.cpp │ │ ├── nocompile.cpp │ │ ├── tests │ │ └── unsorted.cpp ├── rules.md ├── slice │ ├── README.md │ └── tests │ │ ├── CMakeLists.txt │ │ ├── main.cpp │ │ └── tests ├── span │ ├── README.md │ └── tests │ │ ├── CMakeLists.txt │ │ ├── main.cpp │ │ └── tests ├── spy │ ├── README.md │ └── tests │ │ ├── CMakeLists.txt │ │ ├── main.cpp │ │ ├── mocks.hpp │ │ ├── sbo.cpp │ │ ├── static.cpp │ │ └── tests ├── testing.md └── type_lists │ ├── README.md │ └── tests │ ├── CMakeLists.txt │ ├── group_by.cpp │ ├── tests │ ├── type_lists.cpp │ └── value_sequences.cpp └── third_party └── get_deps.cmake /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | IndentWidth: 2 4 | --- 5 | Language: Cpp 6 | AlwaysBreakTemplateDeclarations: true 7 | DerivePointerAlignment: false 8 | PointerAlignment: Left 9 | ReferenceAlignment: Left 10 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/docker-existing-dockerfile 3 | { 4 | "name": "Existing Dockerfile", 5 | 6 | "build": { 7 | "dockerfile": "../Dockerfile", 8 | "target": "metacourse-dev" 9 | }, 10 | 11 | "customizations": { 12 | "vscode": { 13 | "extensions": [ 14 | "ms-vscode.cpptools-extension-pack", 15 | "llvm-vs-code-extensions.vscode-clangd" 16 | ], 17 | "settings": 18 | { 19 | "C_Cpp.intelliSenseEngine": "disabled" 20 | } 21 | } 22 | }, 23 | 24 | // Mount solutions repo 25 | "mounts": [ 26 | "source=${localWorkspaceFolder}/../metaprogramming-course-solutions,target=/workspaces/solutions,type=bind" 27 | ], 28 | 29 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 30 | // "forwardPorts": [], 31 | 32 | // Uncomment the next line to run commands after the container is created - for example installing curl. 33 | // "postCreateCommand": "apt-get update && apt-get install -y curl", 34 | 35 | // Uncomment when using a ptrace-based debugger like C++, Go, and Rust 36 | // "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], 37 | 38 | // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker. 39 | // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], 40 | 41 | // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. 42 | "remoteUser": "student" 43 | } 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .ssh 3 | .cache 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmake.sourceDirectory": "${workspaceFolder}/tasks", 3 | "cmake.generator": "Unix Makefiles", 4 | "cmake.configureArgs": 5 | [ 6 | "-DREPOSITORY_PATH=${workspaceFolder}/../solutions", 7 | "-DTASK=slice", 8 | "-DNOCOMPILE=OFF" 9 | ], 10 | "C_Cpp.default.cppStandard": "c++20" 11 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | # Base stage that can be used for development 3 | FROM alpine:edge AS metacourse-base 4 | 5 | ENV TZ=Europe/Moscow 6 | 7 | ENV LANG C.UTF-8 8 | ENV LC_ALL C.UTF-8 9 | 10 | 11 | RUN apk update && apk upgrade 12 | RUN apk add --no-cache git openssh build-base clang make cmake lld compiler-rt zsh python3 13 | ENV CC=/usr/bin/clang 14 | ENV CXX=/usr/bin/clang++ 15 | 16 | FROM metacourse-base AS metacourse-dev 17 | 18 | RUN apk add --no-cache libuser sudo gdb llvm clang-dev 19 | 20 | ENV USER_ID=65535 21 | ENV GROUP_ID=65535 22 | ENV USER_NAME=student 23 | ENV GROUP_NAME=students 24 | 25 | RUN addgroup -g $GROUP_ID $GROUP_NAME \ 26 | && adduser --shell /bin/zsh --disabled-password --gecos "" \ 27 | --uid $USER_ID --ingroup $GROUP_NAME $USER_NAME 28 | RUN echo "$USER_NAME:123" | chpasswd 29 | RUN echo "$USER_NAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers 30 | 31 | USER $USER_NAME 32 | RUN wget https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - | zsh || true 33 | 34 | FROM metacourse-base AS metacourse-tester 35 | 36 | RUN adduser --disabled-password --shell /bin/zsh tester 37 | COPY .ssh /home/tester/.ssh 38 | RUN ssh-keyscan -H github.com >> /home/tester/.ssh/known_hosts 39 | RUN (cd /home/tester/.ssh && chmod 600 id_rsa id_rsa.pub known_hosts) 40 | RUN chown -R tester:tester /home/tester/.ssh 41 | COPY run_tests.sh /home/tester 42 | RUN chown tester:tester /home/tester/run_tests.sh 43 | RUN chmod 700 /home/tester/run_tests.sh 44 | 45 | USER tester 46 | WORKDIR /home/tester 47 | 48 | ENTRYPOINT [ "/home/tester/run_tests.sh" ] 49 | 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Метапрограммирование на C++ 2 | ============================ 3 | 4 | [Презентации](https://drive.google.com/drive/folders/1JUSjHeZMyicojhJ7ebN3aStnU7VxqZEq?usp=sharing) 5 | 6 | [Условия задач](/tasks) 7 | 8 | [Как тестировать задачи](/tasks/testing.md) 9 | 10 | [Code style](/codestyle.md) 11 | 12 | ## Темы 13 | Темы идут в произвольном порядке и не мапятся 1 к 1 на занятия. Более того, скорее всего осветить все из них за один семестр не удастся. Программа продумана не до конца и будет уточняться по ходу дела. 14 | 15 | 1. Templates. Templateable entities, kinds, metafunctions 16 | 2. Template debugging. Dependent false assertion, tracers, archetypes 17 | 3. Name resolution, two-phase lookup, dependent names 18 | 4. Type deduction. Template arguments, auto, deduction guides 19 | 5. Perfect forwarding. Unversal references, argument forwarding, result forwarding, decltype(auto) 20 | 6. Friends, Barton–Nackman trick 21 | 7. Concepts, requirements 22 | 8. Stateful metaprogramming with friends 23 | 9. Reflection tricks. std::source_location, enum reflection, struct reflection 24 | 10. Type lists 25 | 11. Traits and policies 26 | 12. Policy based design, Mixins, EBCO 27 | 13. Polymorphism. VTables, templates, macros, link time polymorphism, CRTP, type erasure 28 | 14. Multimethods 29 | 15. Named template arguments trick 30 | 16. Hierarchy generation 31 | 17. Tuples and variants 32 | 18. OO patterns revisited: factory, abstract factory, prototype, visitor, command, observer, strategy... 33 | 19. Niebloids 34 | 20. Tag invoke 35 | 21. Ranges 36 | 22. Macros and codegen 37 | 38 | ## Полезные ссылки 39 | 40 | [refactoring.guru](http://refactoring.guru/) — сайт про паттерны и рефакторинг 41 | 42 | [Concepts and constraints](https://en.cppreference.com/w/cpp/language/constraints) — cppreference 43 | 44 | [Концепты: упрощаем реализацию классов std utility](https://youtu.be/udTEfwCkmaw) — доклад Андрея Давыдова 45 | 46 | [Метапрограммирование, щадящее компилятор](https://www.youtube.com/watch?v=udTEfwCkmaw) — доклад Андрея Давыдова 47 | 48 | Building Range Adaptors ([part 1](https://www.youtube.com/watch?v=YWayW5ePpkY), [part 2](https://www.youtube.com/watch?v=g-F280_AQp8)) — Chris Di Bella, CppCon 49 | 50 | [Move only folly::Function](https://youtu.be/SToaMS3jNH0) — Sven Over, CppCon 51 | 52 | [Inplace std::function alternative for audio processing](https://youtu.be/VY83afAJUIg) — Tom Poole, CppCon 53 | 54 | [C++14 Reflections Without Macros, Markup nor External Tooling](https://youtu.be/abdeAew3gmQ) — Антон Полухин, CppCon 55 | 56 | [How to Hack C++ with Templates and Friends](https://www.worldcadaccess.com/blog/2020/05/how-to-hack-c-with-templates-and-friends.html) 57 | 58 | [C++ vtables](https://shaharmike.com/cpp/vtable-part1/) — таблицы виртуальных функций в деталях 59 | 60 | [Customization Point Design in C++11 and Beyond](http://ericniebler.com/2014/10/21/customization-point-design-in-c11-and-beyond/) — ниблоиды, CPO используемые в `std::ranges` 61 | 62 | [~Template~ metaprogramming](https://github.com/boost-ext/mp) — бесплатные списки типов через `std::ranges` 63 | 64 | [tag_invoke: A general pattern for supporting 65 | customisable functions (P1895)](http://open-std.org/JTC1/SC22/WG21/docs/papers/2019/p1895r0.pdf) — новый ультимативный механизм для CPO без измений в язык 66 | 67 | [https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1985r1.pdf](Universal Template Parameters) — то, что мы очень хотим, но получим не скоро 68 | 69 | [function2](https://github.com/Naios/function2) — улучшеный `std::function`, пример реализации `vtable` руками 70 | 71 | [libunifex](https://github.com/facebookexperimental/libunifex) — новый фреймворк для асинхронности в плюсах. Содержит примеры и на `vtable`, и на тайплисты, и на `tag_invoke`, и вообще на всё на свете 72 | 73 | [Building Range Adaptors](https://www.youtube.com/watch?v=YWayW5ePpkY) — Chris Di Bella, CppCon 74 | 75 | [YOMM2](https://github.com/jll63/yomm2) — библиотека для открытых (мульти)методов 76 | 77 | ## Полезные книжки 78 | 79 | Andrei Alexandrescu, "Modern C++ Design: Generic Programming and Design Patterns Applied" — слегка устаревшая классика 80 | 81 | David Vandevoorde, Nicolai M. Josuttis, Douglas Gregor, "C++ Templates: The Complete Guide (2nd ed.)" — всё, что нужно знать о шаблонах в C++17 82 | -------------------------------------------------------------------------------- /book/.gitignore: -------------------------------------------------------------------------------- 1 | build/** 2 | -------------------------------------------------------------------------------- /book/bibliography.bib: -------------------------------------------------------------------------------- 1 | @book{Wirth1976, 2 | added-at = {2011-08-25T16:55:12.000+0200}, 3 | author = {Wirth, Niklaus}, 4 | biburl = {https://www.bibsonomy.org/bibtex/216c905d4dc3cd872303a7122dc7963f8/voj}, 5 | interhash = {3543abd986ab7af1b416c661c1cb164a}, 6 | intrahash = {16c905d4dc3cd872303a7122dc7963f8}, 7 | isbn = {978-0-13-022418-7}, 8 | keywords = {programming}, 9 | publisher = {Prentice-Hall}, 10 | timestamp = {2011-08-25T16:55:12.000+0200}, 11 | title = {Algorithms + Data Structures = Programs}, 12 | year = 1976 13 | } 14 | 15 | @electronic{DynamicLinking, 16 | author = {Ian Wienand}, 17 | title = {PLT and GOT - the key to code sharing and dynamic libraries}, 18 | year = {2011}, 19 | url = {https://www.technovelty.org/linux/plt-and-got-the-key-to-code-sharing-and-dynamic-libraries.html}, 20 | } 21 | 22 | @electronic{ssotWiki, 23 | title = {Single source of truth}, 24 | url = {https://en.wikipedia.org/wiki/Single_source_of_truth}, 25 | } 26 | 27 | @book{ModernCppDesign, 28 | author = {Alexandrescu, Andrei}, 29 | title = {Modern C++ Design: Generic Programming and Design Patterns Applied}, 30 | year = {2001}, 31 | isbn = {0201704315}, 32 | publisher = {Addison-Wesley Longman Publishing Co., Inc.}, 33 | address = {USA}, 34 | } 35 | -------------------------------------------------------------------------------- /book/chapter2.tex: -------------------------------------------------------------------------------- 1 | \chapter{Введение в препроцессор} 2 | Эта глава проклята, не читайте её. 3 | -------------------------------------------------------------------------------- /book/chapter3.tex: -------------------------------------------------------------------------------- 1 | \chapter{Шаблоны и дизайн} 2 | Как и любую другую языковую возможность, важно не только понимать семантику шаблонов, но и уметь выстраивать с их помощью хороший дизайн модулей приложения. 3 | Данная глава посвящена различным устоявшимся техникам и подходам к дизайну с использованием шаблонов. \cite{ModernCppDesign} 4 | 5 | \section{Трейты} 6 | 7 | \section{Curiously recurring template pattern} 8 | 9 | \section{Policy based design} 10 | 11 | \section{Mixins} 12 | 13 | \section{Tag dispatch} 14 | 15 | \section{Полиморфизм} 16 | \todo{Общаяя информация про полиморфизм, классификация его видов, етц} 17 | \subsection{Стирание типов} 18 | 19 | \subsection{Открытые мультиметоды} 20 | 21 | \subsection{Точки кастомизации} 22 | \todo{Ниблоиды, \inlcpp{tag_invoke}} 23 | 24 | \section{Генерация иерархий} 25 | -------------------------------------------------------------------------------- /book/chapter4.tex: -------------------------------------------------------------------------------- 1 | \chapter{Избранные этюды} 2 | Эта глава содержит набор избранных трюков, хаков и этюдов мира метапрограммирования. Автор не несёт ответственности за судьбу читателя в случае попытки применить описанные в этой главе вещи в продакшене. 3 | 4 | \section{Рефлексия перечислений} 5 | 6 | \section{Stateful metaprogramming} 7 | 8 | \section{Рефлексия тривиальных структур} 9 | 10 | \section{X-macros} 11 | 12 | \section{Гигиена макросов} 13 | 14 | \section{Макросные структуры данных} 15 | \subsection{Кортеж} 16 | \subsection{Лист} 17 | \subsection{Гайд} 18 | 19 | \section{Слоты и калькулятор внутри препроцессора} 20 | -------------------------------------------------------------------------------- /book/hyphenation.tex: -------------------------------------------------------------------------------- 1 | 2 | \pghyphenation{russian}{ 3 | ин-стан-ци-а-ци-я 4 | ин-стан-ци-а-ци-ей 5 | ин-стан-ци-а-ци-ю 6 | ин-стан-ци-а-ци-и 7 | ин-стан-ци-а-ци-й 8 | спе-ци-а-ли-за-ци-я 9 | спе-ци-а-ли-за-ци-ей 10 | спе-ци-а-ли-за-ци-ю 11 | спе-ци-а-ли-за-ци-и 12 | спе-ци-а-ли-за-ци-й 13 | } 14 | -------------------------------------------------------------------------------- /book/latexmkrc: -------------------------------------------------------------------------------- 1 | $pdf_mode = 5; 2 | $dvi_mode = $postscript_mode = 0; 3 | @default_files = ('main.tex'); 4 | $out_dir = 'build'; 5 | $compiling_cmd = "mkdir -p build/build"; 6 | $success_cmd = "cp -f ./build/main.pdf ./metaprogramming-book.pdf"; 7 | set_tex_cmds('--shell-escape %O -interaction=nonstopmode %S'); 8 | -------------------------------------------------------------------------------- /book/main.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a5paper, 11pt, openany]{book} 2 | 3 | \title{Метапрограммирование на C++} 4 | \author{Санду Р.А.} 5 | 6 | \input{preamble.tex} 7 | \input{hyphenation.tex} 8 | \bibliography{bibliography} 9 | 10 | \begin{document} 11 | 12 | \maketitle 13 | \tableofcontents 14 | 15 | \include{chapter1} 16 | \include{chapter2} 17 | \include{chapter3} 18 | \include{chapter4} 19 | 20 | 21 | \emergencystretch=3em 22 | \printbibliography 23 | 24 | \end{document} 25 | 26 | -------------------------------------------------------------------------------- /book/metaprogramming-book.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrkol/metaprogramming-course/edf907bc942f7ab31ed83c3e5d98f969c8aaf488/book/metaprogramming-book.pdf -------------------------------------------------------------------------------- /book/preamble.tex: -------------------------------------------------------------------------------- 1 | \usepackage{fontspec} 2 | \usepackage[babelshorthands=true]{polyglossia} 3 | 4 | \setdefaultlanguage{russian} 5 | \setotherlanguage{english} 6 | 7 | 8 | \defaultfontfeatures{Ligatures=TeX} 9 | 10 | \setmainfont{Times New Roman} 11 | \setsansfont{Arial} 12 | \setmonofont[Contextuals=Alternate, Scale=MatchLowercase]{Fira Code} 13 | 14 | \newfontfamily\cyrillicfont{Times New Roman} 15 | \newfontfamily\cyrillicfontsf{Arial} 16 | \newfontfamily\cyrillicfonttt[Contextuals=Alternate, Scale=MatchLowercase]{Fira Code} 17 | 18 | \newfontfamily\englishfont{Times New Roman} 19 | \newfontfamily\englishfontsf{Arial} 20 | \newfontfamily\englishfonttt[Contextuals=Alternate, Scale=MatchLowercase]{Fira Code} 21 | 22 | \usepackage{microtype} % nicer layouts with microtypography tricks 23 | \usepackage{fnpct} % footnotes after punctuation look nicer 24 | 25 | \usepackage[ 26 | inner = 1.5cm, 27 | textwidth=345pt, 28 | bottom = 1.5cm 29 | ]{geometry} 30 | 31 | \usepackage{enumitem} % russian style lists 32 | \setlist{nosep} 33 | \setlist[enumerate]{label={\arabic*)}} 34 | \setlist[itemize]{label={---}} 35 | 36 | \usepackage[outputdir=build]{minted} 37 | \usemintedstyle{vs} 38 | 39 | \newcommand{\inlcpp}[1]{\mintinline[breaklines, breakanywhere]{c++}{#1}} 40 | \newcommand{\mintlbl}[1]{\phantomsection\label{#1}} 41 | 42 | \usepackage{amsfonts} 43 | 44 | \usepackage[dvipsnames]{xcolor} 45 | \usepackage{xurl} 46 | \urlstyle{same} 47 | \usepackage[hidelinks,colorlinks=true]{hyperref} 48 | \hypersetup{ 49 | linkcolor=BrickRed, 50 | citecolor=Green, 51 | filecolor=Mulberry, 52 | urlcolor=NavyBlue, 53 | menucolor=BrickRed, 54 | runcolor=Mulberry, 55 | linkbordercolor=BrickRed, 56 | citebordercolor=Green, 57 | filebordercolor=Mulberry, 58 | urlbordercolor=NavyBlue, 59 | menubordercolor=BrickRed, 60 | runbordercolor=Mulberry 61 | } 62 | 63 | \usepackage[backend=biber, 64 | bibencoding=utf8, 65 | sorting=none, 66 | style=gost-numeric, 67 | language=autobib, 68 | autolang=other, 69 | clearlang=true, 70 | defernumbers=true, 71 | sortcites=true, 72 | ]{biblatex} 73 | 74 | \usepackage{easy-todo} 75 | 76 | \overfullrule=10pt 77 | -------------------------------------------------------------------------------- /codestyle.md: -------------------------------------------------------------------------------- 1 | # Code style 2 | 3 | В основном полагайтесь на [гугловый стайлгайд](https://google.github.io/styleguide/cppguide.html). В спорных моментах полагайтесь на чувство прекрасного. Пишите код так, чтобы проверяющий смог его прочитать. 4 | 5 | В нашем курсе будет не малое количество специфики, по форматированию которой нет консенсуса. При написании трёхэтажных шаблонов рекомендуется (но не требуется) использовать [haskell-стиль](https://kowainik.github.io/posts/2019-02-06-style-guide#alignment): 6 | ```c++ 7 | using MyList = 8 | filter 9 | < Predicate 10 | , Map 11 | < Metafunction 12 | , List<1, 2, 3, 4> 13 | > 14 | > 15 | ``` 16 | -------------------------------------------------------------------------------- /course/lib/assert.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mpc { 4 | 5 | namespace detail { 6 | 7 | struct AssertException 8 | { }; 9 | 10 | } // namespace detail 11 | 12 | #define MPC_VERIFY(condition) \ 13 | do { \ 14 | [[unlikely]] if (!(condition)) { \ 15 | throw ::mpc::detail::AssertException{}; \ 16 | } \ 17 | } while (false) 18 | 19 | // Message is unused since its main purpose is stress relief. 20 | #define MPC_VERIFYF(condition, message) \ 21 | do { \ 22 | [[unlikely]] if (!(condition)) { \ 23 | throw ::mpc::detail::AssertException{}; \ 24 | } \ 25 | } while (false) 26 | 27 | } // namespace mpc 28 | -------------------------------------------------------------------------------- /course/testing/RegularityWitness.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mpc { 4 | 5 | struct RegularityPolicy { 6 | bool default_constructor = true; 7 | bool copy_constructor = true; 8 | bool move_constructor = true; 9 | bool copy_assignment = true; 10 | bool move_assignment = true; 11 | bool nothrow_destructor = true; 12 | }; 13 | 14 | template 15 | struct RegularityWitness { 16 | RegularityWitness() requires (options.default_constructor) = default; 17 | RegularityWitness(const RegularityWitness&) requires (options.copy_constructor) = default; 18 | RegularityWitness(RegularityWitness&&) noexcept requires (options.move_constructor) = default; 19 | RegularityWitness& operator =(const RegularityWitness&) requires (options.copy_assignment) = default; 20 | RegularityWitness& operator =(RegularityWitness&&) noexcept requires (options.move_assignment) = default; 21 | ~RegularityWitness() noexcept(options.nothrow_destructor) {} 22 | }; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /course/testing/assert.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "debug_trap.hpp" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace mpc::detail { 16 | 17 | void fail(const char* file, size_t line) { 18 | std::cerr << "Assertion failed: file \"" << file << "\", line " << line << std::endl; 19 | #ifndef NDEBUG 20 | psnip_trap(); 21 | #else 22 | std::exit(1); 23 | #endif 24 | } 25 | 26 | void assert_true(const char* file, size_t line, const std::convertible_to auto& v1) { 27 | if (!v1) { 28 | fail(file, line); 29 | } 30 | } 31 | 32 | void assert_eq(const char* file, size_t line, const auto& v1, const auto& v2) { 33 | assert_true(file, line, v1 == v2); 34 | } 35 | 36 | void assert_neq(const char* file, size_t line, const auto& v1, const auto& v2) { 37 | assert_true(file, line, v1 != v2); 38 | } 39 | 40 | template 41 | void assert_nullopt(const char* file, size_t line, const std::optional& v) { 42 | assert_true(file, line, !v.has_value()); 43 | } 44 | 45 | } 46 | 47 | #define MPC_REQUIRE(type, ...) mpc::detail::assert_##type(__FILE__, __LINE__, __VA_ARGS__) 48 | 49 | #define MPC_CONCAT3_IMPL(x, y) x ## y 50 | #define MPC_CONCAT3(x, y) MPC_CONCAT3_IMPL(x, y) 51 | #define MPC_UNIQUE_NAME(x) MPC_CONCAT3(x, __LINE__) 52 | 53 | // Most compilers are unable to properly explain why a 54 | // static_assert(requires { ... }); failed, so we have 55 | // to do a hack. 56 | #define EXPECT_STATIC_TRUE(REQS) \ 57 | template requires (REQS) \ 58 | static constexpr bool MPC_UNIQUE_NAME(Checker) = true; \ 59 | static_assert(MPC_UNIQUE_NAME(Checker)<>); 60 | 61 | #define EXPECT_RUNTIME_OK(expr) \ 62 | EXPECT_NO_THROW((expr)); 63 | 64 | #define EXPECT_RUNTIME_FAIL(expr) \ 65 | EXPECT_THROW((expr), ::mpc::detail::AssertException) \ 66 | << "Consider using MPC_VERIFY for a given sanity check test (See \"lib/assert.hpp\")\n"; -------------------------------------------------------------------------------- /course/testing/concepts.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace mpc { 6 | namespace detail { 7 | 8 | template 9 | decltype(sizeof(T)) testIncomplete(T); 10 | 11 | void testIncomplete(...); 12 | 13 | } 14 | 15 | 16 | template 17 | concept Complete = !std::is_same_v()))>; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /course/testing/debug_trap.hpp: -------------------------------------------------------------------------------- 1 | /* Debugging assertions and traps 2 | * Portable Snippets - https://github.com/nemequ/portable-snippets 3 | * Created by Evan Nemerson 4 | * 5 | * To the extent possible under law, the authors have waived all 6 | * copyright and related or neighboring rights to this code. For 7 | * details, see the Creative Commons Zero 1.0 Universal license at 8 | * https://creativecommons.org/publicdomain/zero/1.0/ 9 | */ 10 | 11 | #if !defined(PSNIP_DEBUG_TRAP_H) 12 | #define PSNIP_DEBUG_TRAP_H 13 | 14 | #if !defined(PSNIP_NDEBUG) && defined(NDEBUG) && !defined(PSNIP_DEBUG) 15 | # define PSNIP_NDEBUG 1 16 | #endif 17 | 18 | #if defined(__has_builtin) && !defined(__ibmxl__) 19 | # if __has_builtin(__builtin_debugtrap) 20 | # define psnip_trap() __builtin_debugtrap() 21 | # elif __has_builtin(__debugbreak) 22 | # define psnip_trap() __debugbreak() 23 | # endif 24 | #endif 25 | #if !defined(psnip_trap) 26 | # if defined(_MSC_VER) || defined(__INTEL_COMPILER) 27 | # define psnip_trap() __debugbreak() 28 | # elif defined(__ARMCC_VERSION) 29 | # define psnip_trap() __breakpoint(42) 30 | # elif defined(__ibmxl__) || defined(__xlC__) 31 | # include 32 | # define psnip_trap() __trap(42) 33 | # elif defined(__DMC__) && defined(_M_IX86) 34 | static inline void psnip_trap(void) { __asm int 3h; } 35 | # elif defined(__i386__) || defined(__x86_64__) 36 | static inline void psnip_trap(void) { __asm__ __volatile__("int3"); } 37 | # elif defined(__thumb__) 38 | static inline void psnip_trap(void) { __asm__ __volatile__(".inst 0xde01"); } 39 | # elif defined(__aarch64__) 40 | static inline void psnip_trap(void) { __asm__ __volatile__(".inst 0xd4200000"); } 41 | # elif defined(__arm__) 42 | static inline void psnip_trap(void) { __asm__ __volatile__(".inst 0xe7f001f0"); } 43 | # elif defined (__alpha__) && !defined(__osf__) 44 | static inline void psnip_trap(void) { __asm__ __volatile__("bpt"); } 45 | # elif defined(_54_) 46 | static inline void psnip_trap(void) { __asm__ __volatile__("ESTOP"); } 47 | # elif defined(_55_) 48 | static inline void psnip_trap(void) { __asm__ __volatile__(";\n .if (.MNEMONIC)\n ESTOP_1\n .else\n ESTOP_1()\n .endif\n NOP"); } 49 | # elif defined(_64P_) 50 | static inline void psnip_trap(void) { __asm__ __volatile__("SWBP 0"); } 51 | # elif defined(_6x_) 52 | static inline void psnip_trap(void) { __asm__ __volatile__("NOP\n .word 0x10000000"); } 53 | # elif defined(__STDC_HOSTED__) && (__STDC_HOSTED__ == 0) && defined(__GNUC__) 54 | # define psnip_trap() __builtin_trap() 55 | # else 56 | # include 57 | # if defined(SIGTRAP) 58 | # define psnip_trap() raise(SIGTRAP) 59 | # else 60 | # define psnip_trap() raise(SIGABRT) 61 | # endif 62 | # endif 63 | #endif 64 | 65 | #if defined(HEDLEY_LIKELY) 66 | # define PSNIP_DBG_LIKELY(expr) HEDLEY_LIKELY(expr) 67 | #elif defined(__GNUC__) && (__GNUC__ >= 3) 68 | # define PSNIP_DBG_LIKELY(expr) __builtin_expect(!!(expr), 1) 69 | #else 70 | # define PSNIP_DBG_LIKELY(expr) (!!(expr)) 71 | #endif 72 | 73 | #if !defined(PSNIP_NDEBUG) || (PSNIP_NDEBUG == 0) 74 | # define psnip_dbg_assert(expr) do { \ 75 | if (!PSNIP_DBG_LIKELY(expr)) { \ 76 | psnip_trap(); \ 77 | } \ 78 | } while (0) 79 | #else 80 | # define psnip_dbg_assert(expr) 81 | #endif 82 | 83 | #endif /* !defined(PSNIP_DEBUG_TRAP_H) */ -------------------------------------------------------------------------------- /course/testing/metafunctions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define CURRY(NameSpace, Name, K1, K2) \ 4 | template \ 5 | struct C##Name { \ 6 | template \ 7 | using Curry = NameSpace::Name; \ 8 | } 9 | -------------------------------------------------------------------------------- /run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | student_repo=$1 4 | task_name=$2 5 | 6 | 7 | final_score=0 8 | 9 | function exit_with_code { 10 | message=$1 11 | code=$2 12 | echo "Error: $message" 13 | echo "Error code: $code" 14 | echo "Final score: $final_score" 15 | exit 0 16 | } 17 | 18 | git ls-remote --exit-code --heads $student_repo $task_name &> /dev/null 19 | errc=$? 20 | if [ $errc -ne 0 ] 21 | then 22 | exit_with_code "Your repo don't have branch ${task_name}" 10 23 | fi 24 | 25 | echo "Cloning solution repo $student_repo" 26 | git clone -b $task_name $student_repo solution &> /dev/null 27 | errc=$? 28 | if [ $errc -ne 0 ] 29 | then 30 | exit_with_code "You provided broken link to the repo. Check that you invited techprogchecker to collaborators." 1 31 | fi 32 | 33 | sudents_repo_path=$(realpath solution) 34 | 35 | echo "Cloning course repo" 36 | git clone "git@github.com:Mrkol/metaprogramming-course.git" course &> /dev/null 37 | errc=$? 38 | if [ $errc -ne 0 ] 39 | then 40 | exit_with_code "Failed to clone course repository. Is the internet down?" 1 41 | fi 42 | 43 | pushd course/tasks &> /dev/null 44 | mkdir build 45 | pushd build &> /dev/null 46 | 47 | echo "Configuring the task build" 48 | cmake .. -DREPOSITORY_PATH=${sudents_repo_path} -DTASK=${task_name} -DNOCOMPILE=YES 49 | 50 | while IFS=' ' read -A test_block 51 | do 52 | block_name=${test_block[1]} 53 | test_names=(${(@s:,:)test_block[2]}) 54 | block_score=${test_block[3]} 55 | echo "Testing block ${block_name}" 56 | for test in $test_names 57 | do 58 | echo "Building test ${test}" 59 | cmake --build . --target ${test} 60 | errc=$? 61 | if [ -z "${test##*nocompile*}" ] 62 | then 63 | if [ $errc -eq 0 ] 64 | then 65 | echo "Failed test ${test} from block ${block_name}!" 66 | echo "Code compiled, while it shouldn't have!" 67 | block_score=0 68 | fi 69 | break 70 | else 71 | if [ $errc -ne 0 ] 72 | then 73 | echo "Failed test ${test} from block ${block_name}!" 74 | echo "Code didn't compile!" 75 | block_score=0 76 | break 77 | fi 78 | fi 79 | echo "Running test ${test}" 80 | ctest -R ${test} 81 | errc=$? 82 | if [ $errc -ne 0 ] 83 | then 84 | echo "Failed test ${test} from block ${block_name}!" 85 | echo "Encountered a runtime assertion/error!" 86 | block_score=0 87 | break 88 | fi 89 | done 90 | if [ $block_score -ne 0 ] 91 | then 92 | echo "SUCCESS! Test ${test} passed!" 93 | fi 94 | (( final_score += block_score )) 95 | done < "../${task_name}/tests/tests" 96 | 97 | echo "Final score: ${final_score}" 98 | 99 | popd 2> /dev/null > /dev/null 100 | popd 2> /dev/null > /dev/null 101 | -------------------------------------------------------------------------------- /samples/sem1/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | build 3 | -------------------------------------------------------------------------------- /samples/sem1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | project(test) 4 | 5 | set(CMAKE_CXX_STANDARD 20) 6 | set(CMAKE_CXX_STANDARD_REQUIRED True) 7 | 8 | add_executable(main main.cpp asdf.cpp) 9 | -------------------------------------------------------------------------------- /samples/sem1/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Базовый алгоритм 7 | 8 | size_t binarySearch(std::span data, int value) { 9 | size_t left = 0; 10 | size_t right = data.size(); 11 | while (right - left > 1) { 12 | size_t middle = std::midpoint(left, right); 13 | if (data[middle] <= value) { 14 | left = middle; 15 | } 16 | else { 17 | right = middle; 18 | } 19 | } 20 | return left; 21 | } 22 | 23 | // Macro 24 | 25 | // #define MAKE_BINARY_SEARCH(TYPE) \ 26 | // inline size_t binarySearchWithMacros(std::span data, TYPE value) { \ 27 | // size_t left = 0; \ 28 | // size_t right = data.size(); \ 29 | // while (right - left > 1) { \ 30 | // size_t middle = std::midpoint(left, right); \ 31 | // if (data[middle] <= value) { \ 32 | // left = middle; \ 33 | // } \ 34 | // else { \ 35 | // right = middle; \ 36 | // } \ 37 | // } \ 38 | // return left; \ 39 | // } 40 | 41 | // MAKE_BINARY_SEARCH(float); 42 | 43 | 44 | // C-style 45 | 46 | // size_t binarySearch(std::span data, 47 | // size_t oneObjectSize, const std::byte* value, 48 | // bool (*comparator)(const std::byte*, const std::byte*)) { 49 | 50 | // size_t left = 0; 51 | // size_t right = data.size() / oneObjectSize; 52 | // while (right - left > 1) { 53 | // size_t middle = std::midpoint(left, right); 54 | // if (comparator(&data[middle * oneObjectSize], value)) { 55 | // left = middle; 56 | // } else { 57 | // right = middle; 58 | // } 59 | // } 60 | // return left; 61 | // } 62 | 63 | 64 | // ООП 65 | 66 | // struct IComparable 67 | // { 68 | // virtual bool compareTo(const IComparable&) const = 0; 69 | 70 | // virtual ~IComparable() = default; 71 | // }; 72 | 73 | // struct Int : IComparable 74 | // { 75 | // int value; 76 | 77 | // bool compareTo(const IComparable& other) const override 78 | // { 79 | // return value < dynamic_cast(other).value; 80 | // } 81 | // }; 82 | 83 | // size_t binarySearch(std::span data, 84 | // const IComparable& value) { 85 | // size_t left = 0; 86 | // size_t right = data.size(); 87 | // while (right - left > 1) { 88 | // size_t middle = std::midpoint(left, right); 89 | // if (data[middle]->compareTo(value)) { 90 | // left = middle; 91 | // } 92 | // else { 93 | // right = middle; 94 | // } 95 | // } 96 | // return left; 97 | // } 98 | 99 | 100 | // template 101 | // T binarySearchTemplate(std::span data, T value) { 102 | // size_t left = 0; 103 | // size_t right = data.size(); 104 | // while (right - left > 1) { 105 | // size_t middle = std::midpoint(left, right); 106 | // if (data[middle] <= value) { 107 | // left = middle; 108 | // } 109 | // else { 110 | // right = middle; 111 | // } 112 | // } 113 | // return left; 114 | // } 115 | 116 | int main(int argc, char** argv) 117 | { 118 | return 0; 119 | } 120 | -------------------------------------------------------------------------------- /samples/sem2/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | build 3 | -------------------------------------------------------------------------------- /samples/sem2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | project(test) 4 | 5 | set(CMAKE_CXX_STANDARD 20) 6 | set(CMAKE_CXX_STANDARD_REQUIRED True) 7 | 8 | add_executable(main main.cpp) 9 | -------------------------------------------------------------------------------- /samples/sem2/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | 8 | 9 | template 10 | requires requires(T t) { --t; } 11 | void f(T); 12 | 13 | template 14 | requires requires(T t) { --t; } && requires(T t) 15 | void f(T); 16 | 17 | 18 | int main(int argc, char** argv) 19 | { 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /samples/sem3/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | build 3 | -------------------------------------------------------------------------------- /samples/sem3/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | project(test) 4 | 5 | set(CMAKE_CXX_STANDARD 20) 6 | set(CMAKE_CXX_STANDARD_REQUIRED True) 7 | 8 | 9 | add_executable(main main.cpp) 10 | add_compile_options("-stdlib=libc++") 11 | -------------------------------------------------------------------------------- /samples/sem3/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | 7 | int main() 8 | { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /samples/sem3/operations.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "typelist.hpp" 7 | 8 | 9 | // List that contains all of Ts 10 | template 11 | using PackToList = void; // TODO 12 | 13 | // Append T to the end of TL 14 | template 15 | using Append = void; // TODO 16 | 17 | 18 | static_assert( 19 | std::same_as 20 | < PackToList 21 | , Cons 22 | < float 23 | , Cons 24 | < double 25 | , Cons 26 | > 27 | > 28 | > 29 | ); 30 | 31 | static_assert( 32 | std::same_as 33 | < Append 34 | < PackToList 35 | , int 36 | > 37 | , PackToList 38 | >); 39 | 40 | 41 | // Concatenate 42 | 43 | template 44 | using Concatenate = void; // TODO 45 | 46 | static_assert( 47 | std::same_as 48 | < Concatenate, Cons> 49 | , PackToList 50 | > 51 | ); 52 | 53 | 54 | // Head, Tail 55 | 56 | // First element of TL 57 | template 58 | using Head = void; // TODO 59 | 60 | // All elements of TL but the first one 61 | template 62 | using Tail = void; // TODO 63 | 64 | 65 | static_assert( 66 | std::same_as 67 | < Head>>> 68 | , void 69 | > 70 | ); 71 | 72 | 73 | static_assert( 74 | std::same_as 75 | < Tail>>> 76 | , Cons> 77 | > 78 | ); 79 | 80 | // Length + 81 | 82 | template 83 | constexpr size_t Length = 0; // TODO 84 | 85 | static_assert(Length> == 3); 86 | 87 | // Reverse 88 | 89 | template 90 | using Reverse = void; // TODO 91 | 92 | 93 | static_assert(std::same_as>, PackToList>); 94 | 95 | 96 | // Map 97 | 98 | template class F, class TL> 99 | using Map = void; // TODO 100 | 101 | template 102 | using AddStar = T*; 103 | 104 | static_assert( 105 | std::same_as 106 | < Map> 107 | , PackToList 108 | >); 109 | 110 | 111 | // Intersperse 112 | 113 | template 114 | using Intersperse = void; // TODO 115 | 116 | static_assert( 117 | std::same_as 118 | < Intersperse> 119 | , PackToList 120 | > 121 | ); 122 | 123 | // First 124 | 125 | // Первые N элементов TL. Если элементов меньше -- весь TL 126 | template 127 | using FirstN = void; // TODO 128 | 129 | static_assert( 130 | std::same_as 131 | < FirstN<3, PackToList> 132 | , PackToList 133 | > 134 | ); 135 | static_assert( 136 | std::same_as 137 | < FirstN<3, PackToList> 138 | , PackToList 139 | > 140 | ); 141 | 142 | 143 | // Last 144 | 145 | // Последние N элементов TL. Если их меньше -- весь TL 146 | template 147 | using LastN = void; // TODO 148 | 149 | static_assert( 150 | std::same_as 151 | < LastN<3, PackToList> 152 | , PackToList 153 | > 154 | ); 155 | 156 | -------------------------------------------------------------------------------- /samples/sem3/typelist.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Как задать type list? 4 | 5 | 6 | -------------------------------------------------------------------------------- /samples/sem4/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | build 3 | -------------------------------------------------------------------------------- /samples/sem4/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | project(test) 4 | 5 | set(CMAKE_CXX_STANDARD 20) 6 | set(CMAKE_CXX_STANDARD_REQUIRED True) 7 | 8 | 9 | add_executable(main main.cpp) 10 | add_compile_options("-stdlib=libc++") 11 | -------------------------------------------------------------------------------- /samples/sem4/function.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // TODO: implement 4 | #include 5 | #define Function std::function 6 | -------------------------------------------------------------------------------- /samples/sem4/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "function.hpp" 8 | 9 | void foo(Function f) { 10 | f(0); 11 | } 12 | 13 | struct FunctionalObject { 14 | void operator()(int) { 15 | std::cout << "Hello from struct!"; 16 | }; 17 | }; 18 | 19 | void banana(int) {} 20 | 21 | void test_function() { 22 | Function f( 23 | [called_once = false](int) mutable { 24 | if (!std::exchange(called_once, true)) 25 | std::cout << "Hello!"; 26 | }); 27 | 28 | Function g(FunctionalObject{}); 29 | g(0); 30 | 31 | foo(f); 32 | foo(f); 33 | } 34 | 35 | int main() { 36 | test_function(); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /samples/sem5/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | build 3 | -------------------------------------------------------------------------------- /samples/sem5/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | project(test) 4 | 5 | set(CMAKE_CXX_STANDARD 20) 6 | set(CMAKE_CXX_STANDARD_REQUIRED True) 7 | 8 | 9 | add_executable(main main.cpp) 10 | add_compile_options("-stdlib=libc++ -fsanitize=address,undefined") 11 | -------------------------------------------------------------------------------- /samples/sem5/any.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #define Any std::any 5 | -------------------------------------------------------------------------------- /samples/sem5/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "any.hpp" 8 | 9 | 10 | 11 | int main() { 12 | Any a = 42; 13 | 14 | std::cout << any_cast(a) << std::endl; 15 | 16 | Any b = std::string("Hello!"); 17 | a = b; 18 | 19 | std::cout << any_cast(a) << std::endl; 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /samples/sem6/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | build 3 | -------------------------------------------------------------------------------- /samples/sem6/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | project(test) 4 | 5 | set(CMAKE_CXX_STANDARD 20) 6 | set(CMAKE_CXX_STANDARD_REQUIRED True) 7 | 8 | 9 | add_executable(main main.cpp) 10 | add_compile_options("-stdlib=libc++ -fsanitize=address,undefined") 11 | -------------------------------------------------------------------------------- /samples/sem6/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | template 12 | constexpr bool DepFalse = false; 13 | 14 | #define FOR_TEMPLATE(Is) \ 15 | [&](std::index_sequence) \ 16 | { \ 17 | ([&]() { 18 | 19 | #define END_FOR_TEMPLATE(SIZE) \ 20 | }() , ...); \ 21 | } \ 22 | (std::make_index_sequence{}); 23 | 24 | template 25 | struct Tag 26 | { 27 | friend auto loophole(Tag); 28 | }; 29 | template 30 | struct Loophole 31 | { 32 | friend auto loophole(Tag) {}; 33 | }; 34 | 35 | template 36 | consteval size_t counter() 37 | { 38 | if constexpr (requires { loophole(Tag{}); }) { 39 | return counter(); 40 | } else { 41 | (void) Loophole{}; 42 | return I; 43 | } 44 | } 45 | 46 | std::array a; 47 | std::array b; 48 | std::array c; 49 | 50 | template 51 | auto helper() { return __PRETTY_FUNCTION__; } 52 | 53 | template 54 | auto helper() { return __PRETTY_FUNCTION__; } 55 | 56 | struct Foo 57 | { 58 | int a; 59 | int b; 60 | int c; 61 | }; 62 | 63 | int main() 64 | { 65 | std::cout << a.size() << '\n'; 66 | std::cout << b.size() << '\n'; 67 | std::cout << c.size() << '\n'; 68 | std::tuple tuple{0, 42.f, 420.0, 'A', "Hi"}; 69 | 70 | FOR_TEMPLATE(I) 71 | { 72 | std::cout << std::get(tuple) << '\n'; 73 | } 74 | END_FOR_TEMPLATE(std::tuple_size_v); 75 | 76 | std::cout << helper<&Foo::a>() << '\n'; 77 | 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /samples/sem7/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | build 3 | -------------------------------------------------------------------------------- /samples/sem7/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | project(test) 4 | 5 | set(CMAKE_CXX_STANDARD 20) 6 | set(CMAKE_CXX_STANDARD_REQUIRED True) 7 | 8 | 9 | add_executable(main main.cpp) 10 | add_compile_options("-stdlib=libc++ -fsanitize=address,undefined") 11 | -------------------------------------------------------------------------------- /samples/sem7/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | template 15 | struct Tag {}; 16 | 17 | template 18 | struct CreatorInterface { 19 | virtual std::unique_ptr create(Tag) = 0; 20 | }; 21 | 22 | template 23 | struct AbstractFactory : private CreatorInterface... { 24 | using CreatorInterface::create...; 25 | 26 | virtual ~AbstractFactory() = default; 27 | }; 28 | 29 | struct Cat { virtual ~Cat() = default; }; 30 | struct Dog { virtual ~Dog() = default; }; 31 | struct Cow { virtual ~Cow() = default; }; 32 | 33 | struct Sphinx : Cat {}; 34 | struct Spaniel : Dog {}; 35 | struct BlackAngus : Cow {}; 36 | 37 | template 38 | struct TypeTuple {}; 39 | 40 | template 41 | struct ConcreteCreator : FinalParent {}; 42 | 43 | template 44 | < class Implementation, class... Implementations 45 | , class Interface, class... Interfaces 46 | , class FinalParent 47 | > 48 | struct ConcreteCreator 49 | < TypeTuple 50 | , TypeTuple 51 | , FinalParent 52 | > 53 | : ConcreteCreator 54 | < TypeTuple 55 | , TypeTuple 56 | , FinalParent 57 | > { 58 | std::unique_ptr create(Tag) override { 59 | return std::make_unique(); 60 | } 61 | }; 62 | 63 | template 64 | struct ConcreteFactory; 65 | 66 | template 67 | struct ConcreteFactory 68 | < TypeTuple 69 | , TypeTuple 70 | > 71 | : ConcreteCreator 72 | < TypeTuple 73 | , TypeTuple 74 | , AbstractFactory 75 | > { 76 | }; 77 | 78 | 79 | template 80 | struct ValueTag {}; 81 | 82 | template 83 | struct TupleElement { 84 | T t; 85 | 86 | T& get(ValueTag) { 87 | return t; 88 | } 89 | 90 | T& get(Tag) { 91 | return t; 92 | } 93 | }; 94 | 95 | template 96 | struct Tuple1; 97 | 98 | template 99 | struct Tuple1, Ts...> 100 | : private TupleElement... { 101 | using TupleElement::get...; 102 | }; 103 | 104 | template 105 | using Tuple = Tuple1, Ts...>; 106 | 107 | 108 | 109 | template 110 | struct Tuple2 {}; 111 | 112 | template 113 | struct Tuple2 : private Tuple2 { 114 | template 115 | auto& get() { 116 | if constexpr (I == 0) return t; 117 | else return Tuple::template get(); 118 | } 119 | 120 | T t; 121 | }; 122 | 123 | template 124 | decltype(auto) get(Tuple&& t) { 125 | return std::forward(t).get(ValueTag{}); 126 | } 127 | 128 | template 129 | decltype(auto) get(Tuple&& t) { 130 | return std::forward(t).get(Tag{}); 131 | } 132 | 133 | 134 | int main() { 135 | std::unique_ptr> factory = 136 | std::make_unique< 137 | ConcreteFactory 138 | < TypeTuple 139 | , TypeTuple 140 | > 141 | >(); 142 | 143 | auto cat = factory->create(Tag{}); 144 | std::cout << typeid(*cat).name() << std::endl; 145 | 146 | Tuple tuple{}; 147 | 148 | get(tuple) = 4; 149 | 150 | std::cout << get<2>(tuple) << std::endl; 151 | 152 | return 0; 153 | } 154 | -------------------------------------------------------------------------------- /samples/sem8/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "array": "cpp", 4 | "exception": "cpp", 5 | "iterator": "cpp", 6 | "initializer_list": "cpp", 7 | "iosfwd": "cpp", 8 | "istream": "cpp", 9 | "new": "cpp", 10 | "ostream": "cpp", 11 | "tuple": "cpp", 12 | "type_traits": "cpp", 13 | "utility": "cpp", 14 | "streambuf": "cpp", 15 | "iostream": "cpp", 16 | "any": "cpp", 17 | "atomic": "cpp", 18 | "bit": "cpp", 19 | "*.tcc": "cpp", 20 | "cctype": "cpp", 21 | "clocale": "cpp", 22 | "cmath": "cpp", 23 | "compare": "cpp", 24 | "concepts": "cpp", 25 | "cstdarg": "cpp", 26 | "cstddef": "cpp", 27 | "cstdint": "cpp", 28 | "cstdio": "cpp", 29 | "cstdlib": "cpp", 30 | "cwchar": "cpp", 31 | "cwctype": "cpp", 32 | "deque": "cpp", 33 | "string": "cpp", 34 | "unordered_map": "cpp", 35 | "vector": "cpp", 36 | "algorithm": "cpp", 37 | "functional": "cpp", 38 | "memory": "cpp", 39 | "memory_resource": "cpp", 40 | "numeric": "cpp", 41 | "optional": "cpp", 42 | "random": "cpp", 43 | "string_view": "cpp", 44 | "system_error": "cpp", 45 | "limits": "cpp", 46 | "numbers": "cpp", 47 | "ranges": "cpp", 48 | "span": "cpp", 49 | "stdexcept": "cpp", 50 | "typeinfo": "cpp", 51 | "variant": "cpp" 52 | } 53 | } -------------------------------------------------------------------------------- /samples/sem8/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | project(test) 4 | 5 | set(CMAKE_CXX_STANDARD 20) 6 | set(CMAKE_CXX_STANDARD_REQUIRED True) 7 | 8 | 9 | add_executable(main main.cpp) 10 | add_compile_options("-stdlib=libc++ -fsanitize=address,undefined") 11 | -------------------------------------------------------------------------------- /samples/sem8/any_object.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | template 9 | concept DependentFalse = false; 10 | 11 | inline constexpr std::size_t SMALL_BUFFER_SIZE = 40; 12 | inline constexpr std::size_t ALIGNMENT = 64; 13 | 14 | union Object { 15 | void* dynObject_; 16 | alignas(ALIGNMENT) std::array staticObject_; 17 | }; 18 | 19 | 20 | template 21 | struct ReplaceOne { 22 | using type = T; 23 | }; 24 | 25 | template 26 | struct ReplaceOne { 27 | using type = To; 28 | }; 29 | template 30 | struct ReplaceOne { 31 | using type = const To; 32 | }; 33 | template 34 | struct ReplaceOne { 35 | using type = To&; 36 | }; 37 | template 38 | struct ReplaceOne { 39 | using type = const To&; 40 | }; 41 | template 42 | struct ReplaceOne { 43 | using type = To&&; 44 | }; 45 | 46 | template 47 | struct ReplaceThisImpl; 48 | 49 | template 50 | struct ReplaceThisImpl { 51 | using Type = typename ReplaceOne::type( 52 | typename ReplaceOne::type...); 53 | }; 54 | 55 | template 56 | struct TagInvocableImpl; 57 | 58 | template 59 | struct TagInvocableImpl { 60 | static constexpr bool value = mpc::tag_invocable; 61 | }; 62 | 63 | template 64 | struct DoTagInvoke; 65 | 66 | template 67 | struct DoTagInvoke { 68 | static auto make_tag_invoke() { 69 | static constexpr auto prepare_arg = [](auto&& arg) -> decltype(auto) { 70 | if constexpr (std::is_same_v, Object>) { 71 | using Tptr = std::conditional_t< 72 | std::is_const_v>, 73 | const T*, T*>; 74 | if constexpr (sizeof(T) < SMALL_BUFFER_SIZE) 75 | return *std::launder(reinterpret_cast(arg.staticObject_.data())); 76 | else 77 | return *static_cast(arg.dynObject_); 78 | } 79 | else { 80 | return std::forward(arg); 81 | } 82 | }; 83 | return +[](ArgsErased... args) -> RetErased { 84 | static_assert(!std::is_same_v, Object>); 85 | return mpc::tag_invoke(CPO{}, prepare_arg(std::forward(args))...); 86 | }; 87 | } 88 | }; 89 | 90 | 91 | 92 | template 93 | struct vtable_entry 94 | { 95 | using ModifiedSignature = typename ReplaceThisImpl::Type; 96 | // DONE: replace this_ with Object& 97 | ModifiedSignature* fptr = nullptr; 98 | 99 | template 100 | // requires mpc::tag_invocable 101 | // requires TagInvocableImpl::value 102 | static vtable_entry create() 103 | { 104 | return vtable_entry{ 105 | .fptr = DoTagInvoke::make_tag_invoke() 106 | }; 107 | }; 108 | 109 | 110 | void setImpl(CPO, ModifiedSignature f) { 111 | fptr = f; 112 | } 113 | auto getImpl(CPO) const { 114 | return fptr; 115 | } 116 | }; 117 | 118 | template 119 | struct vtable : vtable_entry... 120 | { 121 | using vtable_entry::getImpl...; 122 | using vtable_entry::setImpl...; 123 | 124 | template 125 | auto get() const 126 | { 127 | return getImpl(CPO{}); 128 | } 129 | 130 | template 131 | static vtable create() 132 | { 133 | vtable object; 134 | ((object.setImpl(CPOs{}, vtable_entry::template create().fptr)), ...); 135 | return object; 136 | }; 137 | }; 138 | 139 | template 140 | struct inline_vtable_storage : vtable { 141 | inline_vtable_storage() = default; 142 | 143 | template 144 | inline_vtable_storage(const T&) 145 | : vtable{vtable::template create()} { 146 | } 147 | 148 | using vtable::get; 149 | }; 150 | 151 | template 152 | struct class_specific_vtable_storage 153 | { 154 | static vtable * get_vtable() { 155 | static vtable static_vtable = vtable::create(); 156 | return &static_vtable; 157 | } 158 | }; 159 | 160 | template 161 | struct indirect_vtable_storage 162 | { 163 | 164 | template 165 | indirect_vtable_storage(const T&) 166 | : vt(class_specific_vtable_storage::get_vtable()) { 167 | } 168 | 169 | indirect_vtable_storage() = default; 170 | 171 | template 172 | auto get() const { 173 | return vt->template get(); 174 | } 175 | 176 | vtable *vt = nullptr; // ptr to **static** storage 177 | }; 178 | 179 | template 180 | class any_object 181 | { 182 | using vtable_t = vtable; 183 | public: 184 | any_object() = default; 185 | 186 | template 187 | requires (!std::derived_from, any_object> 188 | && !std::same_as, any_object> 189 | && std::copyable>) 190 | any_object(T&& t) 191 | : vt_{t} { 192 | using Value = std::remove_cvref_t; 193 | 194 | static_assert(alignof(Value) <= ALIGNMENT); 195 | 196 | if constexpr (sizeof(Value) <= SMALL_BUFFER_SIZE) 197 | new(object_.staticObject_.data()) Value(std::forward(t)); 198 | else 199 | object_.dynObject_ = new Value(std::forward(t)); 200 | 201 | destructor_ = &destroy; 202 | copy_ = ©; 203 | move_ = &move; 204 | } 205 | 206 | any_object(any_object&& other) { 207 | move_(object_, other.object_); 208 | destructor_ = other.destructor_; 209 | copy_ = other.copy_; 210 | move_ = other.move_; 211 | vt_ = other.vt_; 212 | } 213 | 214 | any_object& operator=(any_object&& other) { 215 | if (this != &other) { 216 | clear(); 217 | 218 | move_(object_, other.object_); 219 | destructor_ = std::exchange(other.destructor_, nullptr); 220 | copy_ = std::exchange(other.copy_, nullptr); 221 | move_ = std::exchange(other.move_, nullptr); 222 | vt_ = std::exchange(other.vt_, decltype(other.vt_){}); 223 | } 224 | 225 | return *this; 226 | } 227 | 228 | any_object(const any_object& other) { 229 | destructor_ = other.destructor_; 230 | copy_ = other.copy_; 231 | vt_ = other.vt_; 232 | copy_(object_, other.object_); 233 | } 234 | 235 | any_object& operator=(const any_object& other) { 236 | if (this != &other) { 237 | clear(); 238 | destructor_ = other.destructor_; 239 | copy_ = other.copy_; 240 | vt_ = other.vt_; 241 | copy_(object_, other.object_); 242 | } 243 | return *this; 244 | } 245 | 246 | ~any_object() { 247 | clear(); 248 | } 249 | 250 | void clear() { 251 | if (destructor_) 252 | { 253 | destructor_(object_); 254 | copy_ = nullptr; 255 | destructor_ = nullptr; 256 | move_ = nullptr; 257 | vt_ = {}; 258 | } 259 | } 260 | 261 | private: 262 | template 263 | friend T& any_cast(any_object& a) { 264 | if (a.destructor_ == &any_object::destroy) 265 | { 266 | if constexpr (sizeof(T) <= SMALL_BUFFER_SIZE) 267 | return *std::launder(reinterpret_cast(a.object_.staticObject_.data())); 268 | else 269 | return *static_cast(&a.object_.dynObject_); 270 | } 271 | else 272 | throw std::runtime_error("Bad any_object cast!"); 273 | } 274 | 275 | template 276 | static void destroy(Object& ptr) 277 | { 278 | if constexpr (sizeof(T) < SMALL_BUFFER_SIZE) 279 | std::launder(reinterpret_cast(ptr.staticObject_.data()))->~T(); 280 | else 281 | delete static_cast(ptr.dynObject_); 282 | } 283 | 284 | template 285 | static void copy(Object& to, const Object& from) 286 | { 287 | if constexpr (sizeof(T) < SMALL_BUFFER_SIZE) 288 | new(to.staticObject_.data()) T( 289 | *std::launder(reinterpret_cast(from.staticObject_.data()))); 290 | else 291 | to.dynObject_ = new T(*static_cast(from.dynObject_)); 292 | } 293 | 294 | template 295 | static void move(Object& to, Object& from) 296 | { 297 | if constexpr (sizeof(T) < SMALL_BUFFER_SIZE) 298 | { 299 | auto* object = std::launder(reinterpret_cast(from.staticObject_.data())); 300 | new(to.staticObject_.data()) T(std::move(*object)); 301 | object->~T(); 302 | from.dynObject_ = nullptr; 303 | } 304 | else 305 | to.dynObject_ = std::exchange(from.dynObject_, nullptr); 306 | } 307 | 308 | // TODO: tag_invoke(CPOs)... 309 | 310 | operator Object& () { return object_; } 311 | operator const Object& () const { return object_; } 312 | 313 | template 314 | friend decltype(auto) tag_invoke(CPO, Args&&... args) 315 | requires requires(std::tuple t) { std::get(t); } { 316 | (([&](T&& arg) mutable { 317 | if constexpr (std::derived_from, any_object>) { 318 | auto func = arg.vt_.template get(); 319 | // static_assert(DependentFalse); 320 | func(std::forward(args)...); 321 | } 322 | }(args)), ...); 323 | } 324 | 325 | // TODO: cross-cast to any_object with a (non-strict) subset of CPOs? 326 | 327 | private: 328 | Object object_{nullptr}; 329 | 330 | // TODO: replace with something CPO-ish? 331 | void(*copy_)(Object&, const Object&) {nullptr}; 332 | void(*move_)(Object&, Object&) {nullptr}; 333 | void(*destructor_)(Object&) {nullptr}; 334 | 335 | // TODO: store and use CPO vtable 336 | std::conditional_t, indirect_vtable_storage> 338 | vt_; 339 | }; 340 | -------------------------------------------------------------------------------- /samples/sem8/main.cpp: -------------------------------------------------------------------------------- 1 | #include "tag_invoke.hpp" 2 | #include "any_object.hpp" 3 | #include "shapes.hpp" 4 | 5 | 6 | 7 | 8 | 9 | int main() { 10 | shapes::square sq(42); 11 | 12 | shapes::any_shape1 s{sq}; 13 | 14 | shapes::area(s); 15 | // auto s2 = shapes::scale_by(s, 2); 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /samples/sem8/shapes.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "tag_invoke.hpp" 3 | 4 | #include 5 | 6 | namespace shapes 7 | { 8 | inline constexpr struct width_t { 9 | using type_erased_signature_t = float(const mpc::this_&); 10 | 11 | template 12 | requires mpc::tag_invocable 13 | float operator()(const T& x) const { 14 | return mpc::tag_invoke(*this, x); 15 | } 16 | } width; 17 | 18 | inline constexpr struct height_t { 19 | using type_erased_signature_t = float(const mpc::this_&); 20 | 21 | template 22 | requires mpc::tag_invocable 23 | float operator()(const T& x) const { 24 | return mpc::tag_invoke(*this, x); 25 | } 26 | } height; 27 | 28 | inline constexpr struct area_t { 29 | using type_erased_signature_t = float(const mpc::this_&); 30 | 31 | template 32 | requires mpc::tag_invocable 33 | float operator()(const T& x) const { 34 | return mpc::tag_invoke(*this, x); 35 | } 36 | } area; 37 | 38 | inline constexpr struct scale_by_t { 39 | template 40 | requires mpc::tag_invocable 41 | auto operator()(const T& x, float ratio) const 42 | -> mpc::tag_invoke_result_t { 43 | return mpc::tag_invoke(*this, x, ratio); 44 | } 45 | } scale_by; 46 | 47 | template 48 | concept shape = 49 | std::copyable && 50 | requires(const T& s, float ratio) { 51 | shapes::width(s); 52 | shapes::height(s); 53 | shapes::area(s); 54 | { shapes::scale_by(s, ratio) } -> std::same_as; 55 | }; 56 | 57 | ////////////////////////////////////////////////////////////////// 58 | 59 | struct square { 60 | float size; 61 | public: 62 | explicit square(float size) : size(size) {} 63 | 64 | friend float tag_invoke(mpc::tag_t, const square& s) { 65 | return s.size; 66 | } 67 | 68 | friend float tag_invoke(mpc::tag_t, const square& s) { 69 | return s.size; 70 | } 71 | 72 | friend float tag_invoke(mpc::tag_t, const square& s) { 73 | std::cout << "area"; 74 | return s.size * s.size; 75 | } 76 | 77 | friend square tag_invoke(mpc::tag_t, const square& s, float ratio) { 78 | std::cout << "scale_by"; 79 | return square{s.size * ratio}; 80 | } 81 | }; 82 | 83 | using any_shape1 = any_object< 84 | true, 85 | mpc::tag_t, 86 | mpc::tag_t, 87 | mpc::tag_t>; 88 | 89 | // square sq; 90 | // any_shape1 a = sq; 91 | // shapes::width(a); -> width(sq) 92 | 93 | 94 | // using any_shape2 = mpc::any_object< 95 | // shapes::width, 96 | // shapes::height, 97 | // shapes::area, 98 | // mpc::overload(shapes::scale_by)>; 99 | 100 | class any_shape : public any_object< 101 | true, 102 | mpc::tag_t, 103 | mpc::tag_t, 104 | mpc::tag_t, 105 | mpc::tag_t(shapes::scale_by)>> 106 | { 107 | private: 108 | using base_t = any_object< 109 | true, 110 | mpc::tag_t, 111 | mpc::tag_t, 112 | mpc::tag_t, 113 | mpc::tag_t(shapes::scale_by)>>; 114 | public: 115 | // Inherit the constructors 116 | using base_t::base_t; 117 | }; 118 | 119 | } 120 | -------------------------------------------------------------------------------- /samples/sem8/tag_invoke.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | namespace mpc { 8 | namespace _tag_invoke { 9 | void tag_invoke() = delete; 10 | 11 | struct tag_invoke_fn { 12 | template 13 | constexpr auto operator()(CPO cpo, Args&&... args) const 14 | noexcept(noexcept(tag_invoke(cpo, (Args&&)args...))) 15 | -> decltype(tag_invoke(cpo, (Args&&)args...)) /* SFINAE-friendliness? */ { 16 | return tag_invoke(cpo, (Args&&)args...); 17 | } 18 | }; 19 | } 20 | 21 | inline namespace _cpo { 22 | inline constexpr _tag_invoke::tag_invoke_fn tag_invoke; 23 | } 24 | 25 | template 26 | using tag_t = std::decay_t; 27 | 28 | template 29 | concept tag_invocable = std::invocable; 30 | 31 | template 32 | concept nothrow_tag_invocable = tag_invocable && 33 | std::is_nothrow_invocable_v; 34 | 35 | template 36 | using tag_invoke_result = std::invoke_result; 37 | 38 | template 39 | using tag_invoke_result_t = typename tag_invoke_result::type; 40 | 41 | 42 | struct this_ {}; 43 | 44 | 45 | namespace detail { 46 | 47 | template 48 | struct overloaded_cpo; 49 | 50 | }; // namespace detail 51 | 52 | 53 | template 54 | struct OverloadedCpo { 55 | OverloadedCpo() = default; 56 | OverloadedCpo(CPO) {} 57 | 58 | using type_erased_signature_t = Signature; 59 | 60 | template 61 | decltype(auto) operator()(Args&&... args) { 62 | return mpc::tag_invoke(CPO{}, std::forward(args)...); 63 | } 64 | }; 65 | template 66 | constexpr OverloadedCpo overloaded_storage{}; 67 | 68 | template 69 | constexpr auto& overload(CPO) { 70 | // return detail::overloaded_cpo{}; 71 | 72 | return overloaded_storage; 73 | }; 74 | } // namespace mpc 75 | -------------------------------------------------------------------------------- /solutions.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | }, 6 | { 7 | "path": "../solutions" 8 | } 9 | ], 10 | "settings": { 11 | "cmake.autoSelectActiveFolder": false, 12 | "files.associations": { 13 | "*.ipp": "cpp", 14 | "*.tpp": "cpp", 15 | "array": "cpp", 16 | "atomic": "cpp", 17 | "bit": "cpp", 18 | "*.tcc": "cpp", 19 | "cctype": "cpp", 20 | "clocale": "cpp", 21 | "cmath": "cpp", 22 | "compare": "cpp", 23 | "concepts": "cpp", 24 | "cstddef": "cpp", 25 | "cstdint": "cpp", 26 | "cstdio": "cpp", 27 | "cstdlib": "cpp", 28 | "cwchar": "cpp", 29 | "cwctype": "cpp", 30 | "deque": "cpp", 31 | "string": "cpp", 32 | "unordered_map": "cpp", 33 | "vector": "cpp", 34 | "exception": "cpp", 35 | "algorithm": "cpp", 36 | "functional": "cpp", 37 | "iterator": "cpp", 38 | "memory": "cpp", 39 | "memory_resource": "cpp", 40 | "numeric": "cpp", 41 | "optional": "cpp", 42 | "random": "cpp", 43 | "string_view": "cpp", 44 | "system_error": "cpp", 45 | "tuple": "cpp", 46 | "type_traits": "cpp", 47 | "utility": "cpp", 48 | "initializer_list": "cpp", 49 | "iosfwd": "cpp", 50 | "iostream": "cpp", 51 | "istream": "cpp", 52 | "limits": "cpp", 53 | "new": "cpp", 54 | "numbers": "cpp", 55 | "ostream": "cpp", 56 | "span": "cpp", 57 | "stdexcept": "cpp", 58 | "streambuf": "cpp", 59 | "typeinfo": "cpp" 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /tasks/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | cmake-build-debug/ 3 | cmake-build-debug-gcc/ 4 | cmake-build-debug-clang/ 5 | cmake-build-release-gcc/ 6 | cmake-build-release-clang/ 7 | .idea/ 8 | Testing -------------------------------------------------------------------------------- /tasks/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.20) 2 | 3 | project(METAPROGRAMMING) 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | # deps 7 | # -------------------- 8 | include(../third_party/get_deps.cmake) 9 | # -------------------- 10 | 11 | set(REPOSITORY_PATH "" CACHE STRING "Path to the root of solution repository") 12 | set(TASK "" CACHE STRING "Task name") 13 | option(NOCOMPILE "Build `nocompile` tests in addition to normal ones; `nocompile` tests must not compile" OFF) 14 | 15 | 16 | if (TASK STREQUAL "") 17 | message(FATAL_ERROR "Specify task name with -DTASK option.") 18 | endif() 19 | 20 | if (REPOSITORY_PATH STREQUAL "") 21 | message(FATAL_ERROR "Specify path to solution repository with -DREPOSITORY_PATH option.") 22 | else() 23 | cmake_path(SET REPOSITORY_PATH NORMALIZE ${REPOSITORY_PATH}) 24 | if (IS_ABSOLUTE "${REPOSITORY_PATH}") 25 | set(SOLUTION_PATH "${REPOSITORY_PATH}/${TASK}") 26 | else() 27 | get_filename_component(SOLUTION_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${REPOSITORY_PATH}/${TASK}" ABSOLUTE) 28 | endif() 29 | endif() 30 | 31 | 32 | message(STATUS "Looking for solution in ${SOLUTION_PATH}") 33 | if(NOT IS_DIRECTORY "${SOLUTION_PATH}") 34 | message(FATAL_ERROR "No such directory") 35 | endif() 36 | 37 | # -------------------- 38 | 39 | function (make_test name) 40 | add_executable("${name}" ${ARGN}) 41 | target_link_libraries("${name}" gtest gtest_main gmock) 42 | add_test(NAME "${name}" COMMAND "${name}") 43 | endfunction() 44 | 45 | # -------------------- 46 | 47 | message(STATUS "Building tests for ${TASK}") 48 | 49 | enable_testing() 50 | add_compile_options(-Wall -Wextra) 51 | include_directories("../course" "${SOLUTION_PATH}") 52 | add_subdirectory("${TASK}/tests") 53 | -------------------------------------------------------------------------------- /tasks/annotations/README.md: -------------------------------------------------------------------------------- 1 | Задача 7. Annotation processing 2 | ======================== 3 | 4 | ## Предыстория 5 | 6 | Имея возможность интроспекции, можно обощить решения многих задач. Boost.PFR умеет генерировать для поддеживаемых структур специализацию `std::hash`, операторы ввода и вывода в поток и операторы сравнения. Другие варианты применения: бинарная сериализация, ORM. 7 | 8 | Иногда может быть полезно сообщить функции, использующей интроспекцию, какую-то дополнительную информацию о конкретном поле. Например, указать название колонки в базе данных или исключить поле из рассмотрения в операторах сравнения. В экосистеме Java для передачи такой контекстной информации используются аннотации. Мы попробуем реализовать поддержку рефлекшна с аннотациями в C++. 9 | 10 | В C++ есть два встроенных способа передать контекстную информацию: директивы препроцессора и аттрибуты. Ни тот, ни другой нам не подходят, так как их использование потребовало бы разработки внешних инструментов, выходящих за рамки языка. Вместо этого для аннотирования полей мы будем использовать другие поля: 11 | 12 | ```cpp 13 | struct S { 14 | int x; // no annotation 15 | 16 | Annotate _annot1; 17 | char y; // annotated with Transient 18 | 19 | Annotate _annot2; 20 | Annotate _annot3; 21 | float z; // annotated with Transient, NoCompare 22 | 23 | Annotate _annot4; 24 | float w; // annotated with Transient, NoCompare 25 | }; 26 | ``` 27 | 28 | У аннотаций могут быть аргументы. Мы будем передавать их как типовые параметры шаблона: 29 | 30 | ```cpp 31 | struct MySerializableStruct { 32 | int x; 33 | 34 | Annotate> _annot1; 35 | uint32_t checksum; // annotated with Checksum with parameter Crc32 36 | }; 37 | ``` 38 | 39 | ## Подготовка 40 | 41 | Вспомните, как выполняются различные операции над списками типов. Самые базовые из них [реализованы в стандартной библиотеке](https://en.cppreference.com/w/cpp/header/tuple) для `std::tuple`. 42 | 43 | Вспомните пары про рефлексию в C++. 44 | 45 | Вспомните, как использовать [template template parameters](https://en.cppreference.com/w/cpp/language/template_parameters). 46 | 47 | ## Задача 48 | 49 | Рассмотрим класс-шаблон `Annotate`: 50 | 51 | ```cpp 52 | template 53 | class Annotate {}; 54 | ``` 55 | 56 | Будем считать, что все поля типа `Annotate` описывают аннотации к ближайшему следующему полю, имеющему тип, отличный от `Annotate`. 57 | 58 | Реализуйте шаблон `Describe`: 59 | 60 | ```cpp 61 | template 62 | struct Describe { 63 | static constexpr size_t num_fields = /* number of fields */ 64 | 65 | template 66 | using Field = /* field descriptor (see below) */; 67 | }; 68 | ``` 69 | 70 | `num_fields` — число полей структуры `T` без учёта полей типа `Annotate`. 71 | 72 | Для каждого `I` от 0 до `num_fields - 1`, `Field` — тип, описывающий `I`-е поле в порядке объявления в структуре `T` без учёта полей-аннотаций, со следующими членами: 73 | 74 | ```cpp 75 | struct /* unspecified (field descriptor) */ { 76 | using Type = /* type of field */; 77 | using Annotations = Annotate; 78 | 79 | template