Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
coding-rules
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Scott Snyder
coding-rules
Commits
c21db7c0
Commit
c21db7c0
authored
1 year ago
by
scott snyder
Browse files
Options
Downloads
Patches
Plain Diff
more c++20 updates
parent
c99c922f
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
rules.org
+164
-21
164 additions, 21 deletions
rules.org
with
164 additions
and
21 deletions
rules.org
+
164
−
21
View file @
c21db7c0
...
@@ -155,7 +155,7 @@ package you're working on. But please try to always follow these rules:
...
@@ -155,7 +155,7 @@ package you're working on. But please try to always follow these rules:
An exception for this is xAOD data classes, where the member names are
An exception for this is xAOD data classes, where the member names are
exposed via ROOT for analysis.
exposed via ROOT for analysis.
- *Do not start any other names with* =m_=. [<<m-prefix-reserved>>]
- *Do not start any other names with* =m_=. [<<m-prefix-reserved>>]
...
@@ -208,14 +208,14 @@ class Track;
...
@@ -208,14 +208,14 @@ class Track;
enum State { green, yellow, red };
enum State { green, yellow, red };
#+END_EXAMPLE
#+END_EXAMPLE
- *Typedef names should start with an uppercase letter if they are public
- *Type
alias (type
def
)
names should start with an uppercase letter if they are public
and treated as classes.* [<<typedef-naming>>]
and treated as classes.* [<<typedef-naming>>]
#+BEGIN_EXAMPLE
#+BEGIN_EXAMPLE
typedef
vector<MCParticleKinematics*>
TrackVector
;
using TrackVector =
vector<MCParticleKinematics*>;
#+END_EXAMPLE
#+END_EXAMPLE
- *Alternatively, a typedef name may start with a lower-case letter
- *Alternatively, a
type alias (
typedef
)
name may start with a lower-case letter
and end with* =_t=. [<<typedef-naming-2>>]
and end with* =_t=. [<<typedef-naming-2>>]
This form should be reserved for type names which are not treated
This form should be reserved for type names which are not treated
...
@@ -223,7 +223,7 @@ typedef vector<MCParticleKinematics*> TrackVector;
...
@@ -223,7 +223,7 @@ typedef vector<MCParticleKinematics*> TrackVector;
are private to a class.
are private to a class.
#+BEGIN_EXAMPLE
#+BEGIN_EXAMPLE
typedef unsigned int mycounter_
t;
using mycounter_t = unsigned in
t;
#+END_EXAMPLE
#+END_EXAMPLE
...
@@ -347,7 +347,7 @@ public:
...
@@ -347,7 +347,7 @@ public:
details which are inside its header. This saves time in compilation
details which are inside its header. This saves time in compilation
and avoids an apparent dependency upon the =Line= header file.
and avoids an apparent dependency upon the =Line= header file.
Be careful, however: this does not work if =Line= is actually a
=typedef=
Be careful, however: this does not work if =Line= is actually a
n alias
(as is the case, for example, with many of the xAOD classes).
(as is the case, for example, with many of the xAOD classes).
- *Each header file must contain the declaration for one class only, except for
- *Each header file must contain the declaration for one class only, except for
...
@@ -457,8 +457,8 @@ vector<int> x; // Missing std!
...
@@ -457,8 +457,8 @@ vector<int> x; // Missing std!
Otherwise, the =using= may serve to hide problems with missing
Otherwise, the =using= may serve to hide problems with missing
namespace qualifications in the headers.
namespace qualifications in the headers.
This rule does not apply when =using= is used to define a
new name
This rule does not apply when =using= is used to define a
type alias
for a type
(similarly to =typedef=).
(similarly to =typedef=).
** Control flow
** Control flow
...
@@ -497,6 +497,16 @@ for (std::vector<int>::const_iterator it = v.begin();
...
@@ -497,6 +497,16 @@ for (std::vector<int>::const_iterator it = v.begin();
multiple iterators within the loop. But most simple loops over
multiple iterators within the loop. But most simple loops over
STL ranges are more simply written with a range-based for.
STL ranges are more simply written with a range-based for.
As of C++20, you can initialize additional variables in a
range-based for:
#+BEGIN_EXAMPLE
void foo (const std::vector<float>& v) {
for (int i = 0; float f : v) {
std::cout << i++ << " " << f << "\n";
}
}
#+END_EXAMPLE
- *Switch statements should have a default clause.* [<<switch-default>>]
- *Switch statements should have a default clause.* [<<switch-default>>]
...
@@ -655,6 +665,20 @@ void A::foo()
...
@@ -655,6 +665,20 @@ void A::foo()
defining a symbol for it.
defining a symbol for it.
- *Use <numbers> header for mathematical constants.* [<<math-constants>>]
Basic mathematical constants are available in the header
=<numbers>=. Use these in preference to the =M_= constants from
=math.h= or explicit definitions:
#+BEGIN_EXAMPLE
#include <numbers>
#include <cmath>
double f (double x) {
return std::sin (x * std::numbers::pi);
}
#+END_EXAMPLE
- *Declare each type of variable in a separate declaration statement, and
- *Declare each type of variable in a separate declaration statement, and
do not declare different types (e.g. int and int pointer) in one declaration statement.* [<<separate-declarations>>]
do not declare different types (e.g. int and int pointer) in one declaration statement.* [<<separate-declarations>>]
...
@@ -1211,7 +1235,18 @@ int convertAndBuffer (int* buf, float x)
...
@@ -1211,7 +1235,18 @@ int convertAndBuffer (int* buf, float x)
#+END_EXAMPLE
#+END_EXAMPLE
(As a special case, you can safely convert any pointer type to or from
(As a special case, you can safely convert any pointer type to or from
a =char*=.) The proper way to do such a conversion is with a union.
a =char*=.) The proper way to do such a conversion is with
a =std::bit_cast=:
#+BEGIN_EXAMPLE
#include <bit>
int convertAndBuffer (int* buf, float x)
{
*buf = std::bit_cast<int> (x);
return *buf;
}
#+END_EXAMPLE
Prior to C++20, the recommended way to do this was with a union,
but that should not be used for new code.
** The class interface
** The class interface
...
@@ -1423,6 +1458,38 @@ a = b = c;
...
@@ -1423,6 +1458,38 @@ a = b = c;
will assign =c= to =b= and then =b= to =a= as is the case with built-in objects.
will assign =c= to =b= and then =b= to =a= as is the case with built-in objects.
- *Use* =std::span= *to represent and pass a bounded region of memory.* [<<span>>]
In particular, use =std::span= instead of passing a pointer with
a separate element count (or even worse, a pointer to an array with
no bounds information).
So you can use this:
#+BEGIN_EXAMPLE
#include <span>
int sum (const std::span<int>& s)
{
int ret = 0;
for (int i : s) ret += i;
return ret;
}
#+END_EXAMPLE
instead of
#+BEGIN_EXAMPLE
int sum (const int* p, size_t n)
{
int ret = 0;
for (size_t i = 0; i < n; i++) ret += p[i];
return ret;
}
#+END_EXAMPLE
One might expect that =std::span= would include an =at()= method,
to allow indexing with bounds checking, but that is only available
in C++23. In the meantime, =CxxUtils::span= is very similar to
=std::span= but does implement =at()=.
*** =const= correctness
*** =const= correctness
...
@@ -1493,6 +1560,56 @@ ostream& operator<<(ostream& out, const String& s);
...
@@ -1493,6 +1560,56 @@ ostream& operator<<(ostream& out, const String& s);
closely related member functions is very confusing and is not
closely related member functions is very confusing and is not
recommended.
recommended.
*** Comparisons
- *Define comparions for custom types using* =operator=== *and* =operator<=>=. [<<comparisons>>]
Comparions of for a custom class should be written using
=operator=== (for equality/inequality) and =operator<=>= (for ordering).
The compiler will supply the other comparison operators
(=operator!==, =operator<=, etc.) automatically. Where possible,
=operator<=>= is best defined using the same operator on the
members involved. Examples:
#+BEGIN_EXAMPLE
#include <compare>
#include <tuple>
class S
{
public:
bool operator== (const S& other)
{
return m_key == other.m_key;
}
std::strong_ordering operator<=> (const S& other)
{
return m_key <=> other.m_key;
}
private:
int m_key;
};
class Version
{
public:
bool operator== (const Version& other)
{
return m_major == other.m_major && m_minor == other.m_minor;
}
std::strong_ordering operator<=> (const Version& other)
{
return std::make_tuple (m_major, m_minor) <=>
std::make_tuple (other.m_major, other.m_minor);
}
private:
int m_major;
int m_minor;
};
#+END_EXAMPLE
** =new= and =delete=
** =new= and =delete=
...
@@ -2340,7 +2457,7 @@ safer possibilities.
...
@@ -2340,7 +2457,7 @@ safer possibilities.
=scanf= and =printf= are not type-safe and they are not
=scanf= and =printf= are not type-safe and they are not
extensible. Use =operator>>= and =operator<<= associated with C++ streams
extensible. Use =operator>>= and =operator<<= associated with C++ streams
instead, along with =std::format= to handle formatting (see [[use-format]]).
instead, along with =std::format= to handle formatting (see
use-format
[[use-format]]).
iostream and stdio functions should never be mixed.
iostream and stdio functions should never be mixed.
Example:
Example:
...
@@ -2548,7 +2665,7 @@ some machines, left-to-right on others. -- end note ]
...
@@ -2548,7 +2665,7 @@ some machines, left-to-right on others. -- end note ]
That way, they won't clutter up the header file.
That way, they won't clutter up the header file.
- *Do not declare your own
typedef
for booleans. Use the bool type of C++ for booleans.* [<<use-bool>>]
- *Do not declare your own
alias
for booleans. Use the bool type of C++ for booleans.* [<<use-bool>>]
The =bool= type was not implemented in C. Programmers usually got around
The =bool= type was not implemented in C. Programmers usually got around
the problem by typedefs and/or const declarations. This is no longer
the problem by typedefs and/or const declarations. This is no longer
...
@@ -2638,37 +2755,37 @@ some machines, left-to-right on others. -- end note ]
...
@@ -2638,37 +2755,37 @@ some machines, left-to-right on others. -- end note ]
level.
level.
- *Avoid
us
ing type
def to create
aliases for classes.* [<<avoid-typedef>>]
- *Avoid
creat
ing type aliases for classes.* [<<avoid-typedef>>]
Typedefs are a serious impediment in large systems. While they
Type
aliases (type
defs
)
are a serious impediment in large systems. While they
simplify code for the original author, a system filled with
typedef
s
simplify code for the original author, a system filled with
aliase
s
can be difficult to understand. If the reader encounters a class =A=, he
can be difficult to understand. If the reader encounters a class =A=, he
or she can find an =#include= with ``A.h'' in it to locate a description of =A=;
or she can find an =#include= with ``A.h'' in it to locate a description of =A=;
but
typedef
s carry no context that tell a reader where to find a
but
aliase
s carry no context that tell a reader where to find a
definition. Moreover, most of the generic characteristics obtained
definition. Moreover, most of the generic characteristics obtained
with
typedef
s are better handled by object oriented techniques, like
with
aliase
s are better handled by object oriented techniques, like
polymorphism.
polymorphism.
Typedef
s are acceptible where they provide part of the expected
Aliase
s are acceptible where they provide part of the expected
interface for a class, for example =value_type=, etc.
interface for a class, for example =value_type=, etc.
in classes used with STL algorithms. They are often indispensible
in classes used with STL algorithms. They are often indispensible
in template programming and metaprogramming, and are also part
in template programming and metaprogramming, and are also part
of how xAOD classes and POOL converters are typically defined.
of how xAOD classes and POOL converters are typically defined.
In other contexts, they should be used with care, and should generally
In other contexts, they should be used with care, and should generally
be accompanied with a comment giving the rationale for the
typedef
.
be accompanied with a comment giving the rationale for the
alias
.
Typedef
s may be used as a ``customization point;'' that is, to allow
Aliase
s may be used as a ``customization point;'' that is, to allow
the possibility of changing a type in the future.
the possibility of changing a type in the future.
For example, the auxiliary store code uses integers to identify particular
For example, the auxiliary store code uses integers to identify particular
auxiliary data items. But rather than declaring these as an integer type
auxiliary data items. But rather than declaring these as an integer type
directly, a
typedef
=auxid_t= is used. This allows for the possibility
directly, a
n alias
=auxid_t= is used. This allows for the possibility
of changing the type in the future without having to make changes
of changing the type in the future without having to make changes
throughout the code base. It also makes explicit that variables
throughout the code base. It also makes explicit that variables
of that type are meant to identify auxiliary data items,
of that type are meant to identify auxiliary data items,
rather than being random integers.
rather than being random integers.
A
typedef
may also be used inside a function body to shorten
A
n alias
may also be used inside a function body to shorten
a cumbersome type name; however, this should be used sparingly.
a cumbersome type name; however, this should be used sparingly.
...
@@ -3008,6 +3125,20 @@ foo( 1, 2, 3 )
...
@@ -3008,6 +3125,20 @@ foo( 1, 2, 3 )
fix the whole thing, or code to match.
fix the whole thing, or code to match.
- *Prefer* =using= *to* =typedef=*.* [<<prefer-using>>]
To declare a type alias, prefer the newer =using= syntax:
#+BEGIN_EXAMPLE
using Int_t = int;
#+END_EXAMPLE
to the =typedef= syntax:
#+BEGIN_EXAMPLE
typedef int Int_t;
#+END_EXAMPLE
The =using= syntax makes it clearer what is being defined; it can also
be used to declare templated aliases.
** Comments
** Comments
- *Use Doxygen style comments before class/method/data member declarations.
- *Use Doxygen style comments before class/method/data member declarations.
...
@@ -3130,6 +3261,18 @@ The comment includes the fact that it is the perpendicular distance.
...
@@ -3130,6 +3261,18 @@ The comment includes the fact that it is the perpendicular distance.
** Version 0.8
** Version 0.8
- Updated for C++20.
- Updated for C++20.
- Don't use modules or coroutines.
- Add recommendation to use =<numbers>=.
- Suggest using =auto= to move the return type to the end of a method
signature when returning types defined within the class.
- Suggest not defining template functions without the =template= keyword.
- Recommend =std::format= for formatted output.
- Note that range-for can have init-statements.
- Mention =std::bit_cast=.
- Recommend =using= instead of =typedef=. Rephrase previous references
to =typedef=.
- Comparisons should be defined in terms of =operator=== and =operator<=>=.
- Mention =std::span=.
- Some additional references.
- Some additional references.
- Clarify that non-ascii characters should not be used in identifier names.
- Clarify that non-ascii characters should not be used in identifier names.
- Clarify that variable-length argument lists of varadic template functions
- Clarify that variable-length argument lists of varadic template functions
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment