diff --git a/6-InRealEnvironment/1-SetUpEnvironment.ipynb b/6-InRealEnvironment/1-SetUpEnvironment.ipynb index 88f8052b134655a6020cbee4c35fc4d01ec80fe8..c9bb5db72b0af415df58c21f12569a5dd5f6a197 100644 --- a/6-InRealEnvironment/1-SetUpEnvironment.ipynb +++ b/6-InRealEnvironment/1-SetUpEnvironment.ipynb @@ -287,7 +287,7 @@ "\n", "We will illustrate in next notebook a basic use of CMake.\n", "\n", - "**Important:** Nowadays build systems can leverage the powerful computer on which they run and use several processors at the same time. Depending on the tool you use, the default build might be sequential or parallel (on the few I have used, only `ninja` assumes a parallel build by default). Make sure you know your t\n" + "**Important:** Nowadays build systems can leverage the powerful computer on which they run and use several processors at the same time. Depending on the tool you use, the default build might be sequential or parallel (on the few I have used, only `ninja` assumes a parallel build by default). Make sure you know how your build tool works and that you're leveraging parallel builds!\n" ] }, { diff --git a/6-InRealEnvironment/3-Compilers.ipynb b/6-InRealEnvironment/3-Compilers.ipynb index 7e7639ee76a200c63b19d8ea8b8f347fc39c3f51..47d603652353fa2591d11fb6a101bf3b89dc0e05 100644 --- a/6-InRealEnvironment/3-Compilers.ipynb +++ b/6-InRealEnvironment/3-Compilers.ipynb @@ -165,9 +165,13 @@ "\n", "There is currently a new C++ standard every three years, but there is a very noticeable lag for all features to be supported by both the compilers and their associated standard library (some C++ 20 features such as `std::format` library still aren't supported in February 2024).\n", "\n", - "You may check [here](https://en.cppreference.com/w/cpp/compiler_support) what is the state of support for any given features; each compiler also gets its own page but this one has the merit of providing information for many compilers at once.\n", - "\n", - "\n", + "You may check [here](https://en.cppreference.com/w/cpp/compiler_support) what is the state of support for any given features; each compiler also gets its own page but this one has the merit of providing information for many compilers at once." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "## c++filt\n", "\n", "We won't delve at all into the details, but just know that to allow stuff such as function overload, namespaces (that we'll cover [shortly](../5-Namespace.ipynb)) and so on, C++ proceed to something called _mangling_ (see [here](http://web.mit.edu/tibbetts/Public/inside-c/www/mangling.html) for instance if you're curious about this process). That's the reason in linker error messages you may see stuff such as `_ZN9cdnalizer11rewriteHTMLINS_6apache8IteratorEcEET_RKSsRKNS_6ConfigES3_S3_St8functionIFS3_RKS3_SB_EES9_IFvSsEE`\n", diff --git a/7-Appendix/0-main.ipynb b/7-Appendix/0-main.ipynb index 1f99850dbdd98435862f358722c51b5535f1233b..b7afe9e100985c3d7a0398377dbf7dfe78733577 100644 --- a/7-Appendix/0-main.ipynb +++ b/7-Appendix/0-main.ipynb @@ -22,6 +22,8 @@ "* [Switch](./Switch.ipynb) is the less important one: it just explains `switch` statement and the syntax caveats you might encounter with them. It was mentioned in the [early notebook](../1-ProceduralProgramming/2-Conditions-and-loops.ipynb#switch-statement) about conditions.\n", "\n", "* [StringView](./StringView.ipynb) explains briefly the whereabouts of `std::string_view` which was introduced in C++ 17 - and which will be expanded to other types in C++ 20.\n", + "\n", + "* [Some guidance about how to deal with compilation errors](./CompilationErrors.ipynb).\n", "\n" ] }, diff --git a/7-Appendix/CompilationErrors.ipynb b/7-Appendix/CompilationErrors.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..bc6b28dcb10b3f4c6034cc1bf3ef82d157c3bd54 --- /dev/null +++ b/7-Appendix/CompilationErrors.ipynb @@ -0,0 +1,304 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# [Getting started in C++](./) - [Appendix](./0-main.ipynb) - [Dealing with compilation errors](./CompilationErrors.ipynb)" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": { + "jp-MarkdownHeadingCollapsed": true + }, + "source": [ + "## Introduction\n", + "\n", + "It is easy to be annoyed by the compilation process in C or C++: you write code, want to check how it goes, and that pesky compiler keeps preventing you to just do that by complaining about insignificant stuff as inadequate syntax, sometimes in a really confusing way (and it used to be much worse 20 years ago - compilers aim to be much more user-friendly now).\n", + "\n", + "It is better though to adopt the opposite view: the compiler is not an opponent, but an _ally_ - and in the end you really benefit from identifying issues at compile time rather than at runtime.\n", + "\n", + "The purpose of this notebook is to provide hints how to use to your own advantage the compiler and your build tool.\n" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## Take error messages from top to bottom" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "When compiling even a single file (more on that below), the compiler may lay out plenty of compilation errors, even if most of the time they stop at some points (typically after around 20 errors have been printed).\n", + "\n", + "That doesn't mean there are really 20 (or more) locations in your source file that needs to be fixed: sometimes the compiler can be very confused and isn't able to parse correctly the file.\n", + "\n", + "This is especially true after very usual mistakes such as missing ')', '}' or ';'.\n", + "\n", + "So what I (Sébastien) do is almost always begin at the top of the errors and see if solving this one explains the following errors or not.\n", + "\n", + "I sometimes even redirect the output of the compilation into a file so that I can easily access the top. To my knowledge there's no clean way to do it more properly with the build tool directly; I would be delighted if someone proved me wrong on this one because the following sequence is not entirely satisfactory:\n" + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "```shell\n", + "ninja -j 1 2>&1 | tee compilation_output.txt\n", + "head -n 50 compilation_output.txt\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "(we'll come to the `-j 1` just below)" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + "## Master when (not to) use parallel build" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "\n", + "To leverage the multiple processors available in your computer, you should really take advantage of parallel build... (that we evoked briefly [here](http://localhost:8888/lab/tree/6-InRealEnvironment/1-SetUpEnvironment.ipynb#Build-system)) unless of course you have something better to do (courtesy [Xkcd](https://xkcd.com/303/))" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "It's especially useful when you modified a header file that is heavily used directly or indirectly in your program or your library - all files that depend on it will in this case be recompiled.\n", + "\n", + "But why talk about it here, in a notebook about compilation errors?\n", + "\n", + "The reason is that for compilation errors, I would recommend doing exactly the opposite: once you know something is amiss, you should really strive to concentrate on one error and fix it.\n", + "\n", + "If you're using without much thoughts the output of a parallel build, you may end up providing a fix to a compilation error that is not correct, run a new compilation, and switch to an entirely other issue without realizing that your first fix doesn't work.\n", + "\n", + "I (Sébastien) even go a step further: when I get an error message, I usually copy/paste the command line provided by my build tool (combo CMake / Ninja) until it compiles correctly. For instance if I get:\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "10", + "metadata": {}, + "source": [ + "```shell\n", + "➜ ninja Sources/TestPetscMatrixOperations\n", + "[1/2] Building CXX object Sources/CMakeFiles/TestPetscMatrixOperations.dir/Test/ThirdParty/PETSc/MatrixOperations/test.cpp.o\n", + "FAILED: Sources/CMakeFiles/TestPetscMatrixOperations.dir/Test/ThirdParty/PETSc/MatrixOperations/test.cpp.o \n", + "/Users/sgilles/opt/clang_debug/Openmpi/bin/mpic++ -DDEBUG=1 -DMOREFEM_WITH_MUMPS -DMOREFEM_WITH_SLEPC -DMOREFEM_WITH_SUPERLU_DIST -DMOREFEM_WITH_UMFPACK -D_LIBCPP_DEBUG2=0 -I/Users/sgilles/Codes/MoReFEM/CoreLibrary/Sources -I/Users/sgilles/opt/clang_debug/Petsc/include -I/Users/sgilles/opt/clang_debug/Petsc/config_morefem/include -I/Users/sgilles/opt/clang_debug/Slepc/include -I/Users/sgilles/opt/clang_debug/Slepc/config_morefem/include -I/Users/sgilles/opt/clang_debug/Scotch/include_stub -I/Users/sgilles/opt/clang_debug/Scotch/include -I/Users/sgilles/opt/clang_debug/Lua/include -I/Users/sgilles/opt/clang_debug/Boost/include -I/Users/sgilles/opt/clang_debug/Xtensor/include -I/Users/sgilles/opt/clang_debug/Tclap/include -I/Users/sgilles/opt/clang_debug/Libmeshb/include -stdlib=libc++ -g -std=c++20 -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk -fPIE -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-exit-time-destructors -Wno-global-constructors -Wno-documentation -Wno-documentation-unknown-command -Wno-undefined-func-template -Wno-c11-extensions -Wno-c++1z-extensions -Wno-used-but-marked-unused -Wno-unsafe-buffer-usage -fcolor-diagnostics -fdiagnostics-show-option -MD -MT Sources/CMakeFiles/TestPetscMatrixOperations.dir/Test/ThirdParty/PETSc/MatrixOperations/test.cpp.o -MF Sources/CMakeFiles/TestPetscMatrixOperations.dir/Test/ThirdParty/PETSc/MatrixOperations/test.cpp.o.d -o Sources/CMakeFiles/TestPetscMatrixOperations.dir/Test/ThirdParty/PETSc/MatrixOperations/test.cpp.o -c /Users/sgilles/Codes/MoReFEM/CoreLibrary/Sources/Test/ThirdParty/PETSc/MatrixOperations/test.cpp\n", + "/Users/sgilles/Codes/MoReFEM/CoreLibrary/Sources/Test/ThirdParty/PETSc/MatrixOperations/test.cpp:96:20: error: no member named 'Fill' in 'MoReFEM::GlobalMatrix'\n", + " initial_matrix.Fill(1.);\n", + " ~~~~~~~~~~~~~~ ^\n", + "1 error generated.\n", + "ninja: build stopped: subcommand failed.\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "I copy/paste the command helpfully provided by CMake and work on it until the issue is fixed (in the case above it wouldn't take long of course!)" + ] + }, + { + "cell_type": "markdown", + "id": "12", + "metadata": {}, + "source": [ + "```shell\n", + "/Users/sgilles/opt/clang_debug/Openmpi/bin/mpic++ -DDEBUG=1 -DMOREFEM_WITH_MUMPS -DMOREFEM_WITH_SLEPC -DMOREFEM_WITH_SUPERLU_DIST -DMOREFEM_WITH_UMFPACK -D_LIBCPP_DEBUG2=0 -I/Users/sgilles/Codes/MoReFEM/CoreLibrary/Sources -I/Users/sgilles/opt/clang_debug/Petsc/include -I/Users/sgilles/opt/clang_debug/Petsc/config_morefem/include -I/Users/sgilles/opt/clang_debug/Slepc/include -I/Users/sgilles/opt/clang_debug/Slepc/config_morefem/include -I/Users/sgilles/opt/clang_debug/Scotch/include_stub -I/Users/sgilles/opt/clang_debug/Scotch/include -I/Users/sgilles/opt/clang_debug/Lua/include -I/Users/sgilles/opt/clang_debug/Boost/include -I/Users/sgilles/opt/clang_debug/Xtensor/include -I/Users/sgilles/opt/clang_debug/Tclap/include -I/Users/sgilles/opt/clang_debug/Libmeshb/include -stdlib=libc++ -g -std=c++20 -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk -fPIE -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-exit-time-destructors -Wno-global-constructors -Wno-documentation -Wno-documentation-unknown-command -Wno-undefined-func-template -Wno-c11-extensions -Wno-c++1z-extensions -Wno-used-but-marked-unused -Wno-unsafe-buffer-usage -fcolor-diagnostics -fdiagnostics-show-option -MD -MT Sources/CMakeFiles/TestPetscMatrixOperations.dir/Test/ThirdParty/PETSc/MatrixOperations/test.cpp.o -MF Sources/CMakeFiles/TestPetscMatrixOperations.dir/Test/ThirdParty/PETSc/MatrixOperations/test.cpp.o.d -o Sources/CMakeFiles/TestPetscMatrixOperations.dir/Test/ThirdParty/PETSc/MatrixOperations/test.cpp.o -c /Users/sgilles/Codes/MoReFEM/CoreLibrary/Sources/Test/ThirdParty/PETSc/MatrixOperations/test.cpp\n", + " ```" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "### Know your build tool\n", + "\n", + "When doing refactoring, you might want to know exactly how many files are impacted and don't compile. Usually a build tool will stop as soon as there is a file that chokes (or in case of a parallel build until all the processors meet such a file).\n", + "\n", + "However, there are usually options to tell to compile all that can be compiled; it is for instance `ninja -k 0` for `ninja`.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "14", + "metadata": {}, + "source": [ + "## Read carefully the message\n", + "\n", + "As said in the introduction, compilers are now much more helpful now than what they used to be (see for instance in the example above: it even tries to tell me where exactly in the line the error occurs).\n", + "\n", + "Most of the error messages are now clear enough; when they're not you may find information on the Web as the question has surely been posted somewhere (StackOverflow or similar sites).\n", + "\n", + "c++filt (that we mentioned [here](../6-InRealEnvironment/3-Compilers.ipynb#c++filt)) may be of help if the mangling makes the name of your function / class / whatever too difficult to read." + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "## Don't neglect warnings\n", + "\n", + "When the compilation fails, you may get a jungle of error and warning messages, with an emphasis on errors (that are typically in deep red, whereas warnings are in yellow or purple).\n", + "\n", + "You should however at least read the warning - it might point the very reason for which something is amiss in your code. \n", + "\n", + "Personally I handle them almost exactly the same way as error - it's only when I know for sure that the warning bears no relationship to the following error message that I put it aside.\n" + ] + }, + { + "cell_type": "markdown", + "id": "16", + "metadata": {}, + "source": [ + "## Make sure you're correctly identifying which overload / constructor / etc... is involved\n", + "\n", + "C++ gives you a lot of liberties with naming: same name might be used with a different signature (function overload or multiple constructors for a class for instance) or used in a different namespace.\n", + "\n", + "Make sure you connect the dots well!\n" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "## Include-related potential issues\n", + "\n", + "### Missing include\n", + "\n", + "Sometimes a compilation error might just be due to a missing include!\n", + "\n", + "If the compiler complains about an unknown function, it might just be that the specific source file in which it happens doesn't provide properly the declaration.\n", + "\n", + "### Circular dependency\n", + "\n", + "This is one of the worst compilation you might get, as the compiler won't be able to tell you clearly what happens.\n", + "\n", + "If the compiler complains that it doesn't know about a specific function (or class or method) whereas you're absolutely positive that it is already provided and that you didn't fall into the trap mentioned [just above](http://localhost:8888/lab/tree/7-Appendix/CompilationErrors.ipynb#Make-sure-you're-correctly-identifying-which-overload-/-constructor-/-etc...-is-involved), you might be in a case of circular dependency.\n", + "\n", + "This happens when a file `A.hpp` is included in `B.hpp`, which itself is included in `A.hpp`. Said like that it seems trivial and easy to spot, but in a real code it is often definitely not - as it might be for instance that `B.hpp` includes other headers, which themselves include others, one of which at some point include `A.hpp`.\n", + "\n", + "The way to avoid it is to carefully think your architecture: if there is a clear hierarchy between your components, you should be able to avoid it most of the time.\n", + "\n", + "You should also strive to limit to the minimum the includes to provide in other header files; use [forward declaration](http://localhost:8888/lab/tree/6-InRealEnvironment/2-FileStructure.ipynb#Forward-declaration) when you can, and don't provide includes that are needed only for implementation if said implementation is in a compiled file (of course for `inline` functions `template` classes and functions you don't have the choice). Doing so will also help you limit compilation time anyway.\n", + "\n", + "### Duplicate header guards\n", + "\n", + "Another typical issue is if you provide the same [header guard](http://localhost:8888/lab/tree/6-InRealEnvironment/2-FileStructure.ipynb#Forward-declaration) in two different files.\n", + "\n", + "In this case, one of them will be simply ignored, and its content will probably be sorely missing somewhere along the line in the compilation process.\n", + "\n", + "To avoid this, you should either use `#pragma once` instead, or automate one way or the other the header guard names in the file to ensure unicity.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "18", + "metadata": {}, + "source": [ + "## Template keyword" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "We dedicated a [notebook](../4-Templates/3-Syntax.ipynb) to the specific syntax that might be required to help the compiler understand what is the intention with some specific template tricks.\n", + "\n", + "In both the cases we covered in this notebook, we rejoiced that the compiler was rather helpful if you forgot to use properly one of the expected keyword... but for the `template` keyword (described [here](../4-Templates/3-Syntax.ipynb#Mandatory-template-keyword)) it is fairly recent (around 2022!) - so if you're using a not so recent compiler and get an error message about an unknown method whereas \n", + "\n", + "- The method does exist\n", + "- The method is properly declared at to calling site (it's not a missing include)\n", + "- The method is a template method within a template class\n", + "\n", + "then you should have a look at the explanation in the notebook about the `template` keyword.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "20", + "metadata": {}, + "source": [ + "## Help yourself making the compiler error messages clearer!\n", + "\n", + "Recent versions of C++ have provided many great tools to help you specify at compile time that a condition must be met:\n", + "\n", + "- `static_assert` (since C++ 11)\n", + "- `concept` (since C++ 20)\n", + "\n", + "To which you may also add nifty stuff not directly in the language or STL but that may be added easily such as strong types (here is a [series of FluentCpp posts](https://www.fluentcpp.com/2016/12/05/named-constructors/) explaining the underlying idea; an implementation is provided [there](https://github.com/joboccara/NamedType) but it's easy to write your own implementation tailored for your needs when you read the series).\n", + "\n", + "\n", + "You should really consider using them, especially if you're writing a library, because it might really save a lot of time for the user of the library if a clear message explains exactly how the code didn't match the expectations of the library." + ] + }, + { + "cell_type": "markdown", + "id": "21", + "metadata": {}, + "source": [ + "[© Copyright](../COPYRIGHT.md)" + ] + } + ], + "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" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Images/Xkcd-compiling.png b/Images/Xkcd-compiling.png new file mode 100644 index 0000000000000000000000000000000000000000..1577dcf313cbfaf45511712177447afe74f16995 Binary files /dev/null and b/Images/Xkcd-compiling.png differ