diff --git a/rules.org b/rules.org index 21c33f4cea35decd075427a393c8056a2c641cca..d4db7a0ba62879a3ad5847ae7f6792d7263bf8f9 100644 --- a/rules.org +++ b/rules.org @@ -763,7 +763,7 @@ for (auto cell : caloCellContainer) ... auto foo = Foo {x}; #+END_EXAMPLE - There are three sorts of places where it generally makes sense to + There are a few sorts of places where it generally makes sense to use =auto=. - When the type is already evident in the expression and the declaration @@ -785,10 +785,38 @@ auto ufoo = std::make_unique<Foo>(); std::map<int, std::string> m = ..; auto ret = m.insert (std::make_pair (1, "x")); if (ret.second) .... +#+END_EXAMPLE + + - In the case where a class method returns a type defined within the + class, using the =auto= syntax to write the return type at the + end of the signature can make things more readable when the method + is defined out-of-line: + #+BEGIN_EXAMPLE +template <class T> class C { +public: + using ret_t = int; + ret_t foo(); +}; + +// Verbose: the return type is interpreted at the global scope, so it +// needs to be qualified with the class name. +template <class T> +typename C<T>::ret_t C<T>::foo() ... + +// With this syntax, the return type is interpreted within the class scope. +template <class T> +auto C<T>::foo() -> ret_t ... #+END_EXAMPLE - =auto= may also be useful in writing generic template code. - + + In some cases, C++20 allows declaring a template function without + the =template= keyword when the argument is declared as =auto=: + #+BEGIN_EXAMPLE +auto fn (auto x) { return x + 1; } +#+END_EXAMPLE + It is recommended to avoid this syntax for public interfaces. + In general, the decision as to whether or not to use =auto= should be made on the basis of what makes the code easier to _read_. It is bad practice to use it simply to save a few characters @@ -1998,7 +2026,58 @@ private: that must be acquired in both =getSize()= and =push()= in the above example. +** Formatted output + + - *Prefer std::format to printf or iostream formatting.* [<<use-format>>] + + For new code, use the C++20 formatting library to format values to + a string rather than using printf-style formatting or using + iostream manipulators. + + Example: + #+BEGIN_EXAMPLE +#include <format> +... +const char* typ = "ele"; +float energy = 14.2345; +int mask = 323; +std::cout << std::format ("A {1:.2f} GeV {0} mask {2:#06x}.\n", + typ, energy, mask); +// prints: A 14.23 GeV ele mask 0x0143. +#+END_EXAMPLE + + Compare using =printf=-style formatting: + #+BEGIN_EXAMPLE +#include "CxxUtils/StrFormat.h" +... +std::cout << CxxUtils::strformat ("A %.2f GeV %s mask %#06x.\n", + energy, typ, mask); +#+END_EXAMPLE + or =iostream=: + #+BEGIN_EXAMPLE +#include <iomanip> +... +const int default_precision = std::cout.precision(); +const std::ios_base::fmtflags default_flags = std::cout.flags(); +const char default_fill = std::cout.fill(); +std::cout << "A " << std::fixed << std::setprecision(2) << energy + << std::defaultfloat << std::setprecision(default_precision) + << " GeV " << typ << " mask " + << std::hex << "0x" << std::setfill('0') << std::setw(4) << mask + << std::setfill(default_fill) + << ".\n"; +std::cout.flags(default_flags); +#+END_EXAMPLE + + Like the streaming operator, =std::format= has a way of customizing how + a given type is formatted. However, it is somewhat more involved than + for =operator<<=; in addition, =std::format= will not use existing + custom streaming operators. Therefore, for generating printable + representations of class instances, it is probably better in most cases + to use the =iostream= mechanism. + + ** Assertions and error conditions - *Pre-conditions and post-conditions should be checked for validity.* [<<pre-post-conditions>>] @@ -2208,11 +2287,44 @@ Exception: Mine! ** Parts of C++ to avoid Here a set of different items are collected. They highlight parts of -the language which should be avoided, because there are better ways to -achieve the desired results. In particular, programmers should avoid +the language which should be avoided, either because there are better ways to +achieve the desired results or because the language features are still +immature. In particular, programmers should avoid using the old standard C functions, where C++ has introduced new and safer possibilities. + - *Do not use C++ modules.* [<<no-modules>>] + + Modules were introduced in C++20 as a better alternative to =#include=. + If a module is referenced via =import=, it avoids repeatedly parsing + the code as well as avoiding issues that arise due to interference + between headers. However, building modules requires significant + support from the build system, and the support in compilers and + associated tools is still very immature. Even using the standard + library as a module is not fully functional with C++20. + + For now, avoid any use of modules. With C++23, it may be possible + to use standard libraries as modules, but building ATLAS code + as modules will require significant additional development. + + + - *Do not use C++ coroutines.* [<<no-coroutines>>] + + Coroutines allow for a non-linear style of control flow, where one + can return from the middle of a function and then resume execution + from that point at a later time. However, the coroutine interfaces + available in C++20 are quite low-level: they are intended to be used + as building blocks for other library components rather than for + direct use by user code. Further, uncontrolled use of the type + of control flow made possible by coroutines has the potential to be + terribly confusing. + + For now, avoid use of coroutines. If you have a use case that would + greatly benefit from using coroutines, please consult with software + coordination. This recommendation will be revisited for new versions + of C++ which may include easier mechanisms for using coroutines. + + - *Do not use malloc, calloc, realloc, and free. Use new and delete instead.* [<<no-malloc>>] You should avoid all memory-handling functions from the standard @@ -2228,7 +2340,8 @@ safer possibilities. =scanf= and =printf= are not type-safe and they are not extensible. Use =operator>>= and =operator<<= associated with C++ streams - instead. iostream and stdio functions should never be mixed. + instead, along with =std::format= to handle formatting (see [[use-format]]). + iostream and stdio functions should never be mixed. Example: #+BEGIN_EXAMPLE @@ -2249,9 +2362,8 @@ printf("This does not work: %s \n", aCPPString); It is of course acceptable to use stdio functions if you're calling an external library that requires them. - Admittedly, formatting using C++-style streams is more cumbersome - than a C-style format list. If you want to use =printf= style formatting, - see ``CxxUtils/StrFormat.h''. + If you need to use =printf= style formatting, see ``CxxUtils/StrFormat.h''; + however =std::format= is preferred for new code. - *Do not use the ellipsis notation for function arguments.* [<<no-ellipsis>>]