diff --git a/1-ProceduralProgramming/0-main.ipynb b/1-ProceduralProgramming/0-main.ipynb index 6cbe872cf3dde22cf60e944faf955fd193d5f83b..003c9ef21908b1b3997c819b5c7e9c4049d880c0 100644 --- a/1-ProceduralProgramming/0-main.ipynb +++ b/1-ProceduralProgramming/0-main.ipynb @@ -19,7 +19,8 @@ " * [TP 2](./4b-TP.ipynb)\n", "* [Dynamic allocation](./5-DynamicAllocation.ipynb)\n", "* [Input/output](./6-Streams.ipynb)\n", - " * [TP 3](./6b-TP.ipynb)" + " * [TP 3](./6b-TP.ipynb)\n", + "* [Static and constexpr](./7-StaticAndConstexpr.ipynb)" ] }, { diff --git a/1-ProceduralProgramming/7-StaticAndConstexpr.ipynb b/1-ProceduralProgramming/7-StaticAndConstexpr.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..24bd3c71df17ec4c47ca52b3f272e1f6bf8d7428 --- /dev/null +++ b/1-ProceduralProgramming/7-StaticAndConstexpr.ipynb @@ -0,0 +1,304 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# [Getting started in C++](/) - [Procedural programming](./0-main.ipynb) - [Static and constexpr](./7-StaticAndConstexpr.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "toc": true + }, + "source": [ + "<h1>Table of contents<span class=\"tocSkip\"></span></h1>\n", + "<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#Static-keyword\" data-toc-modified-id=\"Static-keyword-1\">Static keyword</a></span></li><li><span><a href=\"#Constexpr\" data-toc-modified-id=\"Constexpr-2\">Constexpr</a></span></li></ul></div>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Static keyword" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`static` may be seen as _at compile time_, whereas `dynamic` may be seen as _at runtime_.\n", + "\n", + "A reply to this [StackOverflow question](https://stackoverflow.com/questions/572547/what-does-static-mean-in-c) gives a rather good summary of what `static` means in C (we shall see [later](./2-ObjectProgramming/5-static.ipynb) a C++-only keyword that unfortunately share the same moniker):\n", + "\n", + "* Static defined local variables do not lose their value between function calls. In other words they are global variables, but scoped to the local function they are defined in.\n", + "* Static global variables are not visible outside of the C file they are defined in.\n", + "* Static functions are not visible outside of the C file they are defined in.\n", + "\n", + "Only the first one is really relevant in C++, as for quantities that should be accessible only in the current file C++ provides a concept of his own: the [**unnamed namespace**](../6-InRealEnvironment/5-Namespace.ipynb#Unnamed-namespace).\n", + "\n", + "Let's see the remaining case in action:\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <iostream>\n", + "\n", + "void FunctionWithStatic()\n", + "{\n", + " static int n = 0; // This initialisation occurs only at first call\n", + " // But `n` is not destroyed when the end bracket is reached and remains available\n", + " // in subsequent calls. However, `n` is available only inside this function.\n", + " std::cout << \"The function has been called \" << ++n << \" times.\" << std::endl; \n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "{\n", + " for (int i = 0; i < 5; ++i)\n", + " FunctionWithStatic(); \n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It might be used for instance if you need to initialize something on the very first call of a function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "// Pseudo-code\n", + "\n", + "void FunctionWithStatic()\n", + "{\n", + " static bool is_first_call = true;\n", + " \n", + " if (is_first_call)\n", + " {\n", + " // Init stuff here on first call only, for instance something that requires heavy computation.\n", + " // ... \n", + " is_first_call = false;\n", + " }\n", + " \n", + " // ... code executed at each call\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Constexpr\n", + "\n", + "Let's consider the STL container `std::array`, which is the container of choice to store an array which size is known *at compile time* (containers will be seen more in depth in a [future notebook](./notebooks/5-UsefulConceptsAndSTL/3-Containers.ipynb))." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <array>\n", + "\n", + "std::array<double, 3ul> my_array;" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now imagine we want to init such an array with a size that results from a computation; let's say a Fibonacci series:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "// Recursive function - Xeus cling may not appreciate if you call this cell several times.\n", + "\n", + "auto fibonacci (std::size_t n) \n", + "{\n", + " if (n == 0)\n", + " return 0;\n", + " if (n == 1)\n", + " return 1;\n", + " \n", + " return fibonacci(n-1) + fibonacci(n-2);\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <iostream>\n", + "std::cout << fibonacci(5) << std::endl;\n", + "std::cout << fibonacci(10) << std::endl;" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <array>\n", + "std::array<double, fibonacci(4)> my_fibonacci_4_array; // COMPILATION ERROR!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This doesn't seem outlandish: a computation is involved and this computation happens at runtime - even if in fact all the required elements to perform it were available at runtime (there were for instance no argument read from command line involved).\n", + "\n", + "In C++ 03, you had two choices to resolve this\n", + "\n", + "- Using a macro...\n", + "- Or using [metaprogramming](./4-Templates/4-Metaprogramming.ipynb) which is a tad overkill... (and involves boilerplate code).\n", + "\n", + "C++ introduced the keyword `constexpr`, which indicates something happens at compile time. It might be used for just a variable, or for more complicated construct such as functions or classes. The only requirement is that all the subparts used are themselves `constexpr`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "constexpr auto fibonacci_const (std::size_t n) \n", + "{\n", + " if (n == 0)\n", + " return 0;\n", + " if (n == 1)\n", + " return 1;\n", + " \n", + " return fibonacci_const(n-1) + fibonacci_const(n-2);\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <array>\n", + "constexpr auto fibonacci_index = 4ul;\n", + "std::array<double, fibonacci_const(fibonacci_index)> my_fibonacci_4_array; " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`constexpr` function may also be used as runtime function, but in this case their results can't of course be used at compile time." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <iostream>\n", + "\n", + "int i = 7;\n", + "++i; // i is by no stretch a compile time variable!\n", + "\n", + "std::cout << fibonacci_const(7) << std::endl; // ok!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`constexpr` becomes increasingly powerful over time:\n", + "\n", + "- The function `fibonacci_const` above does not in fact work before C++ 14: this version of the standard introduced the possibility to provide several `return` in a `constexpr` function.\n", + "- `constexpr` is added wherever possible in the STL. This is still a work in progress: if you look for instance the [CppReference page](https://en.cppreference.com/w/cpp/algorithm/count) for `std::count` algorithm, you will see this algorithm becomes `constexpr` in C++ 20." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "© _CNRS 2016_ - _Inria 2018-2021_ \n", + "_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_ \n", + "_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "C++17", + "language": "C++17", + "name": "xcpp17" + }, + "language_info": { + "codemirror_mode": "text/x-c++src", + "file_extension": ".cpp", + "mimetype": "text/x-c++src", + "name": "c++", + "version": "17" + }, + "latex_envs": { + "LaTeX_envs_menu_present": true, + "autoclose": false, + "autocomplete": true, + "bibliofile": "biblio.bib", + "cite_by": "apalike", + "current_citInitial": 1, + "eqLabelWithNumbers": true, + "eqNumInitial": 1, + "hotkeys": { + "equation": "Ctrl-E", + "itemize": "Ctrl-I" + }, + "labels_anchors": false, + "latex_user_defs": false, + "report_style_numbering": false, + "user_envs_cfg": false + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of contents", + "title_sidebar": "Contents", + "toc_cell": true, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": true + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/2-ObjectProgramming/5-static.ipynb b/2-ObjectProgramming/5-static.ipynb index 839353352b87d79e9a0173a92224807e0491bd85..ae6b2ed23a0fa7638316dc9616750a5cd8324e56 100644 --- a/2-ObjectProgramming/5-static.ipynb +++ b/2-ObjectProgramming/5-static.ipynb @@ -23,76 +23,9 @@ "source": [ "## Static in C\n", "\n", - "We haven't dealt yet with the keyword `static`, which exists in C and could have been adressed in the procedural part of this lecture.\n", + "As a reminder, we have seen in a [previous notebook](./1-ProceduralProgramming/7-StaticAndConstexpr.ipynb#Static-keyword) the `static` keyword inherited from C. \n", "\n", - "As the C++ concept of **static method** uses up the same keyword and is not entirely related (even if both may be intertwined as we shall see in the [last section](./5-static.ipynb#Static-order-initialization-fiasco---and-its-fix)), we shall first review the C concept before studying the C++ one.\n", - "\n", - "First of all, `static` may be seen as _at compile time_, whereas `dynamic` may be seen as _at runtime_.\n", - "\n", - "A reply to this [StackOverflow question](https://stackoverflow.com/questions/572547/what-does-static-mean-in-c) gives a rather good summary of what `static` means in C:\n", - "\n", - "* Static defined local variables do not lose their value between function calls. In other words they are global variables, but scoped to the local function they are defined in.\n", - "* Static global variables are not visible outside of the C file they are defined in.\n", - "* Static functions are not visible outside of the C file they are defined in.\n", - "\n", - "Only the first one is really relevant in C++, as for quantities that should be accessible only in the current file C++ provides a concept of his own: the [**unnamed namespace**](../6-InRealEnvironment/5-Namespace.ipynb#Unnamed-namespace).\n", - "\n", - "Let's see the remaining case in action:\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#include <iostream>\n", - "\n", - "void FunctionWithStatic()\n", - "{\n", - " static int n = 0; // This initialisation occurs only at first call\n", - " // But `n` is not destroyed when the end bracket is reached and remains available\n", - " // in subsequent calls. However, `n` is available only inside this function.\n", - " std::cout << \"The function has been called \" << ++n << \" times.\" << std::endl; \n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "{\n", - " for (int i = 0; i < 3; ++i)\n", - " FunctionWithStatic(); \n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It might be used for instance if you need to initialize something on the very first call of a function:\n", - "\n", - "````\n", - "void FunctionWithStatic()\n", - "{\n", - " static bool is_first_call = true;\n", - " \n", - " if (is_first_call)\n", - " {\n", - " // Init stuff here on first call only\n", - " ... \n", - " is_first_call = false;\n", - " }\n", - " \n", - " ... // code executed at each call\n", - "}\n", - "\n", - "````" + "What follows is the same keyword used in a very different context (even if the one we already know will pop up in an idion presented here).\n" ] }, {