mirror of
https://github.com/cpp-best-practices/cppbestpractices.git
synced 2025-12-17 11:14:35 +03:00
Initial commit
This commit is contained in:
7
00-Preface.md
Normal file
7
00-Preface.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Preface
|
||||
|
||||
This document is meant to be a collaborative discussion of the best practices in C++. It compliments books such as Effective C++ (Meyers) and C++ Coding Standards (Alexandrescu, Sutter). We fill in some of the lower level details that they don't discuss and provide specific stylistic recommendations while also discussing how to ensure overall code quality.
|
||||
|
||||
In all cases brevity and succinctness is prefered. Examples are prefered for making the case for why one option is prefered over another. If necessary, words will be used.
|
||||
|
||||
|
||||
126
01-Use_the_Tools_Available.md
Normal file
126
01-Use_the_Tools_Available.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Use The Tools Available
|
||||
|
||||
## Source Control
|
||||
|
||||
Source control is an absolute necessity for any software development project. If you are not using one yet, start using one.
|
||||
|
||||
* [github](http://github.com) - allows for unlimited public repositories, must pay for a private repository
|
||||
* [bitbucket](http://bitbucket.org) - allows for unlimited private repositories with up to 5 collaborators, for free.
|
||||
* [sourceforge](http://sf.net) - open source hosting only
|
||||
|
||||
## Build Tool
|
||||
|
||||
Use an industry standard widely accepted build tool. This prevents you from reinventing the wheel whenever you discover / link to a new library / package your product / etc. Examples include:
|
||||
|
||||
* [cmake](http://cmake.org)
|
||||
* [waf](http://waf.googlecode.com)
|
||||
* ninja - can greatly improve the incremental build time of your larger projects. Can be used as a target for cmake
|
||||
* google's build tool
|
||||
|
||||
Remember, it's not just a build tool, it's also a programming language. Try to maintain good clean build scripts and follow the recommended practices for the tool you are using
|
||||
|
||||
## Continuous Integration
|
||||
|
||||
Once you have picked your build tool, set up a continuous integration environment.
|
||||
|
||||
Continuous Integration (CI) tools automatically build the source code as changes are pushed to the repository. These can be hosted privately or with a CI host.
|
||||
|
||||
* [Travis CI](http://travis-ci.org)
|
||||
* works well with C++
|
||||
* designed for use with github
|
||||
* free for public repositories on github
|
||||
* Hudson CI
|
||||
* Decent CI
|
||||
|
||||
If you have an OpenSource, publicly hosted project on github, go enable travis-ci integration right now. We'll wait for you to come back. For a simple example of how to enable it for your C++ CMake based application, see here: https://github.com/ChaiScript/ChaiScript/blob/master/.travis.yml
|
||||
|
||||
|
||||
## Compilers
|
||||
|
||||
Use every available and reasonable set of warning options
|
||||
|
||||
You should use as many compilers as you can for your platform(s). Each compiler implements the standard slightly differently and supporting multiple will help ensure the most portable, most reliable code.
|
||||
|
||||
### GCC / Clang
|
||||
|
||||
`-Wall -Wextra -Wshadow -Wnon-virtual-dtor -pedantic`
|
||||
|
||||
* `-Wall -Wextra` reasonable and standard
|
||||
* `-Wshadow` warn the user if a variable declartion shadows one from a parent context
|
||||
* `-Wnon-virtual-dtor` warn the user if a class with virtual functions has a non-virtual destructor. This helps catch hard to track down memory errors
|
||||
* `-pedantic`
|
||||
|
||||
`-Weffc++` warning mode can be too noisy, but if it works for your project, use it also.
|
||||
|
||||
### MSVC
|
||||
|
||||
`/W4 /W44640`
|
||||
|
||||
* `/W4` - All reasonable warnings
|
||||
* `/w44640` - Enable warning on thread un-safe static member initialization
|
||||
|
||||
Not recommended
|
||||
|
||||
* `/Wall` - Also warns on files included from the standard library, so it's not very useful and creates too many extra warnings.
|
||||
|
||||
|
||||
|
||||
### General
|
||||
|
||||
Start with very strict warnings settings from the beginning. Trying to raise the warning level after the project is underway can be painful.
|
||||
|
||||
Consider using the "treat warnings as errors" setting. `/Wx` with MSVC, `-Werror` with GCC / Clang
|
||||
|
||||
## llvm based tools
|
||||
|
||||
|
||||
include-what-you-use https://github.com/ChaiScript/ChaiScript/commit/c0bf6ee99dac14a19530179874f6c95255fde173
|
||||
|
||||
clang-modernize https://github.com/ChaiScript/ChaiScript/commit/6eab8ddfe154a4ebbe956a5165b390ee700fae1b
|
||||
|
||||
clang-check
|
||||
clang-tidy
|
||||
|
||||
## Static Analyzers
|
||||
|
||||
### cppcheck
|
||||
Cppcheck is free and opensource. It strives for 0 false positives and does a good job at it. Therefor all warning should be enabled: -enable=all
|
||||
|
||||
### Clang's Static Analyzer
|
||||
|
||||
Clang's analyzer's default options are good for the respective platform. It can be used directly [from cmake](http://garykramlich.blogspot.com/2011/10/using-scan-build-from-clang-with-cmake.html). They can also be called via clang-check and clang-tidy from the LLVM Based Tools.
|
||||
|
||||
|
||||
### MSVC's Static Analyzer
|
||||
|
||||
Can be enabled with the `/analyze` [command line option](http://msdn.microsoft.com/en-us/library/ms173498.aspx). For now we will stick with the default options.
|
||||
|
||||
commercial options
|
||||
|
||||
|
||||
|
||||
## Runtime Checkers
|
||||
|
||||
### Code Coverage Analysis
|
||||
|
||||
A coverage analysis tool shall be run when tests are executed to make sure the entire application is being tested. Unfortunately, coverage analysis requires that compiler optimizations be disabled. This can result in significantly longer test execution times.
|
||||
|
||||
The most likely candidate for a coverage visualization is the [lcov](http://ltp.sourceforge.net/coverage/lcov.php) project. A secondary option is [coveralls](https://coveralls.io/), which is free for open source projects.
|
||||
|
||||
<link to chaiscript example of using it>
|
||||
|
||||
### GCC/Clang Sanitizers
|
||||
|
||||
* address
|
||||
* thread
|
||||
* undefined
|
||||
|
||||
|
||||
## Ignoring Warnings
|
||||
|
||||
If it is determined by team consensus that the compiler or analyzer is warning on something that is either incorrect or unavoidable, the team will disable the specific error to as localized part of the code as possible.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
There should be a test enabled for every feature or bug fix that is committed. See also "Code Coverage Analysis."
|
||||
|
||||
256
02-Style.md
Normal file
256
02-Style.md
Normal file
@@ -0,0 +1,256 @@
|
||||
# Style
|
||||
|
||||
Consistency of style is more important. Second most importance is following a style that the average C++ programmer is used to reading.
|
||||
C++ allows for arbitrary length identifier names, so there's no reason to be terse when naming variables. Use descriptive names, and be consistent in the style
|
||||
|
||||
* `CamelCase`
|
||||
* `snake_case`
|
||||
|
||||
are common examples. snake_case has the advantage that it can also work with spell checkers, if desired.
|
||||
|
||||
## Common C++ Naming Conventions
|
||||
|
||||
* Types start with capitals: `MyClass`
|
||||
* functions and variables start with lower case: `myMethod`
|
||||
* constants are all capital: `const int PI=3.14159265358979323;`
|
||||
|
||||
*Note that the C++ standard does not follow any of these guidelines. Everything in the standard is lowercase only.*
|
||||
|
||||
## Distinguish Private Object Data
|
||||
|
||||
Name private data with a `m_` prefix to distinguish it from public data.
|
||||
|
||||
## Distinguish Function Parameters
|
||||
|
||||
Name function parameters with an `t_` prefix.
|
||||
|
||||
## Never begin a preprocessor definition or identifer with _
|
||||
|
||||
http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier
|
||||
|
||||
## Don't name anything starting with an `_`
|
||||
|
||||
If you do, you risk broaching on names reserved for implementation use
|
||||
|
||||
http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier
|
||||
|
||||
|
||||
## Well formed example
|
||||
|
||||
```cpp
|
||||
class MyClass
|
||||
{
|
||||
public:
|
||||
MyClass(int t_data)
|
||||
: m_data(t_data)
|
||||
{
|
||||
}
|
||||
|
||||
int getData() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_data;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Enable out of source directory builds
|
||||
|
||||
### Make sure generated files go into build folder
|
||||
|
||||
Not the source folder
|
||||
|
||||
|
||||
## Use `nullptr`
|
||||
|
||||
C++11 introduces `nullptr` which is a special type denoting a null pointer value. This should be used instead of 0 or NULL to indicate a null pointer.
|
||||
|
||||
## Comments
|
||||
|
||||
Comment blocks should use `//`, not `/* */`. Using `//` makes it much easier to comment out a block of code while debugging.
|
||||
|
||||
```cpp
|
||||
// this function does something
|
||||
int myFunc()
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
To comment out this function block during debugging we might do:
|
||||
|
||||
```cpp
|
||||
/*
|
||||
// this function does something
|
||||
int myFunc()
|
||||
{
|
||||
}
|
||||
*/
|
||||
```
|
||||
|
||||
which would be impossible if the function comment header used `/* */`
|
||||
|
||||
## Never Use `using` In a Header File
|
||||
|
||||
This causes the name space you are `using` to be pulled into the namespace of the header file.
|
||||
|
||||
|
||||
## Include Guards
|
||||
|
||||
Header files must contain an distinctly named include guard to avoid problems with including the same header multiple times or conflicting with other headers from other projects
|
||||
|
||||
```cpp
|
||||
#ifndef MYPROJECT_MYCLASS_HPP
|
||||
#define MYPROEJCT_MYCLASS_HPP
|
||||
|
||||
namespace MyProject {
|
||||
class MyClass {
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
|
||||
## {} are required for blocks.
|
||||
Leaving them off can lead to semantic errors in the code.
|
||||
|
||||
```cpp
|
||||
// Bad Idea
|
||||
// this compiles and does what you want, but can lead to confusing
|
||||
// errors if close attention is not paid.
|
||||
for (int i = 0; i < 15; ++i)
|
||||
std::cout << i << std::endl;
|
||||
|
||||
// Bad Idea
|
||||
// the cout is not part of the loop in this case even though it appears to be
|
||||
int sum = 0;
|
||||
for (int i = 0; i < 15; ++i)
|
||||
++sum;
|
||||
std::cout << i << std::endl;
|
||||
|
||||
|
||||
// Good Idea
|
||||
// It's clear which statements are part of the loop (or if block, or whatever)
|
||||
int sum = 0;
|
||||
for (int i = 0; i < 15; ++i) {
|
||||
++sum;
|
||||
std::cout << i << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
## Keep lines a reasonable length
|
||||
|
||||
```cpp
|
||||
// Bad Idea
|
||||
// hard to follow
|
||||
if (x && y && myFunctionThatReturnsBool() && caseNumber3 && (15 > 12 || 2 < 3)) {
|
||||
}
|
||||
|
||||
// Good Idea
|
||||
// Logical grouping, easier to read
|
||||
if (x && y && myFunctionThatReturnsBool()
|
||||
&& caseNumber3
|
||||
&& (15 > 12 || 2 < 3)) {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Use "" For Including Local Files
|
||||
... `<>` is [reserved for system includes](http://blog2.emptycrate.com/content/when-use-include-verses-include).
|
||||
|
||||
```cpp
|
||||
// Bad Idea. Requires extra -I directives to the compiler
|
||||
// and goes against standards
|
||||
#include <string>
|
||||
#include <includes/MyHeader.hpp>
|
||||
|
||||
// Worse Idea
|
||||
// requires potentially even more specific -I directives and
|
||||
// makes code more difficult to package and distribute
|
||||
#include <string>
|
||||
#include <MyHeader.hpp>
|
||||
|
||||
|
||||
// Good Idea
|
||||
// requires no extra params and notifies the user that the file
|
||||
// is a local file
|
||||
#include <string>
|
||||
#include "MyHeader.hpp"
|
||||
```
|
||||
|
||||
## Initialize Member Variables
|
||||
...with the member initializer list
|
||||
|
||||
```cpp
|
||||
// Bad Idea
|
||||
class MyClass
|
||||
{
|
||||
public:
|
||||
MyClass(int t_value)
|
||||
{
|
||||
m_value = t_value;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
};
|
||||
|
||||
|
||||
// Good Idea
|
||||
// C++'s memeber initializer list is unique to the language and leads to
|
||||
// cleaner code and potential performance gains that other languages cannot
|
||||
// match
|
||||
class MyClass
|
||||
{
|
||||
public:
|
||||
MyClass(int t_value)
|
||||
: m_value(t_value)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## Always Use Namespaces
|
||||
|
||||
There is almost never a reason to declare an identifier in the global namespaces. Instead, functions and classes should exist in an appropriately named namespaces or in a class inside of a namespace. Identifiers which are placed in the global namespace risk conflicting with identifiers from other (mostly C, which doesn't have namespaces) libraries.
|
||||
|
||||
## Avoid Compiler Macros
|
||||
|
||||
Compiler definitions and macros are replaced by the pre-processor before the compiler is ever run. This can make debugging very difficult because the debugger doesn't know where the source came from.
|
||||
|
||||
```cpp
|
||||
// Good Idea
|
||||
namespace my_project {
|
||||
class Constants {
|
||||
public:
|
||||
static const double PI = 3.14159;
|
||||
}
|
||||
}
|
||||
|
||||
// Bad Idea
|
||||
#define PI 3.14159;
|
||||
```
|
||||
|
||||
|
||||
## Use the correct integer type for stdlib features
|
||||
|
||||
|
||||
## Use .hpp and .cpp for your file extensions
|
||||
|
||||
Ultimately this is a matter of preference, but .hpp and .cpp are widely recognized by various editors and tools. So the choice is pragmatic. Specifically, VisualStudio only automatically recognizes .cpp and .cxx for C++ files, plus vim doesn't necessarily recognize .cc as a C++ file.
|
||||
|
||||
One particularly large project (OpenStudio) uses .hpp and .cpp for user generated files and .hxx and .cxx for tool generated files. Both are well recongized and having the disctinction is helpful
|
||||
|
||||
## never ever mix tabs and spaces
|
||||
|
||||
## never put code with side effects inside an assert()
|
||||
|
||||
## Don't be afraid of templates
|
||||
69
03-Considering_Safety.md
Normal file
69
03-Considering_Safety.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Considering Safety
|
||||
|
||||
|
||||
## Const as much as possible
|
||||
`const` tells the compiler that a variable or method is immutable. This helps the compiler optimize the code and helps the developer know if a function side effects. Also, using `const &` prevents the compiler from copying data unnecessarily. [Here](http://kotaku.com/454293019) are some comments on const from John Carmack.
|
||||
|
||||
```cpp
|
||||
// Bad Idea
|
||||
class MyClass
|
||||
{
|
||||
public:
|
||||
MyClass(std::string t_value)
|
||||
: m_value(t_value)
|
||||
{
|
||||
}
|
||||
|
||||
std::string get_value()
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_value;
|
||||
}
|
||||
|
||||
|
||||
// Good Idea
|
||||
class MyClass
|
||||
{
|
||||
public:
|
||||
MyClass(const std::string &t_value)
|
||||
: m_value(t_value)
|
||||
{
|
||||
}
|
||||
|
||||
std::string get_value() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_value;
|
||||
}
|
||||
```
|
||||
|
||||
## Avoid raw memory access
|
||||
|
||||
Raw memory access, allocation and deallocation, are difficult to get correct in C++ without [risking memory errors and leaks](http://blog2.emptycrate.com/content/nobody-understands-c-part-6-are-you-still-using-pointers). C++11 provides tools to avoid these problems.
|
||||
|
||||
```cpp
|
||||
// Bad Idea
|
||||
MyClass *myobj = new MyClass;
|
||||
|
||||
// ...
|
||||
delete myobj;
|
||||
|
||||
|
||||
// Good Idea
|
||||
std::shared_ptr<MyClass> myobj = make_shared<MyClass>();
|
||||
// ...
|
||||
// myobj is automatically freed for you whenever it is no longer used.
|
||||
```
|
||||
|
||||
## Use Exceptions
|
||||
|
||||
Exceptions cannot be ignored. Return values, such as using `boost::optional`, can be ignored and if not checked can cause crashes or memory errors. An exception, on the other hand, can be caught and handled. Potentially all the way up the highest level of the application with a log and automatic restart of the application.
|
||||
|
||||
Stroustrup, the original designer of C++, [makes this point](http://www.stroustrup.com/bs_faq2.html#exceptions-why) much better than I ever could.
|
||||
|
||||
7
04-Considering_Portability.md
Normal file
7
04-Considering_Portability.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Considering Portability
|
||||
|
||||
## Know your types
|
||||
|
||||
Most portability issues that generate warnings are because we are not careful about our types. standard library and arrays are indexed with size_t. Standard containers sizes are reported in size_t. If you get the handling of size_t wrong, you can create subtle lurking 64bit issues that arise only after you start to overflow the indexing of 32bit integers. char vs unsigned char.
|
||||
|
||||
http://www.viva64.com/en/a/0010/
|
||||
11
05-Considering_Threadability.md
Normal file
11
05-Considering_Threadability.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Considering Threadability
|
||||
|
||||
## Avoid Global Data
|
||||
|
||||
This includes statics and singletons
|
||||
|
||||
Global data leads to unintended side effects between functions and can make code difficult or impossible to parallelize. Even if the code is not intended today for parallelization, there is no reason to make it impossible for the future.
|
||||
|
||||
## Avoid Heap Operations
|
||||
|
||||
Much slower in threaded environments. In many or maybe even most cases, copying data is faster. Plus with move operations and such and things
|
||||
101
06-Considering_Performance.md
Normal file
101
06-Considering_Performance.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# Considering Performance
|
||||
|
||||
## Build Time
|
||||
|
||||
## see also
|
||||
include-what-you-use
|
||||
|
||||
|
||||
### Forward Declare when Possible
|
||||
|
||||
This:
|
||||
|
||||
```cpp
|
||||
// some header file
|
||||
class MyClass;
|
||||
|
||||
void doSomething(const MyClass &);
|
||||
```
|
||||
|
||||
instead of:
|
||||
|
||||
```cpp
|
||||
// some header file
|
||||
|
||||
|
||||
|
||||
#include "MyClass.hpp"
|
||||
|
||||
void doSomething(const MyClass &);
|
||||
```
|
||||
|
||||
|
||||
This applies to templates as well:
|
||||
|
||||
```cpp
|
||||
template<typename T> class MyTemplatedType;
|
||||
```
|
||||
|
||||
This is a proactive approach to simplify compilation time and rebuilding dependencies.
|
||||
|
||||
### Firewall Frequently Changing Header Files
|
||||
|
||||
### Don't Unnecessarily Include Headers
|
||||
|
||||
### Reduce the load on the preprocessor
|
||||
|
||||
This is a general form of "Firewall Frequently Changing Header Files" and "Don't Unnecessarily Include Headers." Tools like BOOST_PP can be very helpful, but they also put a huge burden on the preprocessor
|
||||
|
||||
### Consider using precompiled headers
|
||||
|
||||
### Consider Using Tools
|
||||
|
||||
These are not meant to supercede good design
|
||||
|
||||
CCACHE, facebook's thing (warp)
|
||||
|
||||
|
||||
## Runtime
|
||||
|
||||
### Limit Variable Scope
|
||||
|
||||
Variables should be declared as late as possible, and ideally, only when it's possible to initialize the object. Reduced variable scope results in less memory being used, more efficient code in general, and helps the compiler optimize the code further.
|
||||
|
||||
```c++
|
||||
// Good idea
|
||||
for (int i = 0; i < 15; ++i)
|
||||
{
|
||||
MyObject obj(i);
|
||||
// do something with obj
|
||||
}
|
||||
|
||||
// Bad Idea
|
||||
MyObject obj; // meaningless object initialization
|
||||
for (int i = 0; i < 15; ++i)
|
||||
{
|
||||
obj = MyObject(i); // unnecessary assignment operation
|
||||
// do something with obj
|
||||
}
|
||||
// obj is still taking up memory for no reason
|
||||
```
|
||||
|
||||
### Prefer double to float
|
||||
|
||||
### Prefer ++i to i++
|
||||
... when it is semantically correct. Pre-increment is [faster](http://blog2.emptycrate.com/content/why-i-faster-i-c) then post-increment because it does not require a copy of the object to be made.
|
||||
|
||||
```cpp
|
||||
// Bad Idea
|
||||
for (int i = 0; i < 15; i++)
|
||||
{
|
||||
std::cout << i << std::endl;
|
||||
}
|
||||
|
||||
|
||||
// Good Idea
|
||||
for (int i = 0; i < 15; ++i)
|
||||
{
|
||||
std::cout << i << std::endl;
|
||||
}
|
||||
|
||||
```
|
||||
10
07-Consider_Enabling_Scripting.md
Normal file
10
07-Consider_Enabling_Scripting.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Consider Enabling Scripting
|
||||
|
||||
The combination of scripting and compiled languages is very powerful. It gives us the things we've come to love about compiled languages: type safety, performance, thread safety options, consistent memory model while also giving us the flexibility to try something new quickly without a full rebuild.
|
||||
|
||||
The VM based compiled languages have learned this already: jruby, jython, ironruby, ironpython
|
||||
|
||||
* chaiscript
|
||||
* angelscript
|
||||
* luabind
|
||||
* SWIG
|
||||
8
08-Further_Reading.md
Normal file
8
08-Further_Reading.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Further Reading
|
||||
|
||||
* http://geosoft.no/development/cppstyle.html
|
||||
* http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml (Note that google's standard document makes several recommendations which we will NOT be following. For example, they explicitly forbid the use of exceptions, which makes [RAII](http://blog2.emptycrate.com/content/nobody-understands-c-part-2-raii) impossible.)
|
||||
* http://www.parashift.com/c++-faq/
|
||||
* http://www.cplusplus.com/
|
||||
* http://sourceforge.net/apps/mediawiki/cppcheck/index.php?title=ListOfChecks
|
||||
* http://emptycrate.com/
|
||||
Reference in New Issue
Block a user