From 445123e4afd17251ed6aee053402ecadeb63c544 Mon Sep 17 00:00:00 2001
From: Daniel Cheng Try to avoid forward declarations of entities
defined in another project. Define functions inline only when they are small, say, 10
-lines or fewer. Include the definition of a function at its point of declaration in a header file only
+when the definition is short. If the definition otherwise has a reason be in the header,
+put it in an internal part of the file. If necessary to make the definition ODR-safe, mark
+it with an You can declare functions in a way that allows the compiler to expand
-them inline rather than calling them through the usual
-function call mechanism. Functions defined in header files are sometimes referred to as "inline functions",
+which is a somewhat overloaded term that refers to several distinct but overlapping
+situations: While functions tend to be a more common source of confusion, these definitions apply to
+variables as well, and so do the rules here. Inlining a function can generate more efficient object
-code, as long as the inlined function is small. Feel free
-to inline accessors and mutators, and other short,
-performance-critical functions. Overuse of inlining can actually make programs slower.
-Depending on a function's size, inlining it can cause the
-code size to increase or decrease. Inlining a very small
-accessor function will usually decrease code size while
-inlining a very large function can dramatically increase
-code size. On modern processors smaller code usually runs
-faster due to better use of the instruction cache. A decent rule of thumb is to not inline a function if
-it is more than 10 lines long. Beware of destructors,
-which are often longer than they appear because of
-implicit member- and base-destructor calls! Only define a function at its public declaration if it is short, say, 10 lines or fewer. Put
+longer function bodies in the Another useful rule of thumb: it's typically not cost
-effective to inline functions with loops or switch
-statements (unless, in the common case, the loop or
-switch statement is never executed). Even if a definition must be in the header, this is not a sufficient reason to put it within
+the public part. Instead, the definition can be in an internal part of the header, such as the
+private section of a class, within a namespace that includes the word
+ It is important to know that functions are not always
-inlined even if they are declared as such; for example,
-virtual and recursive functions are not normally inlined.
-Usually recursive functions should not be inline. The
-main reason for making a virtual function inline is to
-place its definition in the class, either for convenience
-or to document its behavior, e.g., for accessors and
-mutators. Once a definition is in a header file, it must be ODR-safe by having the Note that there is still a risk of collision between libraries within a
+ nested Single-line nested namespace declarations
@@ -993,7 +1044,7 @@ does not make an observable difference. For example:Inline Functions
+
+Defining Functions in Header Files
-inline specifier.
+
+
+
+
-constexpr functions generally need to
+be defined in the header file that declares them (but not necessarily the public part).
+
-.cc file unless they must be in the header for
+performance or technical reasons.internal, or below a comment like // Implementation details only
+below here.inline
+specifier or being implicitly specified inline by being a function template or defined in a class
+body when first declared.template <typename T>
+class Foo {
+ public:
+ int bar() { return bar_; }
+
+ void MethodWithHugeBody();
+
+ private:
+ int bar_;
+};
+
+// Implementation details only below here
+
+template <typename T>
+void Foo<T>::MethodWithHugeBody() {
+ ...
+}
+
Names and Order of Includes
@@ -696,6 +740,13 @@ inline void my_inline_function() {
// We shouldn't use this internal name in non-absl code.
using ::absl::container_internal::ImplementationDetail;
+
+ internal namespace, so give each library within a
+ namespace a unique internal namespace by adding the library's
+ filename. E.g. gshoe/widget.h would use
+ gshoe::internal_widget as opposed to just
+ gshoe::internal.string_view, character array, or character pointer, pointing
to a string literal. String literals have static storage duration already
and are usually sufficient.
- See TotW #140.Init() method as described in
-TotW #42.
+TotW #42
+.
Avoid Init() methods on objects with
no other states that affect which public methods may be called
(semi-constructed objects of this form are particularly hard to work
@@ -1794,9 +1846,10 @@ Functions for more details.
The output of a C++ function is naturally provided via a return value and sometimes via output parameters (or in/out parameters).
-Prefer using return values over output parameters: they -improve readability, and often provide the same or better -performance.
+Prefer using return values over output parameters: +they improve readability, and often provide the same or better performance. +See +TotW #176.
Prefer to return by value or, failing that, return by reference. Avoid returning a raw pointer unless it can be null.
@@ -1818,8 +1871,7 @@ In some cases reference parameters can bind to temporaries, leading to lifetime bugs. Instead, find a way to eliminate the lifetime requirement (for example, by copying the parameter), or pass retained parameters by pointer and document the lifetime and non-null requirements. - - +See TotW 116 for more.When ordering function parameters, put all input-only parameters before any output parameters. In particular, @@ -1883,10 +1935,9 @@ instead.
identically-named function to take different arguments. It may be necessary for templatized code, and it can be convenient for Visitors. -Overloading based on const or ref qualification may make utility
- code more usable, more efficient, or both.
- (See TotW 148 for more.)
-
Overloading based on const or ref qualification
+may make utility code more usable, more efficient, or both.
+See TotW #148 for more.
If a function is overloaded by the argument types alone, @@ -2171,7 +2222,7 @@ tool.
how to runcpplint.py from their project
tools. If the project you are contributing to does not,
you can download
-
+
cpplint.py separately.
@@ -2620,6 +2671,10 @@ casts when explicit type conversion is necessary.
will not compile if conversion can result in information loss. The
syntax is also concise.
+ std::string(some_cord) to
+ static_cast<std::string>(some_cord).absl::implicit_cast
to safely cast up a type hierarchy,
e.g., casting a Foo* to a
@@ -2845,13 +2900,9 @@ many other contexts as well. In particular:
const has
no effect on the caller, thus is not recommended in function
- declarations. See
+ declarations. See TotW #109.const unless they
+ const unless they
alter the logical state of the object (or enable the user to modify
that state, e.g., by returning a non-const reference, but that's
rare), or they can't safely be invoked concurrently.constexpr. Do not use
is
int. If a program needs an integer type of a
different size, use an exact-width integer type from
-<cstdint>, such as
+<stdint.h>, such as
int16_t. If you have a
value that could ever be greater than or equal to 2^31,
use a 64-bit type such as int64_t.
@@ -2960,7 +3011,7 @@ compiler and architecture.
-The standard library header <cstdint> defines types
+The standard library header <stdint.h> defines types
like int16_t, uint32_t,
int64_t, etc. You should always use
those in preference to short, unsigned
@@ -3883,7 +3934,7 @@ or tests.
C++20 introduces "modules", a new language feature designed as an
alternative to textual inclusion of header files. It introduces three
new keywords to support
-this: module, export,
+this: module, export,
and import.
Modules are a big shift in how C++ is written and compiled, and we @@ -4245,29 +4296,59 @@ declaration of that entity. The pattern-matching engine in our brains relies a great deal on these naming rules.
-Naming rules are pretty arbitrary, but +
Style rules about naming are pretty arbitrary, but we feel that consistency is more important than individual preferences in this area, so regardless of whether you find them sensible or not, the rules are the rules.
-For the purposes of the naming rules below, a "word" is anything that you +would write in English without internal spaces. Either words are all lowercase, +with underscores between words +("snake_case"), or words +are mixed case with the first letter of each word capitalized +("camelCase" or +"PascalCase"). -
Optimize for readability using names that would be clear -even to people on a different team.
+Use names that describe the purpose or intent of the object.
-Do not worry about saving horizontal space as it is far
-more important to make your code immediately
-understandable by a new reader. Minimize the use of
-abbreviations that would likely be unknown to someone outside
-your project (especially acronyms and initialisms). Do not
-abbreviate by deleting letters within a word. As a rule of thumb,
-an abbreviation is probably OK if it's listed in
- Wikipedia. Generally speaking, descriptiveness should be
-proportional to the name's scope of visibility. For example,
-n may be a fine name within a 5-line function,
-but within the scope of a class, it's likely too vague.
Give things names that make their purpose or intent understandable to a new +reader, even someone on a different team than the owners. Do not worry about +saving horizontal space as it is far more important to make your code +immediately understandable by a new reader.
+ +Consider the context in which the name will be used. A name should be +descriptive even if it is used far from the code that makes it available for +use. However, a name should not distract the reader by repeating information +that's present in the immediate context. Generally, this means that +descriptiveness should be proportional to the name's scope of visibility. A free +function declared in a header should probably mention the header's library, +while a local variable probably shouldn't explain what function it's within.
+ +Minimize the use of abbreviations that would likely be unknown to someone
+outside your project (especially acronyms and initialisms). Do not abbreviate by
+deleting letters within a word. When an abbreviation is used, prefer to
+capitalize it as a single "word", e.g., StartRpc() rather than
+StartRPC(). As a rule of thumb, an abbreviation is probably OK
+if it's listed in
+
+Wikipedia. Note that certain universally-known abbreviations are OK, such as
+i for a loop index and T for a template
+parameter.
The names you see most frequently are not like most names; a small number of
+"vocabulary" names are reused so widely that they are always in context. These
+names tend to be short or even abbreviated and their full meaning comes from
+explicit long-form documentation rather than from just comments on their
+definition and the words within the names. E.g. absl::Status has a
+dedicated
+
+page
+in a devguide,
+documenting its proper use. You probably won't define new vocabulary names very
+often, but if you do, get
+additional design review to make sure the chosen names work well when
+used widely.
class MyClass {
public:
@@ -4279,8 +4360,12 @@ but within the scope of a class, it's likely too vague.
}
return n;
}
- void DoSomethingImportant() {
+ // Function comment doesn't need to explain that this returns non-OK on
+ // failure as that is implied by the `absl::Status` return type, but it
+ // might document behavior for some specific codes.
+ absl::Status DoSomethingImportant() {
std::string fqdn = ...; // Well-known abbreviation for Fully Qualified Domain Name
+ return absl::OkStatus();
}
private:
const int kMaxAllowedConnections = ...; // Clear meaning within context
@@ -4297,7 +4382,8 @@ but within the scope of a class, it's likely too vague.
}
return total_number_of_foo_errors;
}
- void DoSomethingImportant() {
+ // A return type with a generic name is unclear without widespread education.
+ Result DoSomethingImportant() {
int cstmr_id = ...; // Deletes internal letters
}
private:
@@ -4305,27 +4391,7 @@ but within the scope of a class, it's likely too vague.
};
-Note that certain universally-known abbreviations are OK, such as
-i for an iteration variable and T for a
-template parameter.
For the purposes of the naming rules below, a "word" is anything that you
-would write in English without internal spaces. This includes abbreviations,
-such as acronyms and initialisms. For names written in mixed case (also
-sometimes referred to as
-"camel case" or
-"Pascal case"), in
-which the first letter of each word is capitalized, prefer to capitalize
-abbreviations as single words, e.g., StartRpc() rather than
-StartRPC().
Template parameters should follow the naming style for their -category: type template parameters should follow the rules for -type names, and non-type template -parameters should follow the rules for -variable names. - -
Filenames should be all lowercase and can include
underscores (_) or dashes (-).
@@ -4410,13 +4476,19 @@ Concept names follow the same rules as type names.
Data members of classes, both static and non-static, are named like ordinary nonmember variables, but with a -trailing underscore.
+trailing underscore. The exception to this is static constant +class members, which should follow the rules for +naming constants.class TableInfo {
+ public:
...
+ static const int kTableVersion = 3; // OK - constant naming.
+ ...
+
private:
- std::string table_name_; // OK - underscore at end.
- static Pool<TableInfo>* pool_; // OK.
+ std::string table_name_; // OK - underscore at end.
+ static Pool<TableInfo>* pool_; // OK.
};
@@ -4451,10 +4523,10 @@ const int kAndroid8_0_0 = 24; // Android 8.0.0
All such variables with static storage duration (i.e., statics and globals, see -Storage Duration for details) should be named this way, including those in templates where -different instantiations of the template may have different values. This convention is optional for -variables of other storage classes, e.g., automatic variables; otherwise the usual variable naming -rules apply. For example:
+Storage Duration for details) should be named this way, including those that are static constant +class data members and those in templates where different instantiations of the template +may have different values. This convention is optional for variables of other storage classes, +e.g., automatic variables; otherwise the usual variable naming rules apply. For example:void ComputeFoo(absl::string_view suffix) {
// Either of these is acceptable.
@@ -4496,41 +4568,22 @@ set_count(int count).
Namespace Names
-Namespace names are all lower-case, with words separated by underscores.
-Top-level namespace names are based on the project name
-. Avoid collisions
-between nested namespaces and well-known top-level namespaces.
+Namespace names are snake_case (all lowercase, with underscores
+between words).
-The name of a top-level namespace should usually be the
-name of the project or team whose code is contained in that
-namespace. The code in that namespace should usually be in
-a directory whose basename matches the namespace name (or in
-subdirectories thereof).
+When choosing names for namespaces, note
+that names must be fully qualified when used in a header outside the namespace,
+because unqualified Aliases are generally banned.
+Top-level namespaces must be globally unique and recognizable, so each one
+should be owned by a single project or team, with a name based on the name of
+that project or team. Usually, all code in the namespace should be under one or
+more directories with the same name as the namespace.
-
-Keep in mind that the rule
-against abbreviated names applies to namespaces just as much
-as variable names. Code inside the namespace seldom needs to
-mention the namespace name, so there's usually no particular need
-for abbreviation anyway.
-
-Avoid nested namespaces that match well-known top-level
-namespaces. Collisions between namespace names can lead to surprising
-build breaks because of name lookup rules. In particular, do not
-create any nested std namespaces. Prefer unique project
-identifiers
-(websearch::index, websearch::index_util)
-over collision-prone names like websearch::util. Also avoid overly deep nesting
- namespaces (TotW #130).
-
-For internal namespaces, be wary of other code being
-added to the same internal namespace causing a collision
-(internal helpers within a team tend to be related and may lead to
-collisions). In such a situation, using the filename to make a unique
-internal name is helpful
-(websearch::index::frobber_internal for use
-in frobber.h).
+Nested namespaces should avoid the names of well-known top-level namespaces,
+especially std and absl, because in C++, nested
+namespaces do not protect from collisions with names in other namespaces
+(see TotW #130).
Enumerator Names
@@ -4563,7 +4616,15 @@ naming.
-Macro Names
+Template Parameter Names
+
+Template parameters should follow the naming style for their
+category: type template parameters should follow the rules for naming
+types, and non-type template
+parameters should follow the rules for naming
+variables or constants.
+
+
Macro Names
You're not really going to
define a macro, are you? If you do, they're like this:
@@ -4578,6 +4639,12 @@ named with all capitals and underscores, and with a project-specific prefix.
#define MYPROJECT_ROUND(x) ...
+Aliases
+
+The name for an alias follows the same principles as
+any other new name, applied in the context where the alias is defined rather
+than where the original name appears.
+
Exceptions to Naming Rules
If you are naming something that is analogous to an
@@ -4620,8 +4687,7 @@ one may be you!
Use either the // or /* */
syntax, as long as you are consistent.
-You can use either the // or the /*
-*/ syntax; however, // is
+
While either syntax is acceptable, // is
much more common. Be consistent with how you
comment and what style you use where.
@@ -4910,7 +4976,8 @@ the example above would be obvious:
}
-Pay attention to punctuation, spelling, and grammar; it is easier to read well-written comments than badly written