Don't make data members `protected`
Reason
protected data is a source of errors. protected data can be manipulated from an unbounded amount of code in various places. protected data is the class hierarchy equivalent to global data.
Example
???
Alternative
RF: References
Many coding standards, rules, and guidelines have been written for C++, and especially for specialized uses of C++. Many
- focus on lower-level issues, such as the spelling of identifiers
- are written by C++ novices
- see "stopping programmers from doing unusual things" as their primary aim
- aim at portability across many compilers (some 10 years old)
- are written to preserve decades old code bases
- aim at a single application domain
- are downright counterproductive
- are ignored (must be ignored by programmers to get their work done well)
A bad coding standard is worse than no coding standard. However an appropriate set of guidelines are much better than no standards: "Form is liberating."
Why can't we just have a language that allows all we want and disallows all we don't want ("a perfect language")? Fundamentally, because affordable languages (and their tool chains) also serve people with needs that differ from yours and serve more needs than you have today. Also, your needs change over time and a general-purpose language is needed to allow you to adapt. A language that is ideal for today would be overly restrictive tomorrow.
Coding guidelines adapt the use of a language to specific needs. Thus, there cannot be a single coding style for everybody. We expect different organizations to provide additions, typically with more restrictions and firmer style rules.
Reference sections:
- RF.rules: Coding rules
- RF.books: Books with coding guidelines
- RF.C++: C++ Programming (C++11/C++14/C++17)
- RF.web: Websites
- RS.video: Videos about "modern C++"
- RF.man: Manuals
- RF.core: Core Guidelines materials
RF.rules: Coding rules
???.
Has a strong emphasis on code organization and layout.
C++03 and (reasonably) a bit backwards looking.
Geared toward C++17 and (also) older code bases. Google experts are now actively collaborating here on helping to improve these Guidelines, and hopefully to merge efforts so these can be a modern common set they could also recommend.
Document Number 2RDU00001 Rev C. December 2005. For flight control software. For hard-real-time. This means that it is necessarily very restrictive ("if the program fails somebody dies"). For example, no free store allocation or deallocation is allowed to occur after the plane takes off (no memory overflow and no fragmentation allowed). No exception is allowed to be used (because there was no available tool for guaranteeing that an exception would be handled within a fixed short time). Libraries used have to have been approved for mission critical applications. Any similarities to this set of guidelines are unsurprising because Bjarne Stroustrup was an author of JSF++. Recommended, but note its very specific focus.
As the name indicates, this aims for portability across many (old) compilers. As such, it is restrictive.
???.
???.
A very nicely done set of rules (with examples and rationales) done for security-sensitive code. Many of their rules apply generally.
Somewhat brief, based on C++14, and (not unreasonably) adjusted to its domain.
- AUTOSAR Guidelines for the use of the C++14 language in critical and safety-related systems v22.11 (obsolete, replaced by MISRA C++:2023)
- Boost Library Requirements and Guidelines.
- Bloomberg: BDE C++ Coding.
- Facebook: ???
- GCC Coding Conventions.
- Google C++ Style Guide.
- JSF++: JOINT STRIKE FIGHTER AIR VEHICLE C++ CODING STANDARDS.
- MISRA C++:2023 Guidelines for the use C++17 in critical systems.
- Using C++ in Mozilla Code.
- Geosoft.no: C++ Programming Style Guidelines.
- Possibility.com: C++ Coding Standard.
- SEI CERT: Secure C++ Coding Standard.
- High Integrity C++ Coding Standard.
- llvm.
- ???
RF.books: Books with coding guidelines
LCSD05. October 2005.
Addison-Wesley 2014. Each chapter ends with an advice section consisting of a set of recommendations.
Addison-Wesley 2013. Each chapter ends with an advice section consisting of a set of recommendations.
for Programming: Principles and Practice using C++. Mostly low-level naming and layout rules. Primarily a teaching tool.
- Meyers96 Scott Meyers: More Effective C++. Addison-Wesley 1996.
- Meyers97 Scott Meyers: Effective C++, Second Edition. Addison-Wesley 1997.
- Meyers01 Scott Meyers: Effective STL. Addison-Wesley 2001.
- Meyers05 Scott Meyers: Effective C++, Third Edition. Addison-Wesley 2005.
- Meyers15 Scott Meyers: Effective Modern C++. O'Reilly 2015.
- SuttAlex05 Sutter and Alexandrescu: C++ Coding Standards. Addison-Wesley 2005. More a set of meta-rules than a set of rules. Pre-C++11.
- Stroustrup05 Bjarne Stroustrup: A rationale for semantically enhanced library languages.
- Stroustrup14 Stroustrup: A Tour of C++.
- Stroustrup13 Stroustrup: The C++ Programming Language (4th Edition).
- Stroustrup: Style Guide
RF.C++: C++ Programming (C++11/C++14)
A thorough description of the C++ language and standard libraries for experienced programmers.
An overview of the C++ language and standard libraries for experienced programmers.
A textbook for beginners and relative novices.
RF.web: Websites
- isocpp.org
- Bjarne Stroustrup's home pages
- WG21
- Boost<a name="Boost"></a>
- Adobe open source
- Poco libraries
- Sutter's Mill?
- ???
RS.video: Videos about "modern C++"
- Bjarne Stroustrup: C++11 Style. 2012.
- Bjarne Stroustrup: The Essence of C++: With Examples in C++84, C++98, C++11, and C++14. 2013
- All the talks from CppCon ’14
- Bjarne Stroustrup: The essence of C++ at the University of Edinburgh. 2014.
- Bjarne Stroustrup: The Evolution of C++ Past, Present and Future. CppCon 2016 keynote.
- Bjarne Stroustrup: Make Simple Tasks Simple!. CppCon 2014 keynote.
- Bjarne Stroustrup: Writing Good C++14. CppCon 2015 keynote about the Core Guidelines.
- Herb Sutter: Writing Good C++14... By Default. CppCon 2015 keynote about the Core Guidelines.
- CppCon 15
- ??? C++ Next
- ??? Meting C++
- ??? more ???
RF.man: Manuals
- ISO C++ Standard C++11.
- ISO C++ Standard C++14.
- ISO C++ Standard C++17. Committee Draft.
- Palo Alto "Concepts" TR.
- ISO C++ Concepts TS.
- WG21 Ranges report. Draft.
RF.core: Core Guidelines materials
This section contains materials that have been useful for presenting the core guidelines and the ideas behind them:
and here are the slides. In Russian. 2017.
It gives some idea of the ambition level for the Core Guidelines.
- Our documents directory
- Stroustrup, Sutter, and Dos Reis: A brief introduction to C++'s model for type- and resource-safety. A paper with lots of examples.
- Sergey Zubkov: a Core Guidelines talk
- Neil MacIntosh: The Guideline Support Library: One Year Later. CppCon 2016.
- Bjarne Stroustrup: Writing Good C++14. CppCon 2015 keynote.
- Herb Sutter: Writing Good C++14... By Default. CppCon 2015 keynote.
- Peter Sommerlad: C++ Core Guidelines - Modernize your C++ Code Base. ACCU 2017.
- Bjarne Stroustrup: No Littering!. Bay Area ACCU 2016.
Note that slides for CppCon presentations are available (links with the posted videos).
Contributions to this list would be most welcome.
Acknowledgements
Thanks to the many people who contributed rules, suggestions, supporting information, references, etc.:
- Peter Juhl
- Neil MacIntosh
- Axel Naumann
- Andrew Pardoe
- Gabriel Dos Reis
- Zhuang, Jiangang (Jeff)
- Sergey Zubkov
and see the contributor list on the github.
Pro: Profiles
Ideally, we would follow all of the guidelines. That would give the cleanest, most regular, least error-prone, and often the fastest code. Unfortunately, that is usually impossible because we have to fit our code into large code bases and use existing libraries. Often, such code has been written over decades and does not follow these guidelines. We must aim for gradual adoption.
Whatever strategy for gradual adoption we adopt, we need to be able to apply sets of related guidelines to address some set of problems first and leave the rest until later. A similar idea of "related guidelines" becomes important when some, but not all, guidelines are considered relevant to a code base or if a set of specialized guidelines is to be applied for a specialized application area. We call such a set of related guidelines a "profile". We aim for such a set of guidelines to be coherent so that they together help us reach a specific goal, such as "absence of range errors" or "static type safety." Each profile is designed to eliminate a class of errors. Enforcement of "random" rules in isolation is more likely to be disruptive to a code base than delivering a definite improvement.
A "profile" is a set of deterministic and portably enforceable subset of rules (i.e., restrictions) that are designed to achieve a specific guarantee. "Deterministic" means they require only local analysis and could be implemented in a compiler (though they don't need to be). "Portably enforceable" means they are like language rules, so programmers can count on different enforcement tools giving the same answer for the same code.
Code written to be warning-free using such a language profile is considered to conform to the profile. Conforming code is considered to be safe by construction with regard to the safety properties targeted by that profile. Conforming code will not be the root cause of errors for that property, although such errors might be introduced into a program by other code, libraries or the external environment. A profile might also introduce additional library types to ease conformance and encourage correct code.
Profiles summary:
In the future, we expect to define many more profiles and add more checks to existing profiles. Candidates include:
- narrowing arithmetic promotions/conversions (likely part of a separate safe-arithmetic profile)
- arithmetic cast from negative floating point to unsigned integral type (ditto)
- selected undefined behavior: Start with Gabriel Dos Reis's UB list developed for the WG21 study group
- selected unspecified behavior: Addressing portability concerns.
constviolations: Mostly done by compilers already, but we can catch inappropriate casting and underuse ofconst.
Enabling a profile is implementation defined; typically, it is set in the analysis tool used.
To suppress enforcement of a profile check, place a suppress annotation on a language contract. For example:
[[suppress("bounds")]] char* raw_find(char* p, int n, char x) // find x in p[0]..p[n - 1]
{
// ...
}
Now raw_find() can scramble memory to its heart's content. Obviously, suppression should be very rare.
Pro.safety: Type-safety profile
This profile makes it easier to construct code that uses types correctly and avoids inadvertent type punning. It does so by focusing on removing the primary sources of type violations, including unsafe uses of casts and unions.
For the purposes of this section, type-safety is defined to be the property that a variable is not used in a way that doesn't obey the rules for the type of its definition. Memory accessed as a type T should not be valid memory that actually contains an object of an unrelated type U. Note that the safety is intended to be complete when combined also with Bounds safety and Lifetime safety.
An implementation of this profile shall recognize the following patterns in source code as non-conforming and issue a diagnostic.
Type safety profile summary:
- <a name="pro-type-avoidcasts"></a>Type.1: Avoid casts:
- <a name="pro-type-reinterpretcast"></a>Don't use
reinterpret_cast; A strict version of Avoid casts and prefer named casts. - <a name="pro-type-arithmeticcast"></a>Don't use
static_castfor arithmetic types; A strict version of Avoid casts and prefer named casts. - <a name="pro-type-identitycast"></a>Don't cast between pointer types where the source type and the target type are the same; A strict version of Avoid casts.
- <a name="pro-type-implicitpointercast"></a>Don't cast between pointer types when the conversion could be implicit; A strict version of Avoid casts.
Prefer construction or named casts or T{expression}.
always initialize, possibly using default constructors or default member initializers.
- <a name="pro-type-downcast"></a>Type.2: Don't use
static_castto downcast: - <a name="pro-type-constcast"></a>Type.3: Don't use
const_castto cast awayconst(i.e., at all): - <a name="pro-type-cstylecast"></a>Type.4: Don't use C-style
(T)expressionor functionalT(expression)casts: - <a name="pro-type-init"></a>Type.5: Don't use a variable before it has been initialized:
- <a name="pro-type-memberinit"></a>Type.6: Always initialize a data member:
- <a name="pro-type-union"></a>Type.7: Avoid naked union:
- <a name="pro-type-varargs"></a>Type.8: Avoid varargs:
Impact
With the type-safety profile you can trust that every operation is applied to a valid object. An exception can be thrown to indicate errors that cannot be detected statically (at compile time). Note that this type-safety can be complete only if we also have Bounds safety and Lifetime safety. Without those guarantees, a region of memory could be accessed independent of which object, objects, or parts of objects are stored in it.
Pro.bounds: Bounds safety profile
This profile makes it easier to construct code that operates within the bounds of allocated blocks of memory. It does so by focusing on removing the primary sources of bounds violations: pointer arithmetic and array indexing. One of the core features of this profile is to restrict pointers to only refer to single objects, not arrays.
We define bounds-safety to be the property that a program does not use an object to access memory outside of the range that was allocated for it. Bounds safety is intended to be complete only when combined with Type safety and Lifetime safety, which cover other unsafe operations that allow bounds violations.
Bounds safety profile summary:
Pass pointers to single objects (only) and Keep pointer arithmetic simple.
Pass pointers to single objects (only) and Keep pointer arithmetic simple.
Pass pointers to single objects (only) and Keep pointer arithmetic simple.
Use the standard library in a type-safe manner.
- <a name="pro-bounds-arithmetic"></a>Bounds.1: Don't use pointer arithmetic. Use
spaninstead: - <a name="pro-bounds-arrayindex"></a>Bounds.2: Only index into arrays using constant expressions:
- <a name="pro-bounds-decay"></a>Bounds.3: No array-to-pointer decay:
- <a name="pro-bounds-stdlib"></a>Bounds.4: Don't use standard-library functions and types that are not bounds-checked:
Impact
Bounds safety implies that access to an object - notably arrays - does not access beyond the object's memory allocation. This eliminates a large class of insidious and hard-to-find errors, including the (in)famous "buffer overflow" errors. This closes security loopholes as well as a prominent source of memory corruption (when writing out of bounds). Even if an out-of-bounds access is "just a read", it can lead to invariant violations (when the accessed isn't of the assumed type) and "mysterious values."
Pro.lifetime: Lifetime safety profile
Accessing through a pointer that doesn't point to anything is a major source of errors, and very hard to avoid in many traditional C or C++ styles of programming. For example, a pointer might be uninitialized, the nullptr, point beyond the range of an array, or to a deleted object.
See the current design specification here.
Lifetime safety profile summary:
- <a name="pro-lifetime-invalid-deref"></a>Lifetime.1: Don't dereference a possibly invalid pointer:
Impact
Once completely enforced through a combination of style rules, static analysis, and library support, this profile
- eliminates one of the major sources of nasty errors in C++
- eliminates a major source of potential security violations
- improves performance by eliminating redundant "paranoia" checks
- increases confidence in correctness of code
- avoids undefined behavior by enforcing a key C++ language rule
GSL: Guidelines support library
The GSL is a small library of facilities designed to support this set of guidelines. Without these facilities, the guidelines would have to be far more restrictive on language details.
The Core Guidelines support library is defined in namespace gsl and the names might be aliases for standard library or other well-known library names. Using the (compile-time) indirection through the gsl namespace allows for experimentation and for local variants of the support facilities.
The GSL is header only, and can be found at GSL: Guidelines support library. The support library facilities are designed to be extremely lightweight (zero-overhead) so that they impose no overhead compared to using conventional alternatives. Where desirable, they can be "instrumented" with additional functionality (e.g., checks) for tasks such as debugging.
These Guidelines use types from the standard (e.g., C++17) in addition to ones from the GSL. For example, we assume a variant type, but this is not currently in GSL. Eventually, use the one voted into C++17.
Some of the GSL types listed below might not be supported in the library you use due to technical reasons such as limitations in the current versions of C++. Therefore, please consult your GSL documentation to find out more.
For each GSL type below we state an invariant for that type. That invariant holds as long as user code only changes the state of a GSL object using the type's provided member/free functions (i.e., user code does not bypass the type's interface to change the object's value/bits by violating any other Guidelines rule).
Summary of GSL components:
- GSL.view: Views
- GSL.owner: Ownership pointers
- GSL.assert: Assertions
- GSL.util: Utilities
- GSL.concept: Concepts
We plan for a "ISO C++ standard style" semi-formal specification of the GSL.
We rely on the ISO C++ Standard Library and hope for parts of the GSL to be absorbed into the standard library.
GSL.view: Views
These types allow the user to distinguish between owning and non-owning pointers and between pointers to a single object and pointers to the first element of a sequence.
These "views" are never owners.
References are never owners (see R.4). Note: References have many opportunities to outlive the objects they refer to (returning a local variable by reference, holding a reference to an element of a vector and doing push_back, binding to std::max(x, y + 1), etc). The Lifetime safety profile aims to address those things, but even so owner<T&> does not make sense and is discouraged.
The names are mostly ISO standard-library style (lower case and underscore):
T*// TheT*is not an owner, might be null; assumed to be pointing to a single element.T&// TheT&is not an owner and can never be a "null reference"; references are always bound to objects.
The "raw-pointer" notation (e.g. int*) is assumed to have its most common meaning; that is, a pointer points to an object, but does not own it. Owners should be converted to resource handles (e.g., unique_ptr or vector<T>) or marked owner<T*>.
owner<T*>// aT*that owns the object pointed/referred to; might benullptr.
owner is used to mark owning pointers in code that cannot be upgraded to use proper resource handles. Reasons for that include:
- Cost of conversion.
- The pointer is used with an ABI.
- The pointer is part of the implementation of a resource handle.
An owner<T> differs from a resource handle for a T by still requiring an explicit delete.
An owner<T> is assumed to refer to an object on the free store (heap).
If something is not supposed to be nullptr, say so:
T can be any type for which ==nullptr is meaningful.
not_null<T>//Tis usually a pointer type (e.g.,not_null<int*>andnot_null<owner<Foo*>>) that must not benullptr.
span<T>//[p:p+n), constructor from{p, q}and{p, n};Tis the pointer typespan_p<T>//{p, predicate}[p:q)whereqis the first element for whichpredicate(*p)is true
A span<T> refers to zero or more mutable Ts unless T is a const type. All accesses to elements of the span, notably via operator[], are guaranteed to be bounds-checked by default.
Note: GSL's
span(initially calledarray_view) was proposed for inclusion in the C++ standard library, and was adopted (with changes to its name and interface) except only thatstd::spandoes not provide for guaranteed bounds checking. Therefore GSL changedspan's name and interface to trackstd::spanand should be exactly the same asstd::span, and the only difference should be that GSLspanis fully bounds-safe by default. If bounds-safety might affect its interface, then those change proposals should be brought back via the ISO C++ committee to keepgsl::spaninterface-compatible withstd::span. If a future evolution ofstd::spanadds bounds checking,gsl::spancan be removed.
"Pointer arithmetic" is best done within spans. A char* that points to more than one char but is not a C-style string (e.g., a pointer into an input buffer) should be represented by a span.
zstring// achar*supposed to be a C-style string; that is, a zero-terminated sequence ofcharornullptrczstring// aconst char*supposed to be a C-style string; that is, a zero-terminated sequence ofconstcharornullptr
Logically, those last two aliases are not needed, but we are not always logical, and they make the distinction between a pointer to one char and a pointer to a C-style string explicit. A sequence of characters that is not assumed to be zero-terminated should be a span<char>, or if that is impossible because of ABI issues a char*, rather than a zstring.
Use not_null<zstring> for C-style strings that cannot be nullptr. ??? Do we need a name for not_null<zstring>? or is its ugliness a feature?
GSL.owner: Ownership pointers
The elements are mutable unless T is a const type. Basically a span that allocates and owns its elements.
unique_ptr<T>// unique ownership:std::unique_ptr<T>shared_ptr<T>// shared ownership:std::shared_ptr<T>(a counted pointer)stack_array<T>// A stack-allocated array. The number of elements is determined at construction and fixed thereafter. The elements are mutable unlessTis aconsttype.dyn_array<T>// A container, non-growing dynamically allocated array. The number of elements is determined at construction and fixed thereafter.
GSL.assert: Assertions
// Expects(p) terminates the program unless p == true // Expects is under control of some options (enforcement, error message, alternatives to terminate)
Expects// precondition assertion. Currently placed in function bodies. Later, should be moved to declarations.Ensures// postcondition assertion. Currently placed in function bodies. Later, should be moved to declarations.
These assertions are currently macros (yuck!) and must appear in function definitions (only) pending standard committee decisions on contracts and assertion syntax. See the contract proposal; using the attribute syntax, for example, Expects(p) will become [[expects: p]].
GSL.util: Utilities
finally//finally(f)makes afinal_action{f}with a destructor that invokesfnarrow_cast//narrow_cast<T>(x)isstatic_cast<T>(x)narrow//narrow<T>(x)isstatic_cast<T>(x)ifstatic_cast<T>(x) == xwith no signedness promotions, or it throwsnarrowing_error(e.g.,narrow<unsigned>(-42)throws)[[implicit]]// "Marker" to put on single-argument constructors to explicitly make them non-explicit.move_owner//p = move_owner(q)meansp = qbut ???joining_thread// a RAII style version ofstd::threadthat joins.index// a type to use for all container and array indexing (currently an alias forptrdiff_t)
GSL.concept: Concepts
These concepts (type predicates) are borrowed from Andrew Sutton's Origin library, the Range proposal, and the ISO WG21 Palo Alto TR. Many of them are very similar to what became part of the ISO C++ standard in C++20.
StringNumberBooleanRange// in C++20,std::ranges::rangeSortable// in C++20,std::sortableEqualityComparable// in C++20,std::equality_comparableConvertible// in C++20,std::convertible_toCommon// in C++20,std::common_withIntegral// in C++20,std::integralSignedIntegral// in C++20,std::signed_integralSemiRegular// in C++20,std::semiregularRegular// in C++20,std::regularTotallyOrdered// in C++20,std::totally_orderedFunction// in C++20,std::invocableRegularFunction// in C++20,std::regular_invocablePredicate// in C++20,std::predicateRelation// in C++20,std::relation- ...
GSL.ptr: Smart pointer concepts
Pointer// A type with*,->,==, and default construction (default construction is assumed to set the singular "null" value)Unique_pointer// A type that matchesPointer, is movable, and is not copyableShared_pointer// A type that matchesPointer, and is copyable
NL: Naming and layout suggestions
Consistent naming and layout are helpful. If for no other reason because it minimizes "my style is better than your style" arguments. However, there are many, many, different styles around and people are passionate about them (pro and con). Also, most real-world projects include code from many sources, so standardizing on a single style for all code is often impossible. After many requests for guidance from users, we present a set of rules that you might use if you have no better ideas, but the real aim is consistency, rather than any particular rule set. IDEs and tools can help (as well as hinder).
Naming and layout rules:
- NL.1: Don't say in comments what can be clearly stated in code
- NL.2: State intent in comments
- NL.3: Keep comments crisp
- NL.4: Maintain a consistent indentation style
- NL.5: Avoid encoding type information in names
- NL.7: Make the length of a name roughly proportional to the length of its scope
- NL.8: Use a consistent naming style
- NL.9: Use
ALL_CAPSfor macro names only - NL.10: Prefer
underscore_stylenames - NL.11: Make literals readable
- NL.15: Use spaces sparingly
- NL.16: Use a conventional class member declaration order
- NL.17: Use K&R-derived layout
- NL.18: Use C++-style declarator layout
- NL.19: Avoid names that are easily misread
- NL.20: Don't place two statements on the same line
- NL.21: Declare one name (only) per declaration
- NL.25: Don't use
voidas an argument type - NL.26: Use conventional
constnotation - NL.27: Use a
.cppsuffix for code files and.hfor interface files
Most of these rules are aesthetic and programmers hold strong opinions. IDEs also tend to have defaults and a range of alternatives. These rules are suggested defaults to follow unless you have reasons not to.
We have had comments to the effect that naming and layout are so personal and/or arbitrary that we should not try to "legislate" them. We are not "legislating" (see the previous paragraph). However, we have had many requests for a set of naming and layout conventions to use when there are no external constraints.
More specific and detailed rules are easier to enforce.
These rules bear a strong resemblance to the recommendations in the PPP Style Guide written in support of Stroustrup's Programming: Principles and Practice using C++.