Skip to content
Snippets Groups Projects
Commit c99c922f authored by scott snyder's avatar scott snyder
Browse files

more c++20 updates

parent c3c186a8
Branches
Tags
No related merge requests found
......@@ -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>>]
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment