Commit d1b6b652 authored by CHAMONT David's avatar CHAMONT David
Browse files

work for cpp-on-demand/tmp

parent c078f4d2
......@@ -503,7 +503,7 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 10,
"metadata": {
"slideshow": {
"slide_type": "subslide"
......@@ -514,7 +514,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting tmp.gsl-arrays.h\n"
"Writing tmp.gsl-arrays.h\n"
]
}
],
......@@ -643,7 +643,7 @@
},
{
"cell_type": "code",
"execution_count": 19,
"execution_count": 17,
"metadata": {
"slideshow": {
"slide_type": "subslide"
......@@ -662,23 +662,23 @@
"%%file tmp.gsl-arrays.cpp\n",
"\n",
"#include \"tmp.gsl-arrays.h\"\n",
"#include <array>\n",
"#include <vector>\n",
"\n",
"int main()\n",
" {\n",
" std::vector<int> data { 1, 2, 3, 4, 5 } ;\n",
" gsl::span<int> s{ data } ;\n",
" std::vector<double> data { 1.1, 2.2, 3.3, 4.4, 5.5 } ;\n",
" gsl::span<double> s{ data } ;\n",
"\n",
" // run time\n",
" display(s.first(2)) ;\n",
" display(s.last(2)) ;\n",
" display(s.subspan(1,3)) ;\n",
" display(s.subspan(2,3)) ;\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 20,
"execution_count": 18,
"metadata": {
"slideshow": {
"slide_type": "skip"
......@@ -691,7 +691,7 @@
},
{
"cell_type": "code",
"execution_count": 21,
"execution_count": 19,
"metadata": {
"slideshow": {
"slide_type": "fragment"
......@@ -702,9 +702,9 @@
"name": "stdout",
"output_type": "stream",
"text": [
"1 2 \r\n",
"4 5 \r\n",
"2 3 4 \r\n"
"1.1 2.2 \r\n",
"4.4 5.5 \r\n",
"3.3 4.4 5.5 \r\n"
]
}
],
......@@ -714,7 +714,7 @@
},
{
"cell_type": "code",
"execution_count": 22,
"execution_count": 21,
"metadata": {
"slideshow": {
"slide_type": "subslide"
......@@ -742,13 +742,13 @@
" // compile time\n",
" display(s.first<2>()) ;\n",
" display(s.last<2>()) ;\n",
" display(s.subspan<1,3>()) ;\n",
" display(s.subspan<2,3>()) ;\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 23,
"execution_count": 22,
"metadata": {
"slideshow": {
"slide_type": "skip"
......@@ -761,7 +761,7 @@
},
{
"cell_type": "code",
"execution_count": 24,
"execution_count": 23,
"metadata": {
"slideshow": {
"slide_type": "fragment"
......@@ -774,7 +774,7 @@
"text": [
"1 2 \r\n",
"4 5 \r\n",
"2 3 4 \r\n"
"2 3 4 5 \r\n"
]
}
],
......
......@@ -84,16 +84,21 @@
"metadata": {
"celltoolbar": "Diaporama",
"kernelspec": {
"display_name": "C++17",
"language": "C++17",
"name": "xcpp17"
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": "text/x-c++src",
"file_extension": ".cpp",
"mimetype": "text/x-c++src",
"name": "c++",
"version": "17"
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.5"
}
},
"nbformat": 4,
......
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Legacy TMP\n",
"\n",
"Template Meta Programming (TMP) means writing template code which is able to take types as input, apply operations on them, return some types as a result... at compile time."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"The language was **not initially designed with this in mind**, but the specialization feature gave birth to unexpected and efficient idioms, which were harnessed and diverted very early. "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"It was demonstrated that TMP is \"turing-complete\": able to declare variables, make loops, call functions, etc. Writing **such kind of code is complicated**, the compilation time is very long, the error messages undecipherable..."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Yet the libraries made this way deliver **unbeatable performance**. Below, we detail two old TMP idioms."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Make an `if` which depends on some input type\n",
"\n",
"Have a look at the pseudo-code below, meant to move an iterator. We would like to support all the kinds of iterators, and benefit from a direct fast jump when this is a random access iterator."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"template< typename IterT, typename DistT >\n",
"void jump( IterT & iter, DistT d )\n",
" {\n",
" if (iter is a random access iterator)\n",
" {\n",
" iter += d ; // use iterator arithmetic\n",
" } // for random access iters\n",
" else\n",
" {\n",
" if (d >= 0) { while (d--) ++iter ; } // use iterative calls to\n",
" else { while (d++) --iter ; } // ++ or -- for other\n",
" } // iterator categories\n",
" }"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"If the iterator comes from the standard libray: it is defining a nested `iterator_category`. If the latter is an alias of `std::random_access_iterator_tag`, we know that the iterator has an operator `+=`. We could implement `jump()` this way:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"#include <typeinfo>\n",
"\n",
"template< typename IterT, typename DistT >\n",
"void jump( IterT & iter, DistT d )\n",
" {\n",
" if ( typeid(typename std::iterator_traits<IterT>::iterator_category) ==\n",
" typeid(std::random_access_iterator_tag) )\n",
" { iter += d ; } \n",
" else\n",
" {\n",
" if (d >= 0) { while (d--) ++iter ; }\n",
" else { while (d++) --iter ; }\n",
" }\n",
" }"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"This implementation suffers from two drawbacks:\n",
"* the evaluation of `typeid` is done at runtime, as well as the selection of the good branch of code, which is a pitty because the compiler has all the information needed to make this choice upstream.\n",
"* the code doesn't compile ! indeed, the compiler tries to compile the whole of the function regardless of the type of iterator, and will try to compile `iter+=d` even if the iterator is not a direct access iterator.\n",
"\n",
"To solve these two problems, we can delegate the execution of the action to another overloaded function, to which we pass a fictitious argument of type `iterator_tag`, which allows us to select the best implementation to execute, to avoid to compile useless lines (smaller code), and to make the choice at compile time (faster code). This idiom is called \"tag dispatching\":"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"#include <iostream>\n",
"#include <vector>\n",
"#include <list>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"template< typename IterT, typename DistT >\n",
"void jump_impl\n",
" ( IterT & iter, DistT d, std::random_access_iterator_tag )\n",
" {\n",
" iter += d ;\n",
" std::cout<<\"(direct jump)\"<<std::endl ;\n",
" }\n",
"\n",
"template< typename IterT, typename DistT >\n",
"void jump_impl\n",
" ( IterT & iter, DistT d, std::bidirectional_iterator_tag )\n",
" {\n",
" if (d >= 0) { while (d--) ++iter ; }\n",
" else { while (d++) --iter ; }\n",
" std::cout<<\"(incremental jump)\"<<std::endl ;\n",
" }\n",
"\n",
"template< typename IterT, typename DistT >\n",
"void jump( IterT & iter, DistT d )\n",
" { jump_impl( iter, d, typename IterT::iterator_category() ) ; }"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"{\n",
" std::list<int> l = { 0, 1, 2, 3, 4 } ;\n",
" auto litr = l.begin() ;\n",
" jump(litr,2) ;\n",
"\n",
" std::vector<int> v = { 0, 1, 2, 3, 4 } ;\n",
" auto vitr = v.begin() ;\n",
" jump(vitr,3) ;\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"This old idiom is a kind of TMP, because this is a kind of `if...else` which takes types as input, and select the relevant branch at compile time. Only the advent of C++17, and its feature `if constexpr`, has brought a simpler and more natural syntax to achieve the same result."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Make a compile time loop, thanks to recursivity"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"For another glance of template-based metaprogramming, let's look at compile time loops. They are made using recursion, yet not the ordinary one (a function that calls itself), but recursive template instantiations.\n",
"\n",
"The \"hello world\" of recursivity is the calculation of factorials, illustrated below. This is the oldest traditional C++ implementation, where enums are used for static class constants."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"#include <iostream>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"template < unsigned n >\n",
"struct Factorial\n",
" {\n",
" static const int value = n * Factorial<n-1>::value ;\n",
" } ;"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"template <>\n",
"struct Factorial<0>\n",
" {\n",
" static const int value = 1 ;\n",
" } ;"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"std::cout << Factorial<5>::value << std::endl ; // prints 120\n",
"std::cout << Factorial<10>::value << std::endl ; // prints 3628800"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Recursion occurs when `Factorial<n>` refers to `Factorial<n-1>`, and so on, until a final special case stop recursion, here the specialization `Factorial<0>`. Each instance of the pattern is a structure, and each structure hijacks an `enum` to declare a `value` variable. Because we use a recursive template instantiation instead of a traditional loop, each passage of the loop creates its own structure and its own `value`. \n",
"\n",
"The approach is twisted, syntactically verbose, puts the compiler on the grill... but this pre-calculates the function at the compilation stage ! C++11 then C++14 brought the `constexpr` functions, in order to make this possible in a more natural way."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"## Other meta-programming techniques\n",
"\n",
"* Type testing and manipulation: with a high usage of specialization, the compiler can test types and apply them some modifications, such as removing a pointer, etc. The standard library is now well furnished with such utilities.\n",
"* Definition of constraints on the parameters of a template: by exploiting the previous type tests and the mechanism called SFINAE, one can restrain the allowed parameters for a given class or function template. In C++20, we hope that the \"Concepts\" will make this easier.\n",
"* Expression templates: for the evaluation of a mathematical expression based on operators and vector entities, one can automatically create a custom structure for this specific expression, which will only loop once at runtime, without any intermediate result."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## To remember\n",
"\n",
"Template-based metaprogramming is not for the average developer. It is a paradigm that arose by accident in C++, with a not very intuitive syntax, and a still insufficient support by development tools (compilers, debuggers...). However, the efficiency it brings, by moving part of the work from execution to compilation, and its ability to express certain behaviors and notations (**DSEL**) makes it a very promising tool. For library authors, it is a formidable weapon. In C++20, the introduction of **concepts** should be major facilitating step forward in this direction."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Questions ?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"# Exercice\n",
"\n",
"Simplify the code below using `if constexpr` and modern type testing methods."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting tmp.legacy.cpp\n"
]
}
],
"source": [
"%%file tmp.legacy.cpp\n",
"\n",
"#include <iostream>\n",
"#include <vector>\n",
"#include <list>\n",
"\n",
"template< typename IterT, typename DistT >\n",
"void jump_impl\n",
" ( IterT & iter, DistT d, std::random_access_iterator_tag )\n",
" {\n",
" iter += d ;\n",
" std::cout<<\"(direct jump)\"<<std::endl ;\n",
" }\n",
"\n",
"template< typename IterT, typename DistT >\n",
"void jump_impl\n",
" ( IterT & iter, DistT d, std::bidirectional_iterator_tag )\n",
" {\n",
" if (d >= 0) { while (d--) ++iter ; }\n",
" else { while (d++) --iter ; }\n",
" std::cout<<\"(incremental jump)\"<<std::endl ;\n",
" }\n",
"\n",
"template< typename IterT, typename DistT >\n",
"void jump( IterT & iter, DistT d )\n",
" { jump_impl( iter, d, typename IterT::iterator_category() ) ; }\n",
"\n",
"int main()\n",
" {\n",
" std::list<int> l { 0, 1, 2, 3, 4 } ;\n",
" auto litr {l.begin()} ;\n",
" jump(litr,2) ;\n",
" std::cout<<(*litr)<<std::endl ;\n",
" \n",
" std::vector<int> v { 0, 1, 2, 3, 4 } ;\n",
" auto vitr {v.begin()} ;\n",
" jump(vitr,3) ; \n",
" std::cout<<(*vitr)<<std::endl ;\n",
" }"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"!rm -f tmp.legacy.exe && g++ -std=c++17 tmp.legacy.cpp -o tmp.legacy.exe"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(incremental jump)\n",
"2\n",
"(direct jump)\n",
"3\n"
]
}
],
"source": [
"!./tmp.legacy.exe"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"© *CNRS 2022* \n",
"*This document was created by David Chamont. It is available under the [License Creative Commons - Attribution - No commercial use - Shared under the conditions 4.0 International](http://creativecommons.org/licenses/by-nc-sa/4.0/)*"
]
}
],
"metadata": {
"celltoolbar": "Diaporama",